-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
Add a new Link component #22273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a new Link component #22273
Changes from all commits
b7a1591
d83b244
7be2a6c
66c4ed6
aaa54c2
c73ae6c
949a207
fc4d63b
aba1dbb
4a82c3d
b136372
fd2dd50
f0ce807
209901b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
<?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\Bridge\Twig\Extension; | ||
|
||
use Fig\Link\GenericLinkProvider; | ||
use Fig\Link\Link; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
|
||
/** | ||
* Twig extension for the Symfony WebLink component. | ||
* | ||
* @author Kévin Dunglas <dunglas@gmail.com> | ||
*/ | ||
class WebLinkExtension extends \Twig_Extension | ||
{ | ||
private $requestStack; | ||
|
||
public function __construct(RequestStack $requestStack) | ||
{ | ||
$this->requestStack = $requestStack; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getFunctions() | ||
{ | ||
return array( | ||
new \Twig_SimpleFunction('link', array($this, 'link')), | ||
new \Twig_SimpleFunction('preload', array($this, 'preload')), | ||
new \Twig_SimpleFunction('dns_prefetch', array($this, 'dnsPrefetch')), | ||
new \Twig_SimpleFunction('preconnect', array($this, 'preconnect')), | ||
new \Twig_SimpleFunction('prefetch', array($this, 'prefetch')), | ||
new \Twig_SimpleFunction('prerender', array($this, 'prerender')), | ||
); | ||
} | ||
|
||
/** | ||
* Adds a "Link" HTTP header. | ||
* | ||
* @param string $uri The relation URI | ||
* @param string $rel The relation type (e.g. "preload", "prefetch", "prerender" or "dns-prefetch") | ||
* @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") | ||
* | ||
* @return string The relation URI | ||
*/ | ||
public function link($uri, $rel, array $attributes = array()) | ||
{ | ||
if (!$request = $this->requestStack->getMasterRequest()) { | ||
return $uri; | ||
} | ||
|
||
$link = new Link($rel, $uri); | ||
foreach ($attributes as $key => $value) { | ||
$link = $link->withAttribute($key, $value); | ||
} | ||
|
||
$linkProvider = $request->attributes->get('_links', new GenericLinkProvider()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this ok or should we always populate this key? The current implementation is more memory efficient. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, I really think the ideal case would be for But I understand that's not probably not possible considering the mutable nature of our There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I agree but as you pointed out, it's not possible.
Why? |
||
$request->attributes->set('_links', $linkProvider->withLink($link)); | ||
|
||
return $uri; | ||
} | ||
|
||
/** | ||
* Preloads a resource. | ||
* | ||
* @param string $uri A public path | ||
* @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('crossorigin' => 'use-credentials')") | ||
* | ||
* @return string The path of the asset | ||
*/ | ||
public function preload($uri, array $attributes = array()) | ||
{ | ||
return $this->link($uri, 'preload', $attributes); | ||
} | ||
|
||
/** | ||
* Resolves a resource origin as early as possible. | ||
* | ||
* @param string $uri A public path | ||
* @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") | ||
* | ||
* @return string The path of the asset | ||
*/ | ||
public function dnsPrefetch($uri, array $attributes = array()) | ||
{ | ||
return $this->link($uri, 'dns-prefetch', $attributes); | ||
} | ||
|
||
/** | ||
* Initiates a early connection to a resource (DNS resolution, TCP handshake, TLS negotiation). | ||
* | ||
* @param string $uri A public path | ||
* @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") | ||
* | ||
* @return string The path of the asset | ||
*/ | ||
public function preconnect($uri, array $attributes = array()) | ||
{ | ||
return $this->link($uri, 'preconnect', $attributes); | ||
} | ||
|
||
/** | ||
* Indicates to the client that it should prefetch this resource. | ||
* | ||
* @param string $uri A public path | ||
* @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") | ||
* | ||
* @return string The path of the asset | ||
*/ | ||
public function prefetch($uri, array $attributes = array()) | ||
{ | ||
return $this->link($uri, 'prefetch', $attributes); | ||
} | ||
|
||
/** | ||
* Indicates to the client that it should prerender this resource . | ||
* | ||
* @param string $uri A public path | ||
* @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") | ||
* | ||
* @return string The path of the asset | ||
*/ | ||
public function prerender($uri, array $attributes = array()) | ||
{ | ||
return $this->link($uri, 'prerender', $attributes); | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
<?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\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; | ||
|
||
/** | ||
* @author Kévin Dunglas <dunglas@gmail.com> | ||
*/ | ||
class WebLinkExtensionTest extends TestCase | ||
{ | ||
/** | ||
* @var Request | ||
*/ | ||
private $request; | ||
|
||
/** | ||
* @var WebLinkExtension | ||
*/ | ||
private $extension; | ||
|
||
protected function setUp() | ||
{ | ||
$this->request = new Request(); | ||
|
||
$requestStack = new RequestStack(); | ||
$requestStack->push($this->request); | ||
|
||
$this->extension = new WebLinkExtension($requestStack); | ||
} | ||
|
||
public function testLink() | ||
{ | ||
$this->assertEquals('/foo.css', $this->extension->link('/foo.css', 'preload', array('as' => 'style', 'nopush' => true))); | ||
|
||
$link = (new Link('preload', '/foo.css'))->withAttribute('as', 'style')->withAttribute('nopush', true); | ||
$this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); | ||
} | ||
|
||
public function testPreload() | ||
{ | ||
$this->assertEquals('/foo.css', $this->extension->preload('/foo.css', array('as' => 'style', 'crossorigin' => true))); | ||
|
||
$link = (new Link('preload', '/foo.css'))->withAttribute('as', 'style')->withAttribute('crossorigin', true); | ||
$this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); | ||
} | ||
|
||
public function testDnsPrefetch() | ||
{ | ||
$this->assertEquals('/foo.css', $this->extension->dnsPrefetch('/foo.css', array('as' => 'style', 'crossorigin' => true))); | ||
|
||
$link = (new Link('dns-prefetch', '/foo.css'))->withAttribute('as', 'style')->withAttribute('crossorigin', true); | ||
$this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); | ||
} | ||
|
||
public function testPreconnect() | ||
{ | ||
$this->assertEquals('/foo.css', $this->extension->preconnect('/foo.css', array('as' => 'style', 'crossorigin' => true))); | ||
|
||
$link = (new Link('preconnect', '/foo.css'))->withAttribute('as', 'style')->withAttribute('crossorigin', true); | ||
$this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); | ||
} | ||
|
||
public function testPrefetch() | ||
{ | ||
$this->assertEquals('/foo.css', $this->extension->prefetch('/foo.css', array('as' => 'style', 'crossorigin' => true))); | ||
|
||
$link = (new Link('prefetch', '/foo.css'))->withAttribute('as', 'style')->withAttribute('crossorigin', true); | ||
$this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); | ||
} | ||
|
||
public function testPrerender() | ||
{ | ||
$this->assertEquals('/foo.css', $this->extension->prerender('/foo.css', array('as' => 'style', 'crossorigin' => true))); | ||
|
||
$link = (new Link('prerender', '/foo.css'))->withAttribute('as', 'style')->withAttribute('crossorigin', true); | ||
$this->assertEquals(array($link), array_values($this->request->attributes->get('_links')->getLinks())); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this dep is in require-dev in all components, but in require here, looks suspicious
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need this package in the Twig extension and the FrameworkBundle (even at runtime). But the those packages have only a soft dependency to WebLink. In the context of the full stack, I think it makes sense to add it to have something working out of the box.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not
require
d by any of the packages inreplace
, so it should indeed remain inrequire-dev
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is required if you use WebLink && (Twig Bridge || Framework Bundle). We cannot express such conditions with Composer, but the full stack framework install WebLink + Twig Bridge + Framework Bundle so it needs to have this package as dependency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still looks inconsistent to me. symfony/symfony should not behave differently than using standalone components...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fig/link-util
added back as a dependency of the WebLink component. It fixes the inconsistency, makes the component easier to use for end users and... it's the only one implementation of PSR-13 for now.