From d10b3c6464c1731bff2656a3306cf6afcce5e361 Mon Sep 17 00:00:00 2001 From: Wouter Brinkman <30869399+wouterSkepp@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:19:07 +0200 Subject: [PATCH] JsonManifestStrategy, support for FormatExtensionResolver (#1588) --- CHANGELOG.md | 1 + Resources/doc/asset-versioning.rst | 2 +- Templating/LazyFilterRuntime.php | 52 +++++++++++++++++++++- Tests/Templating/LazyFilterRuntimeTest.php | 33 ++++++++++++++ 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 627beec4..bc17e2ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This file contains a complete enumeration of all [pull requests](https://github. for a given releases. Unreleased, upcoming changes will be updated here periodically; reference the next release on our [milestones](https://github.com/liip/LiipImagineBundle/milestones) page for the latest changes. +<<<<<<< HEAD # 2.x ## unreleased diff --git a/Resources/doc/asset-versioning.rst b/Resources/doc/asset-versioning.rst index b7331599..18dbc403 100644 --- a/Resources/doc/asset-versioning.rst +++ b/Resources/doc/asset-versioning.rst @@ -17,7 +17,7 @@ setting for ``framework.assets.version``. It strips the version from the file name and appends it to the resulting image URL so that the file is found and cache busting is used. -Since LiipImagineBundle version 2.12, we integrate with the configuration +Since LiipImagineBundle version 2.13, we integrate with the configuration setting for ``framework.assets.json_manifest_path``. The manifest file is used to lookup the location of the actual file, and append the versioning string to the resulting image URL so that cache busting is used. diff --git a/Templating/LazyFilterRuntime.php b/Templating/LazyFilterRuntime.php index 19ce8f6c..6df049b0 100644 --- a/Templating/LazyFilterRuntime.php +++ b/Templating/LazyFilterRuntime.php @@ -108,13 +108,63 @@ private function appendAssetVersion(string $resolvedPath, string $path): string return $resolvedPath.$separator.$this->assetVersion; } + + if (\array_key_exists($path, $this->jsonManifest)) { + $prefixedSlash = '/' !== mb_substr($path, 0, 1) && '/' === mb_substr($this->jsonManifest[$path], 0, 1); $versionedPath = $prefixedSlash ? mb_substr($this->jsonManifest[$path], 1) : $this->jsonManifest[$path]; - $resolvedPath = str_replace($path, $versionedPath, $resolvedPath); + $originalExt = pathinfo($path, PATHINFO_EXTENSION); + $resolvedExt = pathinfo($resolvedPath, PATHINFO_EXTENSION); + + if ($originalExt !== $resolvedExt) { + $path = str_replace('.'.$originalExt, '.'.$resolvedExt, $path); + $versionedPath = str_replace('.'.$originalExt, '.'.$resolvedExt, $versionedPath); + } + + $versioning = $this->captureVersion(pathinfo($path, PATHINFO_BASENAME), pathinfo($versionedPath, PATHINFO_BASENAME)); + $resolvedFilename = pathinfo($resolvedPath, PATHINFO_BASENAME); + $resolvedDir = pathinfo($resolvedPath, PATHINFO_DIRNAME); + $resolvedPath = $resolvedDir.'/'.$this->insertVersion($resolvedFilename, $versioning['version'], $versioning['position']); } return $resolvedPath; } + + /** + * Capture the versioning string from the versioned filename + */ + private function captureVersion(string $originalFilename, string $versionedFilename): array + { + $originalLength = strlen($originalFilename); + $versionedLength = strlen($versionedFilename); + + for ($i = 0; $i < $originalLength && $i < $versionedLength; $i++) { + if ($originalFilename[$i] !== $versionedFilename[$i]) { + break; + } + } + + $version = substr($versionedFilename, $i, $versionedLength - $originalLength); + + return ['version' => $version, 'position' => $i]; + } + + /** + * Insert the version string into our resolved filename + */ + private function insertVersion(string $resolvedFilename, string $version, int $position): string + { + if ($position < 0 || $position > strlen($resolvedFilename)) { + return $resolvedFilename; + } + + $firstPart = substr($resolvedFilename, 0, $position); + $secondPart = substr($resolvedFilename, $position); + + $versionedFilename = $firstPart . $version . $secondPart; + + return $versionedFilename; + } } diff --git a/Tests/Templating/LazyFilterRuntimeTest.php b/Tests/Templating/LazyFilterRuntimeTest.php index 6e55b9ef..a89cc03b 100644 --- a/Tests/Templating/LazyFilterRuntimeTest.php +++ b/Tests/Templating/LazyFilterRuntimeTest.php @@ -128,6 +128,29 @@ public function testJsonManifestVersionHandling(string $sourcePath, string $vers $this->assertSame($expectedPath, $actualPath); } + /** + * @dataProvider provideJsonManifestSwapExt + */ + public function testJsonManifestVersionHandlingWithExtensionSwapping(string $sourcePath, string $versionedPath, $originalExt, $newExt): void + { + $this->runtime = new LazyFilterRuntime($this->manager, null, self::JSON_MANIFEST); + + $cachePath = 'image/cache/'.self::FILTER.'/'.('/' === (mb_substr($sourcePath, 0, 1)) ? mb_substr($sourcePath, 1) : $sourcePath); + $cachePath = str_replace('.'.$originalExt, '.'.$newExt, $cachePath); + $expectedPath = 'image/cache/'.self::FILTER.'/'.('/' === (mb_substr($versionedPath, 0, 1)) ? mb_substr($versionedPath, 1) : $versionedPath); + $expectedPath = str_replace('.'.$originalExt, '.'.$newExt, $expectedPath); + + $this->manager + ->expects($this->once()) + ->method('getBrowserPath') + ->with($sourcePath, self::FILTER) + ->willReturn($cachePath); + + $actualPath = $this->runtime->filter($versionedPath, self::FILTER); + + $this->assertSame($expectedPath, $actualPath); + } + public function provideJsonManifest(): array { return [ @@ -138,6 +161,16 @@ public function provideJsonManifest(): array ]; } + public function provideJsonManifestSwapExt(): array + { + return [ + 'query parameter, no slash' => [array_keys(self::JSON_MANIFEST)[0], array_values(self::JSON_MANIFEST)[0], 'png', 'webp'], + 'in filename, no slash' => [array_keys(self::JSON_MANIFEST)[1], array_values(self::JSON_MANIFEST)[1], 'png', 'webp'], + 'query parameter, slash' => [array_keys(self::JSON_MANIFEST)[2], array_values(self::JSON_MANIFEST)[2], 'png', 'webp'], + 'in filename, slash' => [array_keys(self::JSON_MANIFEST)[3], array_values(self::JSON_MANIFEST)[3], 'png', 'webp'], + ]; + } + public function testInvokeFilterCacheMethod(): void { $expectedInputPath = 'thePathToTheImage';