diff --git a/Templating/LazyFilterRuntime.php b/Templating/LazyFilterRuntime.php index 3dcdd4fbe..2c8426263 100644 --- a/Templating/LazyFilterRuntime.php +++ b/Templating/LazyFilterRuntime.php @@ -76,6 +76,12 @@ public function filterCache(string $path, string $filter, array $config = [], ?s private function cleanPath(string $path): string { + // strip absolute URLs (e.g. remote filesystems like S3) to their path + // host guard: parse_url(PHP_URL_PATH) would also truncate local names at "?"/"#" and strip the version query string + if (null !== parse_url($path, PHP_URL_HOST)) { + $path = parse_url($path, PHP_URL_PATH) ?? $path; + } + if (!$this->assetVersion && !$this->jsonManifest) { return $path; } diff --git a/Tests/Templating/LazyFilterRuntimeTest.php b/Tests/Templating/LazyFilterRuntimeTest.php index a89cc03b4..3a1986d20 100644 --- a/Tests/Templating/LazyFilterRuntimeTest.php +++ b/Tests/Templating/LazyFilterRuntimeTest.php @@ -48,22 +48,23 @@ protected function setUp(): void public function provideImageNames(): iterable { - yield 'regular' => ['image' => 'cats.jpeg', 'urlimage' => 'cats.jpeg']; - yield 'whitespace' => ['image' => 'white cat.jpeg', 'urlimage' => 'white%20cat.jpeg']; - yield 'plus' => ['image' => 'cat+plus.jpeg', 'urlimage' => 'cat%2Bplus.jpeg']; - yield 'questionmark' => ['image' => 'cat?question.jpeg', 'urlimage' => 'cat%3Fquestion.jpeg']; - yield 'hash' => ['image' => 'cat#hash.jpeg', 'urlimage' => 'cat%23hash.jpeg']; + yield 'regular' => ['image' => 'cats.jpeg', 'callingimage' => 'cats.jpeg', 'urlimage' => 'cats.jpeg']; + yield 'whitespace' => ['image' => 'white cat.jpeg', 'callingimage' => 'white cat.jpeg', 'urlimage' => 'white%20cat.jpeg']; + yield 'plus' => ['image' => 'cat+plus.jpeg', 'callingimage' => 'cat+plus.jpeg', 'urlimage' => 'cat%2Bplus.jpeg']; + yield 'questionmark' => ['image' => 'cat?question.jpeg', 'callingimage' => 'cat?question.jpeg', 'urlimage' => 'cat%3Fquestion.jpeg']; + yield 'hash' => ['image' => 'cat#hash.jpeg', 'callingimage' => 'cat#hash.jpeg', 'urlimage' => 'cat%23hash.jpeg']; + yield 'absolute url' => ['image' => 'https://www.example.org/images/dogs.jpg', 'callingimage' => '/images/dogs.jpg', 'urlimage' => '/media/cache/thumbnail/images/dogs.jpg']; } /** * @dataProvider provideImageNames */ - public function testInvokeFilterMethod($image, $urlimage): void + public function testInvokeFilterMethod($image, $callingimage, $urlimage): void { $this->manager ->expects($this->once()) ->method('getBrowserPath') - ->with($image, self::FILTER) + ->with($callingimage, self::FILTER) ->willReturn($urlimage); $actualPath = $this->runtime->filter($image, self::FILTER); @@ -71,6 +72,20 @@ public function testInvokeFilterMethod($image, $urlimage): void $this->assertSame($urlimage, $actualPath); } + public function testLocalPathContainingQuestionMarkIsNotTruncated(): void + { + // guards the host check in cleanPath(): without it, parse_url() would reduce this to "cat" + $path = 'cat?question.jpeg'; + + $this->manager + ->expects($this->once()) + ->method('getBrowserPath') + ->with($path, self::FILTER) + ->willReturn('irrelevant'); + + $this->runtime->filter($path, self::FILTER); + } + public function testVersionHandling(): void { $this->runtime = new LazyFilterRuntime($this->manager, self::VERSION); @@ -187,6 +202,22 @@ public function testInvokeFilterCacheMethod(): void $this->assertSame($expectedCachePath, $actualPath); } + public function testInvokeFilterCacheMethodWithAbsoluteUrl(): void + { + $expectedInputPath = '/images/dogs.jpg'; + $expectedCachePath = '/media/cache/thumbnail/images/dogs.jpg'; + + $this->manager + ->expects($this->once()) + ->method('resolve') + ->with($expectedInputPath, self::FILTER) + ->willReturn($expectedCachePath); + + $actualPath = $this->runtime->filterCache('https://www.example.org/images/dogs.jpg', self::FILTER); + + $this->assertSame($expectedCachePath, $actualPath); + } + public function testInvokeFilterCacheMethodWithRuntimeConfig(): void { $expectedInputPath = 'thePathToTheImage';