From e83f07b32214168890f1dc7767bf940298741f08 Mon Sep 17 00:00:00 2001 From: banghamid <62824084+banghamid@users.noreply.github.com> Date: Wed, 11 Nov 2026 17:21:23 +0700 Subject: [PATCH 1/3] fix(Router): fix back-reference regex in replaceBackReferences() for 9+ placeholders --- system/Router/Router.php | 2 +- tests/system/Router/RouterTest.php | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) 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..ae7a06a9284c 100644 --- a/tests/system/Router/RouterTest.php +++ b/tests/system/Router/RouterTest.php @@ -1009,6 +1009,26 @@ public function testRoutePlaceholderAnyWithMultipleSegmentsParamTrue(): void $this->assertSame(['123/456'], $router->params()); } + public function testMultipleSegmentsParamTrueWithTenPlaceholders(): void + { + $routingConfig = new Routing(); + $routingConfig->multipleSegmentsOneParam = true; + Factories::injectMock('config', Routing::class, $routingConfig); + + $this->createRouteCollection($routingConfig); + $this->collection->get( + '(:segment)/(:segment)/(:segment)/(:segment)/(:segment)/(:segment)/(:segment)/(:segment)/(:segment)/(:segment)', + 'Catalog::lookup/$1/$2/$3/$4/$5/$6/$7/$8/$9/$10', + ); + $router = new Router($this->collection, $this->request); + + $router->handle('a/b/c/d/e/f/g/h/i/j'); + + $this->assertSame('\Catalog', $router->controllerName()); + $this->assertSame('lookup', $router->methodName()); + $this->assertSame(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'], $router->params()); + } + public function testLogsRouteAttributeInstantiationFailureWithContext(): void { $collection = clone $this->collection; From a0d4dbb929e74c728d86573d0905f8c4c4777d71 Mon Sep 17 00:00:00 2001 From: banghamid <62824084+banghamid@users.noreply.github.com> Date: Thu, 12 Nov 2026 02:48:14 +0700 Subject: [PATCH 2/3] test(Router): cover 9-placeholder boundary and out-of-range back-references --- tests/system/Router/RouterTest.php | 51 +++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/tests/system/Router/RouterTest.php b/tests/system/Router/RouterTest.php index ae7a06a9284c..e2541cf0962b 100644 --- a/tests/system/Router/RouterTest.php +++ b/tests/system/Router/RouterTest.php @@ -1009,24 +1009,59 @@ public function testRoutePlaceholderAnyWithMultipleSegmentsParamTrue(): void $this->assertSame(['123/456'], $router->params()); } - public function testMultipleSegmentsParamTrueWithTenPlaceholders(): void - { + /** + * @param list $expectedParams + */ + #[DataProvider('provideMultipleSegmentsParamTruePlaceholders')] + 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( - '(:segment)/(:segment)/(:segment)/(:segment)/(:segment)/(:segment)/(:segment)/(:segment)/(:segment)/(:segment)', - 'Catalog::lookup/$1/$2/$3/$4/$5/$6/$7/$8/$9/$10', - ); + $this->collection->get($route, $handler); $router = new Router($this->collection, $this->request); - $router->handle('a/b/c/d/e/f/g/h/i/j'); + $router->handle($uri); $this->assertSame('\Catalog', $router->controllerName()); $this->assertSame('lookup', $router->methodName()); - $this->assertSame(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'], $router->params()); + $this->assertSame($expectedParams, $router->params()); + } + + /** + * @return iterable}> + */ + public static function provideMultipleSegmentsParamTruePlaceholders(): 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 From 31dfc6e00ee17abcb697383a00ad3768cf18cac3 Mon Sep 17 00:00:00 2001 From: banghamid <62824084+banghamid@users.noreply.github.com> Date: Thu, 12 Nov 2026 05:59:16 +0700 Subject: [PATCH 3/3] style(RouterTest): rename data provider to match PHP CS Fixer convention --- tests/system/Router/RouterTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/Router/RouterTest.php b/tests/system/Router/RouterTest.php index e2541cf0962b..bb5aaf1fde95 100644 --- a/tests/system/Router/RouterTest.php +++ b/tests/system/Router/RouterTest.php @@ -1012,7 +1012,7 @@ public function testRoutePlaceholderAnyWithMultipleSegmentsParamTrue(): void /** * @param list $expectedParams */ - #[DataProvider('provideMultipleSegmentsParamTruePlaceholders')] + #[DataProvider('provideMultipleSegmentsParamTrueWithManyPlaceholders')] public function testMultipleSegmentsParamTrueWithManyPlaceholders( string $route, string $handler, @@ -1037,7 +1037,7 @@ public function testMultipleSegmentsParamTrueWithManyPlaceholders( /** * @return iterable}> */ - public static function provideMultipleSegmentsParamTruePlaceholders(): iterable + public static function provideMultipleSegmentsParamTrueWithManyPlaceholders(): iterable { $nineSegments = implode('/', array_fill(0, 9, '(:segment)')); $tenSegments = implode('/', array_fill(0, 10, '(:segment)'));