diff --git a/Classes/Service/VariablesService.php b/Classes/Service/VariablesService.php index a9aa907..91ce70a 100644 --- a/Classes/Service/VariablesService.php +++ b/Classes/Service/VariablesService.php @@ -87,30 +87,31 @@ private function replaceMarkersInText( MarkerCollection $markerCollection, string &$text ): void { - $markerRegexp = '/(' . implode('|', array_map(preg_quote(...), $markerCollection->getMarkerKeys())) . ')/'; - - $loops = 0; - - while (preg_match($markerRegexp, $text) && $loops++ < self::MAXIMUM_LOOP_COUNT) { - foreach ($markerCollection as $marker) { - $newContent = str_replace( - $marker->getMarkerWithBrackets(), - $marker->replacement, - $text - ); + if ($markerCollection->isEmpty() === false) { + $markerRegexp = $this->buildMarkerRegExp($markerCollection); + $loops = 0; + + while (preg_match($markerRegexp, $text) && $loops++ < self::MAXIMUM_LOOP_COUNT) { + foreach ($markerCollection as $marker) { + $newContent = str_replace( + $marker->getMarkerWithBrackets(), + $marker->replacement, + $text + ); + + if ($newContent === $text) { + continue; + } - if ($newContent === $text) { - continue; + // Assign a cache key associated with the marker + $this->cacheTags->add( + CacheKeyUtility::getCacheKey( + $marker->getMarkerWithBrackets() + ) + ); + $this->usedMarkerKeys[] = $marker->key; + $text = $newContent; } - - // Assign a cache key associated with the marker - $this->cacheTags->add( - CacheKeyUtility::getCacheKey( - $marker->getMarkerWithBrackets() - ) - ); - $this->usedMarkerKeys[] = $marker->key; - $text = $newContent; } } @@ -169,6 +170,16 @@ private function getMarkers( return $markers; } + private function buildMarkerRegExp(MarkerCollection $markerCollection): string + { + $quotedMarkerKeys = []; + foreach ($markerCollection->getMarkerKeys() as $markerKey) { + $quotedMarkerKeys[] = preg_quote($markerKey, '/'); + } + + return '/(' . implode('|', $quotedMarkerKeys) . ')/'; + } + private function setCacheTags( ServerRequestInterface $request, ): void { diff --git a/Tests/Unit/Service/VariablesServiceTest.php b/Tests/Unit/Service/VariablesServiceTest.php new file mode 100644 index 0000000..85b29e8 --- /dev/null +++ b/Tests/Unit/Service/VariablesServiceTest.php @@ -0,0 +1,74 @@ + + */ + public static function markerKeyDataProvider(): iterable + { + yield 'plain key' => ['key', 'replacement', 'before {{key}} after', 'before replacement after']; + yield 'key containing a slash' => ['foo/bar', 'replacement', 'before {{foo/bar}} after', 'before replacement after']; + yield 'key containing a regular expression special character' => ['foo.bar', 'replacement', 'before {{foo.bar}} after', 'before replacement after']; + } + + #[Test] + #[DataProvider('markerKeyDataProvider')] + public function replacesMarkerRegardlessOfSpecialCharactersInKey( + string $markerKey, + string $replacement, + string $text, + string $expected, + ): void { + $markerCollection = new MarkerCollection(); + $markerCollection->add(new Marker(1, $markerKey, $replacement)); + + $this->replaceMarkersInStructure($markerCollection, $text); + + self::assertSame($expected, $text); + } + + private function replaceMarkersInStructure(MarkerCollection $markerCollection, mixed &$structure): void + { + $extensionConfiguration = $this->createMock(ExtensionConfiguration::class); + $extensionConfiguration->method('get')->willReturn(false); + + $subject = new VariablesService( + $extensionConfiguration, + $this->createMock(Context::class), + $this->createMock(ConnectionPool::class), + ); + + $reflectionMethod = new \ReflectionMethod($subject, 'replaceMarkersInStructure'); + $reflectionMethod->invokeArgs($subject, [$markerCollection, &$structure]); + } +}