diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index ef70dcd008..ce65523745 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -1057,7 +1057,12 @@ class FrameworkExtension extends Extension
}
if (null !== $jsonManifestPath) {
- $def = new ChildDefinition('assets.json_manifest_version_strategy');
+ $definitionName = 'assets.json_manifest_version_strategy';
+ if (0 === strpos(parse_url($jsonManifestPath, PHP_URL_SCHEME), 'http')) {
+ $definitionName = 'assets.remote_json_manifest_version_strategy';
+ }
+
+ $def = new ChildDefinition($definitionName);
$def->replaceArgument(0, $jsonManifestPath);
$container->setDefinition('assets._version_'.$name, $def);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml
index 4aaa702df5..eebb28161d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml
@@ -50,5 +50,10 @@
+
+
+
+
+
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 c05c6fe3a1..ef2fd77013 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php
@@ -27,6 +27,9 @@ $container->loadFromExtension('framework', [
'json_manifest_strategy' => [
'json_manifest_path' => '/path/to/manifest.json',
],
+ 'remote_manifest' => [
+ 'json_manifest_path' => 'https://cdn.example.com/manifest.json',
+ ],
],
],
]);
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 7ae57afaab..24bfdc6456 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml
@@ -22,6 +22,7 @@
https://bar_version_strategy.example.com
+
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 a1679e389d..4a4a57bc43 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml
@@ -19,3 +19,5 @@ framework:
version_strategy: assets.custom_version_strategy
json_manifest_strategy:
json_manifest_path: '/path/to/manifest.json'
+ remote_manifest:
+ json_manifest_path: 'https://cdn.example.com/manifest.json'
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index 9588b90c4e..7f26bbcb9e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -535,7 +535,7 @@ abstract class FrameworkExtensionTest extends TestCase
// packages
$packages = $packages->getArgument(1);
- $this->assertCount(6, $packages);
+ $this->assertCount(7, $packages);
$package = $container->getDefinition((string) $packages['images_path']);
$this->assertPathPackage($container, $package, '/foo', 'SomeVersionScheme', '%%s?version=%%s');
@@ -556,6 +556,11 @@ abstract class FrameworkExtensionTest extends TestCase
$versionStrategy = $container->getDefinition((string) $package->getArgument(1));
$this->assertEquals('assets.json_manifest_version_strategy', $versionStrategy->getParent());
$this->assertEquals('/path/to/manifest.json', $versionStrategy->getArgument(0));
+
+ $package = $container->getDefinition($packages['remote_manifest']);
+ $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));
}
public function testAssetsDefaultVersionStrategyAsService()
diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json
index 60b337aa77..9aca3a8432 100644
--- a/src/Symfony/Bundle/FrameworkBundle/composer.json
+++ b/src/Symfony/Bundle/FrameworkBundle/composer.json
@@ -33,7 +33,7 @@
"require-dev": {
"doctrine/annotations": "~1.7",
"doctrine/cache": "~1.0",
- "symfony/asset": "^4.4|^5.0",
+ "symfony/asset": "^5.1",
"symfony/browser-kit": "^4.4|^5.0",
"symfony/console": "^4.4|^5.0",
"symfony/css-selector": "^4.4|^5.0",
@@ -70,7 +70,7 @@
"phpdocumentor/reflection-docblock": "<3.0",
"phpdocumentor/type-resolver": "<0.2.1",
"phpunit/phpunit": "<5.4.3",
- "symfony/asset": "<4.4",
+ "symfony/asset": "<5.1",
"symfony/browser-kit": "<4.4",
"symfony/console": "<4.4",
"symfony/dotenv": "<5.1",
diff --git a/src/Symfony/Component/Asset/CHANGELOG.md b/src/Symfony/Component/Asset/CHANGELOG.md
index 1c473dd1e5..9df5fc14d0 100644
--- a/src/Symfony/Component/Asset/CHANGELOG.md
+++ b/src/Symfony/Component/Asset/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========
+5.1.0
+-----
+
+ * added `RemoteJsonManifestVersionStrategy` to download manifest over HTTP.
+
4.2.0
-----
diff --git a/src/Symfony/Component/Asset/Tests/VersionStrategy/RemoteJsonManifestVersionStrategyTest.php b/src/Symfony/Component/Asset/Tests/VersionStrategy/RemoteJsonManifestVersionStrategyTest.php
new file mode 100644
index 0000000000..64f7e6c169
--- /dev/null
+++ b/src/Symfony/Component/Asset/Tests/VersionStrategy/RemoteJsonManifestVersionStrategyTest.php
@@ -0,0 +1,73 @@
+
+ *
+ * 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\RemoteJsonManifestVersionStrategy;
+use Symfony\Component\HttpClient\Exception\JsonException;
+use Symfony\Component\HttpClient\MockHttpClient;
+use Symfony\Component\HttpClient\Response\MockResponse;
+
+class RemoteJsonManifestVersionStrategyTest extends TestCase
+{
+ public function testGetVersion()
+ {
+ $strategy = $this->createStrategy('https://cdn.example.com/manifest-valid.json');
+
+ $this->assertSame('main.123abc.js', $strategy->getVersion('main.js'));
+ }
+
+ public function testApplyVersion()
+ {
+ $strategy = $this->createStrategy('https://cdn.example.com/manifest-valid.json');
+
+ $this->assertSame('css/styles.555def.css', $strategy->getVersion('css/styles.css'));
+ }
+
+ public function testApplyVersionWhenKeyDoesNotExistInManifest()
+ {
+ $strategy = $this->createStrategy('https://cdn.example.com/manifest-valid.json');
+
+ $this->assertSame('css/other.css', $strategy->getVersion('css/other.css'));
+ }
+
+ public function testMissingManifestFileThrowsException()
+ {
+ $this->expectException('RuntimeException');
+ $this->expectExceptionMessage('HTTP 404 returned for "https://cdn.example.com/non-existent-file.json"');
+ $strategy = $this->createStrategy('https://cdn.example.com/non-existent-file.json');
+ $strategy->getVersion('main.js');
+ }
+
+ public function testManifestFileWithBadJSONThrowsException()
+ {
+ $this->expectException(JsonException::class);
+ $this->expectExceptionMessage('Syntax error');
+ $strategy = $this->createStrategy('https://cdn.example.com/manifest-invalid.json');
+ $strategy->getVersion('main.js');
+ }
+
+ private function createStrategy($manifestUrl)
+ {
+ $httpClient = new MockHttpClient(function ($method, $url, $options) {
+ $filename = __DIR__.'/../fixtures/'.basename($url);
+
+ if (file_exists($filename)) {
+ return new MockResponse(file_get_contents($filename), ['http_headers' => ['content-type' => 'application/json']]);
+ }
+
+ return new MockResponse('{}', ['http_code' => 404]);
+ });
+
+ return new RemoteJsonManifestVersionStrategy($manifestUrl, $httpClient);
+ }
+}
diff --git a/src/Symfony/Component/Asset/VersionStrategy/RemoteJsonManifestVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/RemoteJsonManifestVersionStrategy.php
new file mode 100644
index 0000000000..db45b3b7ec
--- /dev/null
+++ b/src/Symfony/Component/Asset/VersionStrategy/RemoteJsonManifestVersionStrategy.php
@@ -0,0 +1,62 @@
+
+ *
+ * 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\HttpClient\HttpClientInterface;
+
+/**
+ * Reads the versioned path of an asset from a remote JSON manifest file.
+ *
+ * For example, the manifest file might look like this:
+ * {
+ * "main.js": "main.abc123.js",
+ * "css/styles.css": "css/styles.555abc.css"
+ * }
+ *
+ * You could then ask for the version of "main.js" or "css/styles.css".
+ */
+class RemoteJsonManifestVersionStrategy implements VersionStrategyInterface
+{
+ private $manifestData;
+ private $manifestUrl;
+ private $httpClient;
+
+ /**
+ * @param string $manifestUrl Absolute URL to the manifest file
+ */
+ public function __construct(string $manifestUrl, HttpClientInterface $httpClient)
+ {
+ $this->manifestUrl = $manifestUrl;
+ $this->httpClient = $httpClient;
+ }
+
+ /**
+ * With a manifest, we don't really know or care about what
+ * the version is. Instead, this returns the path to the
+ * versioned file.
+ */
+ public function getVersion(string $path)
+ {
+ return $this->applyVersion($path);
+ }
+
+ public function applyVersion(string $path)
+ {
+ if (null === $this->manifestData) {
+ $this->manifestData = $this->httpClient->request('GET', $this->manifestUrl, [
+ 'headers' => ['accept' => 'application/json'],
+ ])->toArray();
+ }
+
+ return $this->manifestData[$path] ?? $path;
+ }
+}
diff --git a/src/Symfony/Component/Asset/composer.json b/src/Symfony/Component/Asset/composer.json
index 0a82b5dd0c..79fac11262 100644
--- a/src/Symfony/Component/Asset/composer.json
+++ b/src/Symfony/Component/Asset/composer.json
@@ -22,6 +22,7 @@
"symfony/http-foundation": ""
},
"require-dev": {
+ "symfony/http-client": "^4.4|^5.0",
"symfony/http-foundation": "^4.4|^5.0",
"symfony/http-kernel": "^4.4|^5.0"
},