-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Asset] added the component #13234
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
[Asset] added the component #13234
Changes from all commits
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,144 @@ | ||
<?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 Symfony\Component\Asset\Packages; | ||
use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy; | ||
|
||
/** | ||
* Twig extension for the Symfony Asset component. | ||
* | ||
* @author Fabien Potencier <fabien@symfony.com> | ||
*/ | ||
class AssetExtension extends \Twig_Extension | ||
{ | ||
private $packages; | ||
private $foundationExtension; | ||
|
||
/** | ||
* Passing an HttpFoundationExtension instance as a second argument must not be relied on | ||
* as it's only there to maintain BC with older Symfony version. It will be removed in 3.0. | ||
*/ | ||
public function __construct(Packages $packages, HttpFoundationExtension $foundationExtension = null) | ||
{ | ||
$this->packages = $packages; | ||
$this->foundationExtension = $foundationExtension; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getFunctions() | ||
{ | ||
return array( | ||
new \Twig_SimpleFunction('asset', array($this, 'getAssetUrl')), | ||
new \Twig_SimpleFunction('asset_version', array($this, 'getAssetVersion')), | ||
new \Twig_SimpleFunction('assets_version', array($this, 'getAssetsVersion')), | ||
); | ||
} | ||
|
||
/** | ||
* Returns the public url/path of an asset. | ||
* | ||
* If the package used to generate the path is an instance of | ||
* UrlPackage, you will always get a URL and not a path. | ||
* | ||
* @param string $path A public path | ||
* @param string $packageName The name of the asset package to use | ||
* | ||
* @return string The public path of the asset | ||
*/ | ||
public function getAssetUrl($path, $packageName = null, $absolute = false, $version = null) | ||
{ | ||
// BC layer to be removed in 3.0 | ||
if (2 < $count = func_num_args()) { | ||
trigger_error('Generating absolute URLs with the Twig asset() function was deprecated in 2.7 and will be removed in 3.0. Please use absolute_url() instead.', E_USER_DEPRECATED); | ||
if (4 === $count) { | ||
trigger_error('Forcing a version with the Twig asset() function was deprecated in 2.7 and will be removed in 3.0.', E_USER_DEPRECATED); | ||
} | ||
|
||
$args = func_get_args(); | ||
|
||
return $this->getLegacyAssetUrl($path, $packageName, $args[2], isset($args[3]) ? $args[3] : null); | ||
} | ||
|
||
return $this->packages->getUrl($path, $packageName); | ||
} | ||
|
||
/** | ||
* Returns the version of an asset. | ||
* | ||
* @param string $path A public path | ||
* @param string $packageName The name of the asset package to use | ||
* | ||
* @return string The asset version | ||
*/ | ||
public function getAssetVersion($path, $packageName = null) | ||
{ | ||
return $this->packages->getVersion($path, $packageName); | ||
} | ||
|
||
public function getAssetsVersion($packageName = null) | ||
{ | ||
trigger_error('The Twig assets_version() function was deprecated in 2.7 and will be removed in 3.0. Please use asset_version() instead.', E_USER_DEPRECATED); | ||
|
||
return $this->packages->getVersion('/', $packageName); | ||
} | ||
|
||
private function getLegacyAssetUrl($path, $packageName = null, $absolute = false, $version = null) | ||
{ | ||
if ($version) { | ||
$package = $this->packages->getPackage($packageName); | ||
|
||
$v = new \ReflectionProperty($package, 'versionStrategy'); | ||
$v->setAccessible(true); | ||
|
||
$currentVersionStrategy = $v->getValue($package); | ||
|
||
$f = new \ReflectionProperty($currentVersionStrategy, 'format'); | ||
$f->setAccessible(true); | ||
$format = $f->getValue($currentVersionStrategy); | ||
|
||
$v->setValue($package, new StaticVersionStrategy($version, $format)); | ||
} | ||
|
||
try { | ||
$url = $this->packages->getUrl($path, $packageName); | ||
} catch (\Exception $e) { | ||
if ($version) { | ||
$v->setValue($package, $currentVersionStrategy); | ||
} | ||
|
||
throw $e; | ||
} | ||
|
||
if ($version) { | ||
$v->setValue($package, $currentVersionStrategy); | ||
} | ||
|
||
if ($absolute) { | ||
return $this->foundationExtension->generateAbsoluteUrl($url); | ||
} | ||
|
||
return $url; | ||
} | ||
|
||
/** | ||
* Returns the name of the extension. | ||
* | ||
* @return string The extension name | ||
*/ | ||
public function getName() | ||
{ | ||
return 'asset'; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?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 Symfony\Bridge\Twig\Extension\AssetExtension; | ||
use Symfony\Component\Asset\Package; | ||
use Symfony\Component\Asset\Packages; | ||
use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy; | ||
|
||
class AssetExtensionTest extends \PHPUnit_Framework_TestCase | ||
{ | ||
public function testLegacyGetAssetUrl() | ||
{ | ||
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); | ||
|
||
$foundationExtension = $this->getMockBuilder('Symfony\Bridge\Twig\Extension\HttpFoundationExtension')->disableOriginalConstructor()->getMock(); | ||
$foundationExtension | ||
->expects($this->any()) | ||
->method('generateAbsoluteUrl') | ||
->will($this->returnCallback(function ($arg) { return 'http://localhost/'.$arg; })) | ||
; | ||
|
||
$package = new Package(new StaticVersionStrategy('22', '%s?version=%s')); | ||
$packages = new Packages($package); | ||
$extension = new AssetExtension($packages, $foundationExtension); | ||
|
||
$this->assertEquals('me.png?version=42', $extension->getAssetUrl('me.png', null, false, '42')); | ||
$this->assertEquals('http://localhost/me.png?version=22', $extension->getAssetUrl('me.png', null, true)); | ||
$this->assertEquals('http://localhost/me.png?version=42', $extension->getAssetUrl('me.png', null, true, '42')); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,6 +53,44 @@ public function getConfigTreeBuilder() | |
return $v; | ||
}) | ||
->end() | ||
->validate() | ||
->ifTrue(function ($v) { return isset($v['templating']); }) | ||
->then(function ($v) { | ||
if ($v['templating']['assets_version'] | ||
|| count($v['templating']['assets_base_urls']['http']) | ||
|| count($v['templating']['assets_base_urls']['ssl']) | ||
|| count($v['templating']['packages']) | ||
) { | ||
trigger_error('The assets settings under framework.templating are deprecated since version 2.7 and will be removed in 3.0. Use the framework.assets configuration key instead', E_USER_DEPRECATED); | ||
|
||
// convert the old configuration to the new one | ||
if (isset($v['assets'])) { | ||
throw new LogicException('You cannot use assets settings under "templating.templating" and "assets" configurations in the same project.'); | ||
} | ||
|
||
$v['assets'] = array( | ||
'version' => $v['templating']['assets_version'], | ||
'version_format' => $v['templating']['assets_version_format'], | ||
'base_path' => '', | ||
'base_urls' => array_values(array_unique(array_merge($v['templating']['assets_base_urls']['http'], $v['templating']['assets_base_urls']['ssl']))), | ||
'packages' => array(), | ||
); | ||
|
||
foreach ($v['templating']['packages'] as $name => $config) { | ||
$v['assets']['packages'][$name] = array( | ||
'version' => (string) $config['version'], | ||
'version_format' => $config['version_format'], | ||
'base_path' => '', | ||
'base_urls' => array_values(array_unique(array_merge($config['base_urls']['http'], $config['base_urls']['ssl']))), | ||
); | ||
} | ||
} | ||
|
||
unset($v['templating']['assets_version'], $v['templating']['assets_version_format'], $v['templating']['assets_base_urls'], $v['templating']['packages']); | ||
|
||
return $v; | ||
}) | ||
->end() | ||
->children() | ||
->scalarNode('secret')->end() | ||
->scalarNode('http_method_override') | ||
|
@@ -108,6 +146,7 @@ public function getConfigTreeBuilder() | |
$this->addSessionSection($rootNode); | ||
$this->addRequestSection($rootNode); | ||
$this->addTemplatingSection($rootNode); | ||
$this->addAssetsSection($rootNode); | ||
$this->addTranslatorSection($rootNode); | ||
$this->addValidationSection($rootNode); | ||
$this->addAnnotationsSection($rootNode); | ||
|
@@ -347,8 +386,8 @@ private function addTemplatingSection(ArrayNodeDefinition $rootNode) | |
->info('templating configuration') | ||
->canBeUnset() | ||
->children() | ||
->scalarNode('assets_version')->defaultValue(null)->end() | ||
->scalarNode('assets_version_format')->defaultValue('%%s?%%s')->end() | ||
->scalarNode('assets_version')->defaultNull()->info('Deprecated since 2.7, will be removed in 3.0. Use the new assets entry instead.')->end() | ||
->scalarNode('assets_version_format')->defaultValue('%%s?%%s')->info('Deprecated since 2.7, will be removed in 3.0. Use the new assets entry instead.')->end() | ||
->scalarNode('hinclude_default_template')->defaultNull()->end() | ||
->arrayNode('form') | ||
->addDefaultsIfNotSet() | ||
|
@@ -370,6 +409,7 @@ private function addTemplatingSection(ArrayNodeDefinition $rootNode) | |
->fixXmlConfig('assets_base_url') | ||
->children() | ||
->arrayNode('assets_base_urls') | ||
->info('Deprecated since 2.7, will be removed in 3.0. Use the new assets entry instead.') | ||
->performNoDeepMerging() | ||
->addDefaultsIfNotSet() | ||
->beforeNormalization() | ||
|
@@ -417,6 +457,7 @@ private function addTemplatingSection(ArrayNodeDefinition $rootNode) | |
->fixXmlConfig('package') | ||
->children() | ||
->arrayNode('packages') | ||
->info('Deprecated since 2.7, will be removed in 3.0. Use the new assets entry instead.') | ||
->useAttributeAsKey('name') | ||
->prototype('array') | ||
->fixXmlConfig('base_url') | ||
|
@@ -452,6 +493,54 @@ private function addTemplatingSection(ArrayNodeDefinition $rootNode) | |
; | ||
} | ||
|
||
private function addAssetsSection(ArrayNodeDefinition $rootNode) | ||
{ | ||
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. This code reminds me http://www.history.com/topics/ancient-history/the-egyptian-pyramids, 👯 #troll |
||
$rootNode | ||
->children() | ||
->arrayNode('assets') | ||
->info('assets configuration') | ||
->canBeUnset() | ||
->fixXmlConfig('base_url') | ||
->children() | ||
->scalarNode('version')->defaultNull()->end() | ||
->scalarNode('version_format')->defaultValue('%%s?%%s')->end() | ||
->scalarNode('base_path')->defaultValue('')->end() | ||
->arrayNode('base_urls') | ||
->requiresAtLeastOneElement() | ||
->beforeNormalization() | ||
->ifTrue(function ($v) { return !is_array($v); }) | ||
->then(function ($v) { return array($v); }) | ||
->end() | ||
->prototype('scalar')->end() | ||
->end() | ||
->end() | ||
->fixXmlConfig('package') | ||
->children() | ||
->arrayNode('packages') | ||
->useAttributeAsKey('name') | ||
->prototype('array') | ||
->fixXmlConfig('base_url') | ||
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. could all this config below share the same code as the one before? extract it as a function maybe? 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. We could indeed use a private method returning an array node, and then using 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. Not sure it would bring us a lot. |
||
->children() | ||
->scalarNode('version')->defaultNull()->end() | ||
->scalarNode('version_format')->defaultNull()->end() | ||
->scalarNode('base_path')->defaultValue('')->end() | ||
->arrayNode('base_urls') | ||
->requiresAtLeastOneElement() | ||
->beforeNormalization() | ||
->ifTrue(function ($v) { return !is_array($v); }) | ||
->then(function ($v) { return array($v); }) | ||
->end() | ||
->prototype('scalar')->end() | ||
->end() | ||
->end() | ||
->end() | ||
->end() | ||
->end() | ||
->end() | ||
->end() | ||
; | ||
} | ||
|
||
private function addTranslatorSection(ArrayNodeDefinition $rootNode) | ||
{ | ||
$rootNode | ||
|
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.
what if getting the url of the package throws an exception for some reason ? Resetting the internal state needs to be done in this case too, to avoid leaving it in a bad state
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.
fixed