diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index eab898b8829fd..897341c554c45 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -580,6 +580,7 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode)
->scalarNode('version')->defaultNull()->end()
->scalarNode('version_format')->defaultValue('%%s?%%s')->end()
->scalarNode('json_manifest_path')->defaultNull()->end()
+ ->booleanNode('cache')->defaultFalse()->end()
->scalarNode('base_path')->defaultValue('')->end()
->arrayNode('base_urls')
->requiresAtLeastOneElement()
@@ -622,6 +623,7 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode)
->end()
->scalarNode('version_format')->defaultNull()->end()
->scalarNode('json_manifest_path')->defaultNull()->end()
+ ->scalarNode('cache')->defaultFalse()->end()
->scalarNode('base_path')->defaultValue('')->end()
->arrayNode('base_urls')
->requiresAtLeastOneElement()
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 666797f2039c3..ffa5ae4eec5b7 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -996,6 +996,10 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co
$defaultVersion = $this->createVersion($container, $config['version'], $config['version_format'], $config['json_manifest_path'], '_default');
}
+ if ($config['cache']) {
+ $defaultVersion = $this->decorateVersionStrategyWithCache($container, $defaultVersion);
+ }
+
$defaultPackage = $this->createPackageDefinition($config['base_path'], $config['base_urls'], $defaultVersion);
$container->setDefinition('assets._default_package', $defaultPackage);
@@ -1011,6 +1015,9 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co
$format = $package['version_format'] ?: $config['version_format'];
$version = isset($package['version']) ? $package['version'] : null;
$version = $this->createVersion($container, $version, $format, $package['json_manifest_path'], $name);
+ if ($package['cache']) {
+ $version = $this->decorateVersionStrategyWithCache($container, $version);
+ }
}
$container->setDefinition('assets._package_'.$name, $this->createPackageDefinition($package['base_path'], $package['base_urls'], $version));
@@ -1073,6 +1080,16 @@ private function createVersion(ContainerBuilder $container, ?string $version, ?s
return new Reference('assets.empty_version_strategy');
}
+ private function decorateVersionStrategyWithCache(ContainerBuilder $container, Reference $reference): Reference
+ {
+ $cachedReference = new Reference($reference.'.cached');
+ $cached = new ChildDefinition('assets.cached_version_strategy');
+ $cached->replaceArgument(0, $reference);
+ $container->setDefinition($cachedReference, $cached);
+
+ return $cachedReference;
+ }
+
private function registerTranslatorConfiguration(array $config, ContainerBuilder $container, LoaderInterface $loader, string $defaultLocale)
{
if (!$this->isConfigEnabled($container, $config)) {
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml
index 73ec21ab429e0..0613bf6141c45 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml
@@ -55,5 +55,10 @@
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml
index 9959debfa94a5..956582b9a2756 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml
@@ -15,6 +15,10 @@
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
index 99ffbabb82cdc..c99cda3f50274 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
@@ -144,6 +144,7 @@
+
@@ -157,6 +158,7 @@
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
index 3ba4c3ecfecf8..13168dd3bb60e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
@@ -86,6 +86,7 @@ public function testAssetsCanBeEnabled()
'base_urls' => [],
'packages' => [],
'json_manifest_path' => null,
+ 'cache' => false,
];
$this->assertEquals($defaultConfig, $config['assets']);
@@ -439,6 +440,7 @@ protected static function getBundleDefaultConfig()
'base_urls' => [],
'packages' => [],
'json_manifest_path' => null,
+ 'cache' => false,
],
'cache' => [
'pools' => [],
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php
index ef2fd77013f85..20b2f03851f1d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php
@@ -30,6 +30,10 @@
'remote_manifest' => [
'json_manifest_path' => 'https://cdn.example.com/manifest.json',
],
+ 'cached' => [
+ 'json_manifest_path' => 'https://cdn.example.com/manifest.json',
+ 'cache' => true,
+ ],
],
],
]);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml
index 24bfdc6456185..0ab7d2c7d487e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml
@@ -23,6 +23,7 @@
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml
index 4a4a57bc43a79..72260201a777f 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml
@@ -21,3 +21,6 @@ framework:
json_manifest_path: '/path/to/manifest.json'
remote_manifest:
json_manifest_path: 'https://cdn.example.com/manifest.json'
+ cached:
+ json_manifest_path: 'https://cdn.example.com/manifest.json'
+ cache: true
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index f17597589683d..c8a373d192a59 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -535,7 +535,7 @@ public function testAssets()
// packages
$packages = $packages->getArgument(1);
- $this->assertCount(7, $packages);
+ $this->assertCount(8, $packages);
$package = $container->getDefinition((string) $packages['images_path']);
$this->assertPathPackage($container, $package, '/foo', 'SomeVersionScheme', '%%s?version=%%s');
@@ -561,6 +561,10 @@ public function testAssets()
$versionStrategy = $container->getDefinition($package->getArgument(1));
$this->assertSame('assets.remote_json_manifest_version_strategy', $versionStrategy->getParent());
$this->assertSame('https://cdn.example.com/manifest.json', $versionStrategy->getArgument(0));
+
+ $package = $container->getDefinition($packages['cached']);
+ $versionStrategy = $container->getDefinition($package->getArgument(1));
+ $this->assertSame('assets.cached_version_strategy', $versionStrategy->getParent());
}
public function testAssetsDefaultVersionStrategyAsService()
diff --git a/src/Symfony/Component/Asset/Tests/VersionStrategy/CachedVersionStrategyTest.php b/src/Symfony/Component/Asset/Tests/VersionStrategy/CachedVersionStrategyTest.php
new file mode 100644
index 0000000000000..3caf78d9bb0de
--- /dev/null
+++ b/src/Symfony/Component/Asset/Tests/VersionStrategy/CachedVersionStrategyTest.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Asset\Tests\VersionStrategy;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Asset\VersionStrategy\CachedVersionStrategy;
+use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;
+use Symfony\Component\Cache\Adapter\ArrayAdapter;
+
+class CachedVersionStrategyTest extends TestCase
+{
+ public function testGetVersion()
+ {
+ $value = '1.0.0';
+ $strategy = $this->getMockBuilder(VersionStrategyInterface::class)->getMock();
+ $strategy
+ ->expects($this->once())
+ ->method('getVersion')
+ ->willReturn($value);
+
+ $cache = new ArrayAdapter();
+ $cachedVersionStrategy = new CachedVersionStrategy($strategy, $cache);
+ $path = 'test-path';
+
+ $this->assertSame($value, $cachedVersionStrategy->getVersion($path));
+ $this->assertSame($value, $cachedVersionStrategy->getVersion($path), '2nd call is cached');
+ }
+
+ public function testApplyVersion()
+ {
+ $value = 'test/path/1.0.0';
+ $strategy = $this->getMockBuilder(VersionStrategyInterface::class)->getMock();
+ $strategy
+ ->expects($this->once())
+ ->method('applyVersion')
+ ->willReturn($value);
+
+ $cache = new ArrayAdapter();
+ $cachedVersionStrategy = new CachedVersionStrategy($strategy, $cache);
+ $path = 'test/path';
+
+ $this->assertSame($value, $cachedVersionStrategy->applyVersion($path));
+ $this->assertSame($value, $cachedVersionStrategy->applyVersion($path), '2nd call is cached');
+ }
+}
diff --git a/src/Symfony/Component/Asset/VersionStrategy/CachedVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/CachedVersionStrategy.php
new file mode 100644
index 0000000000000..63e6c015b6e56
--- /dev/null
+++ b/src/Symfony/Component/Asset/VersionStrategy/CachedVersionStrategy.php
@@ -0,0 +1,52 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Asset\VersionStrategy;
+
+use Symfony\Contracts\Cache\CacheInterface;
+
+/**
+ * Cache generated versions and path of any version strategy.
+ *
+ * @author Jérôme TAMARELLE
+ */
+class CachedVersionStrategy implements VersionStrategyInterface
+{
+ private $strategy;
+
+ private $cache;
+
+ public function __construct(VersionStrategyInterface $strategy, CacheInterface $cache)
+ {
+ $this->strategy = $strategy;
+ $this->cache = $cache;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getVersion(string $path)
+ {
+ return $this->cache->get('v_'.rawurlencode($path), function () use ($path) {
+ return $this->strategy->getVersion($path);
+ });
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function applyVersion(string $path)
+ {
+ return $this->cache->get('p_'.rawurlencode($path), function () use ($path) {
+ return $this->strategy->applyVersion($path);
+ });
+ }
+}
diff --git a/src/Symfony/Component/Asset/composer.json b/src/Symfony/Component/Asset/composer.json
index 79fac112628d3..b65533000c42c 100644
--- a/src/Symfony/Component/Asset/composer.json
+++ b/src/Symfony/Component/Asset/composer.json
@@ -24,7 +24,8 @@
"require-dev": {
"symfony/http-client": "^4.4|^5.0",
"symfony/http-foundation": "^4.4|^5.0",
- "symfony/http-kernel": "^4.4|^5.0"
+ "symfony/http-kernel": "^4.4|^5.0",
+ "symfony/cache": "^4.4|^5.0"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Asset\\": "" },