diff --git a/system/Router/Router.php b/system/Router/Router.php index f0f9555ad1ac..f230c9c9e37b 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -565,7 +565,7 @@ protected function checkRoutes(string $uri): bool */ private function replaceBackReferences(string $input, array $matches): string { - $pattern = '/\$([1-' . count($matches) . '])/u'; + $pattern = '/\$([1-9][0-9]*)/u'; return preg_replace_callback( $pattern, diff --git a/tests/system/Router/RouterTest.php b/tests/system/Router/RouterTest.php index 6e22c95ed4f9..bb5aaf1fde95 100644 --- a/tests/system/Router/RouterTest.php +++ b/tests/system/Router/RouterTest.php @@ -1009,6 +1009,61 @@ public function testRoutePlaceholderAnyWithMultipleSegmentsParamTrue(): void $this->assertSame(['123/456'], $router->params()); } + /** + * @param list $expectedParams + */ + #[DataProvider('provideMultipleSegmentsParamTrueWithManyPlaceholders')] + public function testMultipleSegmentsParamTrueWithManyPlaceholders( + string $route, + string $handler, + string $uri, + array $expectedParams, + ): void { + $routingConfig = new Routing(); + $routingConfig->multipleSegmentsOneParam = true; + Factories::injectMock('config', Routing::class, $routingConfig); + + $this->createRouteCollection($routingConfig); + $this->collection->get($route, $handler); + $router = new Router($this->collection, $this->request); + + $router->handle($uri); + + $this->assertSame('\Catalog', $router->controllerName()); + $this->assertSame('lookup', $router->methodName()); + $this->assertSame($expectedParams, $router->params()); + } + + /** + * @return iterable}> + */ + public static function provideMultipleSegmentsParamTrueWithManyPlaceholders(): iterable + { + $nineSegments = implode('/', array_fill(0, 9, '(:segment)')); + $tenSegments = implode('/', array_fill(0, 10, '(:segment)')); + + yield 'nine placeholders (boundary)' => [ + $nineSegments, + 'Catalog::lookup/$1/$2/$3/$4/$5/$6/$7/$8/$9', + 'a/b/c/d/e/f/g/h/i', + ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], + ]; + + yield 'ten placeholders' => [ + $tenSegments, + 'Catalog::lookup/$1/$2/$3/$4/$5/$6/$7/$8/$9/$10', + 'a/b/c/d/e/f/g/h/i/j', + ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'], + ]; + + yield 'out-of-range reference resolves to empty string' => [ + $tenSegments, + 'Catalog::lookup/$1/$2/$3/$4/$5/$6/$7/$8/$9/$10/$11', + 'a/b/c/d/e/f/g/h/i/j', + ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', ''], + ]; + } + public function testLogsRouteAttributeInstantiationFailureWithContext(): void { $collection = clone $this->collection;