diff --git a/system/Router/AutoRouterImproved.php b/system/Router/AutoRouterImproved.php index 0db5ebc1ddc2..63cdd7702635 100644 --- a/system/Router/AutoRouterImproved.php +++ b/system/Router/AutoRouterImproved.php @@ -162,7 +162,7 @@ private function searchFirstController(): bool $segment = array_shift($segments); $controllerPos++; - $class = $this->translateURIDashes($segment); + $class = $this->translateURI($segment); // as soon as we encounter any segment that is not PSR-4 compliant, stop searching if (! $this->isValidSegment($class)) { @@ -209,7 +209,7 @@ private function searchLastDefaultController(): bool } $namespaces = array_map( - fn ($segment) => $this->translateURIDashes($segment), + fn ($segment) => $this->translateURI($segment), $segments ); @@ -307,7 +307,7 @@ public function getRoute(string $uri, string $httpVerb): array $method = ''; if ($methodParam !== null) { - $method = $httpVerb . $this->translateURIDashes($methodParam); + $method = $httpVerb . $this->translateURI($methodParam); $this->checkUriForMethod($method); } @@ -519,7 +519,17 @@ private function checkUriForMethod(string $method): void return; } - if (! in_array($method, get_class_methods($this->controller), true)) { + if ( + // For example, if `getSomeMethod()` exists in the controller, only + // the URI `controller/some-method` should be accessible. But if a + // visitor navigates to the URI `controller/somemethod`, `getSomemethod()` + // will be checked, and `method_exists()` will return true because + // method names in PHP are case-insensitive. + method_exists($this->controller, $method) + // But we do not permit `controller/somemethod`, so check the exact + // method name. + && ! in_array($method, get_class_methods($this->controller), true) + ) { throw new PageNotFoundException( '"' . $this->controller . '::' . $method . '()" is not found.' ); @@ -536,7 +546,10 @@ private function isValidSegment(string $segment): bool return (bool) preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $segment); } - private function translateURIDashes(string $segment): string + /** + * Translates URI segment to CamelCase or replaces `-` with `_`. + */ + private function translateURI(string $segment): string { if ($this->translateUriToCamelCase) { if (strtolower($segment) !== $segment) { diff --git a/tests/system/Router/AutoRouterImprovedTest.php b/tests/system/Router/AutoRouterImprovedTest.php index c60c978e4a3c..5975280bcdfd 100644 --- a/tests/system/Router/AutoRouterImprovedTest.php +++ b/tests/system/Router/AutoRouterImprovedTest.php @@ -277,6 +277,28 @@ public function testAutoRouteFallbackToDefaultMethod(): void ], $router->getPos()); } + public function testAutoRouteFallbackToDefaultMethodWithTranslateUriToCamelCase(): void + { + $config = config(Routing::class); + $config->translateUriToCamelCase = true; + Factories::injectMock('config', Routing::class, $config); + + $router = $this->createNewAutoRouter(); + + [$directory, $controller, $method, $params] + = $router->getRoute('index/15', Method::GET); + + $this->assertNull($directory); + $this->assertSame('\\' . Index::class, $controller); + $this->assertSame('getIndex', $method); + $this->assertSame(['15'], $params); + $this->assertSame([ + 'controller' => 0, + 'method' => null, + 'params' => 1, + ], $router->getPos()); + } + public function testAutoRouteFallbackToDefaultControllerOneParam(): void { $router = $this->createNewAutoRouter();