Skip to content

Commit

Permalink
feature #33122 [WebLink] implement PSR-13 directly (nicolas-grekas)
Browse files Browse the repository at this point in the history
This PR was merged into the 4.4 branch.

Discussion
----------

[WebLink] implement PSR-13 directly

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Implementing PSR-13 is simple enough and the repo we're using is [freezed](https://github.com/php-fig/link-util/pulls).

This also allows us to add some type declarations. We're going to need them before merging #30323.

Commits
-------

b570ee1 [WebLink] implement PSR-13 directly
  • Loading branch information
nicolas-grekas committed Aug 12, 2019
2 parents 1aa41ed + b570ee1 commit 3bc4e4f
Show file tree
Hide file tree
Showing 15 changed files with 454 additions and 16 deletions.
1 change: 0 additions & 1 deletion composer.json
Expand Up @@ -20,7 +20,6 @@
"ext-xml": "*",
"doctrine/event-manager": "~1.0",
"doctrine/persistence": "~1.0",
"fig/link-util": "^1.0",
"twig/twig": "^1.41|^2.10",
"psr/cache": "~1.0",
"psr/container": "^1.0",
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php
Expand Up @@ -11,9 +11,9 @@

namespace Symfony\Bridge\Twig\Extension;

use Fig\Link\GenericLinkProvider;
use Fig\Link\Link;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\WebLink\GenericLinkProvider;
use Symfony\Component\WebLink\Link;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

Expand Down
Expand Up @@ -11,11 +11,11 @@

namespace Symfony\Bridge\Twig\Tests\Extension;

use Fig\Link\Link;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Twig\Extension\WebLinkExtension;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\WebLink\Link;

/**
* @author Kévin Dunglas <dunglas@gmail.com>
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bridge/Twig/composer.json
Expand Up @@ -42,7 +42,7 @@
"symfony/console": "^3.4|^4.0|^5.0",
"symfony/var-dumper": "^3.4|^4.0|^5.0",
"symfony/expression-language": "^3.4|^4.0|^5.0",
"symfony/web-link": "^3.4|^4.0|^5.0",
"symfony/web-link": "^4.4|^5.0",
"symfony/workflow": "^4.3|^5.0"
},
"conflict": {
Expand Down
Expand Up @@ -12,9 +12,8 @@
namespace Symfony\Bundle\FrameworkBundle\Controller;

use Doctrine\Common\Persistence\ManagerRegistry;
use Fig\Link\GenericLinkProvider;
use Fig\Link\Link;
use Psr\Container\ContainerInterface;
use Psr\Link\LinkInterface;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
Expand All @@ -33,6 +32,7 @@
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\WebLink\EventListener\AddLinkHeaderListener;
use Symfony\Component\WebLink\GenericLinkProvider;

/**
* Common features needed in controllers.
Expand Down Expand Up @@ -420,7 +420,7 @@ protected function dispatchMessage($message, array $stamps = []): Envelope
*
* @final
*/
protected function addLink(Request $request, Link $link)
protected function addLink(Request $request, LinkInterface $link)
{
if (!class_exists(AddLinkHeaderListener::class)) {
throw new \LogicException('You can not use the "addLink" method if the WebLink component is not available. Try running "composer require symfony/web-link".');
Expand Down
Expand Up @@ -11,7 +11,6 @@

namespace Symfony\Bundle\FrameworkBundle\Tests\Controller;

use Fig\Link\Link;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Component\DependencyInjection\Container;
Expand All @@ -29,6 +28,7 @@
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\WebLink\Link;

abstract class ControllerTraitTest extends TestCase
{
Expand Down
3 changes: 1 addition & 2 deletions src/Symfony/Bundle/FrameworkBundle/composer.json
Expand Up @@ -31,7 +31,6 @@
},
"require-dev": {
"doctrine/cache": "~1.0",
"fig/link-util": "^1.0",
"symfony/asset": "^3.4|^4.0|^5.0",
"symfony/browser-kit": "^4.3|^5.0",
"symfony/console": "^4.3|^5.0",
Expand All @@ -58,7 +57,7 @@
"symfony/workflow": "^4.3|^5.0",
"symfony/yaml": "^3.4|^4.0|^5.0",
"symfony/property-info": "^3.4|^4.0|^5.0",
"symfony/web-link": "^3.4|^4.0|^5.0",
"symfony/web-link": "^4.4|^5.0",
"doctrine/annotations": "~1.0",
"phpdocumentor/reflection-docblock": "^3.0|^4.0",
"twig/twig": "~1.34|~2.4"
Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Component/WebLink/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========

4.4.0
-----

* implement PSR-13 directly

3.3.0
-----

Expand Down
83 changes: 83 additions & 0 deletions src/Symfony/Component/WebLink/GenericLinkProvider.php
@@ -0,0 +1,83 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\WebLink;

use Psr\Link\EvolvableLinkProviderInterface;
use Psr\Link\LinkInterface;

class GenericLinkProvider implements EvolvableLinkProviderInterface
{
/**
* @var LinkInterface[]
*/
private $links = [];

/**
* @param LinkInterface[] $links
*/
public function __construct(array $links = [])
{
$that = $this;

foreach ($links as $link) {
$that = $that->withLink($link);
}

$this->links = $that->links;
}

/**
* {@inheritdoc}
*/
public function getLinks(): array
{
return array_values($this->links);
}

/**
* {@inheritdoc}
*/
public function getLinksByRel($rel): array
{
$links = [];

foreach ($this->links as $link) {
if (\in_array($rel, $link->getRels())) {
$links[] = $link;
}
}

return $links;
}

/**
* {@inheritdoc}
*/
public function withLink(LinkInterface $link)
{
$that = clone $this;
$that->links[spl_object_id($link)] = $link;

return $that;
}

/**
* {@inheritdoc}
*/
public function withoutLink(LinkInterface $link)
{
$that = clone $this;
unset($that->links[spl_object_id($link)]);

return $that;
}
}
153 changes: 153 additions & 0 deletions src/Symfony/Component/WebLink/Link.php
@@ -0,0 +1,153 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\WebLink;

use Psr\Link\EvolvableLinkInterface;

class Link implements EvolvableLinkInterface
{
// Relations defined in https://www.w3.org/TR/html5/links.html#links and applicable on link elements
public const REL_ALTERNATE = 'alternate';
public const REL_AUTHOR = 'author';
public const REL_HELP = 'help';
public const REL_ICON = 'icon';
public const REL_LICENSE = 'license';
public const REL_SEARCH = 'search';
public const REL_STYLESHEET = 'stylesheet';
public const REL_NEXT = 'next';
public const REL_PREV = 'prev';

// Relation defined in https://www.w3.org/TR/preload/
public const REL_PRELOAD = 'preload';

// Relations defined in https://www.w3.org/TR/resource-hints/
public const REL_DNS_PREFETCH = 'dns-prefetch';
public const REL_PRECONNECT = 'preconnect';
public const REL_PREFETCH = 'prefetch';
public const REL_PRERENDER = 'prerender';

// Extra relations
public const REL_MERCURE = 'mercure';

private $href = '';

/**
* @var string[]
*/
private $rel = [];

/**
* @var string[]
*/
private $attributes = [];

public function __construct(string $rel = null, string $href = '')
{
if (null !== $rel) {
$this->rel[$rel] = $rel;
}
$this->href = $href;
}

/**
* {@inheritdoc}
*/
public function getHref(): string
{
return $this->href;
}

/**
* {@inheritdoc}
*/
public function isTemplated(): bool
{
return $this->hrefIsTemplated($this->href);
}

/**
* {@inheritdoc}
*/
public function getRels(): array
{
return array_values($this->rel);
}

/**
* {@inheritdoc}
*/
public function getAttributes(): array
{
return $this->attributes;
}

/**
* {@inheritdoc}
*/
public function withHref($href)
{
$that = clone $this;
$that->href = $href;
$that->templated = $this->hrefIsTemplated($href);

return $that;
}

/**
* {@inheritdoc}
*/
public function withRel($rel)
{
$that = clone $this;
$that->rel[$rel] = $rel;

return $that;
}

/**
* {@inheritdoc}
*/
public function withoutRel($rel)
{
$that = clone $this;
unset($that->rel[$rel]);

return $that;
}

/**
* {@inheritdoc}
*/
public function withAttribute($attribute, $value)
{
$that = clone $this;
$that->attributes[$attribute] = $value;

return $that;
}

/**
* {@inheritdoc}
*/
public function withoutAttribute($attribute)
{
$that = clone $this;
unset($that->attributes[$attribute]);

return $that;
}

private function hrefIsTemplated(string $href): bool
{
return false !== strpos($href, '{') || false !== strpos($href, '}');
}
}
Expand Up @@ -11,15 +11,15 @@

namespace Symfony\Component\WebLink\Tests\EventListener;

use Fig\Link\GenericLinkProvider;
use Fig\Link\Link;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\WebLink\EventListener\AddLinkHeaderListener;
use Symfony\Component\WebLink\GenericLinkProvider;
use Symfony\Component\WebLink\Link;

/**
* @author Kévin Dunglas <dunglas@gmail.com>
Expand Down

0 comments on commit 3bc4e4f

Please sign in to comment.