diff --git a/system/Router/AutoRouter.php b/system/Router/AutoRouter.php index f7cd02a3d329..c35e375cfa2f 100644 --- a/system/Router/AutoRouter.php +++ b/system/Router/AutoRouter.php @@ -157,10 +157,13 @@ public function getRoute(string $uri, string $httpVerb): array // Load the file so that it's available for CodeIgniter. $file = APPPATH . 'Controllers/' . $this->directory . $controllerName . '.php'; - if (is_file($file)) { - include_once $file; + + if (! is_file($file)) { + throw PageNotFoundException::forControllerNotFound($this->controller, $this->method); } + include_once $file; + // Ensure the controller stores the fully-qualified class name // We have to check for a length over 1, since by default it will be '\' if (strpos($this->controller, '\\') === false && strlen($this->defaultNamespace) > 1) { diff --git a/tests/_support/_controller/Admin_user.php b/tests/_support/_controller/Admin_user.php new file mode 100644 index 000000000000..62e154410955 --- /dev/null +++ b/tests/_support/_controller/Admin_user.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace App\Controllers; + +use CodeIgniter\Controller; + +class Admin_user extends Controller +{ + public function show_list(): void + { + } +} diff --git a/tests/_support/_controller/Dash_folder/Dash_controller.php b/tests/_support/_controller/Dash_folder/Dash_controller.php new file mode 100644 index 000000000000..c474aff32e6e --- /dev/null +++ b/tests/_support/_controller/Dash_folder/Dash_controller.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace App\Controllers\Dash_folder; + +use CodeIgniter\Controller; + +class Dash_controller extends Controller +{ + public function getSomemethod(): void + { + } + + public function getDash_method(): void + { + } +} diff --git a/tests/_support/_controller/Dash_folder/Home.php b/tests/_support/_controller/Dash_folder/Home.php new file mode 100644 index 000000000000..20b28e109721 --- /dev/null +++ b/tests/_support/_controller/Dash_folder/Home.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace App\Controllers\Dash_folder; + +use CodeIgniter\Controller; + +class Home extends Controller +{ + public function getIndex(): void + { + } +} diff --git a/tests/_support/_controller/Dash_folder/Mycontroller.php b/tests/_support/_controller/Dash_folder/Mycontroller.php new file mode 100644 index 000000000000..2aef903bc891 --- /dev/null +++ b/tests/_support/_controller/Dash_folder/Mycontroller.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace App\Controllers\Dash_folder; + +use CodeIgniter\Controller; + +class Mycontroller extends Controller +{ + public function getSomemethod(): void + { + } +} diff --git a/tests/_support/_controller/Mycontroller.php b/tests/_support/_controller/Mycontroller.php new file mode 100644 index 000000000000..d107198c78d4 --- /dev/null +++ b/tests/_support/_controller/Mycontroller.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace App\Controllers; + +use CodeIgniter\Controller; + +class Mycontroller extends Controller +{ + public function getIndex(): void + { + } + + public function getSomemethod($first = ''): void + { + } +} diff --git a/tests/_support/_controller/Product.php b/tests/_support/_controller/Product.php new file mode 100644 index 000000000000..88951b8d90c1 --- /dev/null +++ b/tests/_support/_controller/Product.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace App\Controllers; + +use CodeIgniter\Controller; + +class Product extends Controller +{ + public function index(): void + { + } +} diff --git a/tests/_support/_controller/Subfolder/Mycontroller.php b/tests/_support/_controller/Subfolder/Mycontroller.php new file mode 100644 index 000000000000..1da9d80843b3 --- /dev/null +++ b/tests/_support/_controller/Subfolder/Mycontroller.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace App\Controllers\Subfolder; + +use CodeIgniter\Controller; + +class Mycontroller extends Controller +{ + public function getSomemethod(): void + { + } +} diff --git a/tests/_support/_controller/foo/bar/baz/Some_controller.php b/tests/_support/_controller/foo/bar/baz/Some_controller.php new file mode 100644 index 000000000000..25fe509c56cc --- /dev/null +++ b/tests/_support/_controller/foo/bar/baz/Some_controller.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace App\Controllers\foo\bar\baz; + +use CodeIgniter\Controller; + +class Some_controller extends Controller +{ + public function some_method($first = ''): void + { + } +} diff --git "a/tests/_support/_controller/\316\246.php" "b/tests/_support/_controller/\316\246.php" new file mode 100644 index 000000000000..a48ba76f77cd --- /dev/null +++ "b/tests/_support/_controller/\316\246.php" @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace App\Controllers; + +use CodeIgniter\Controller; + +class Φ extends Controller +{ + public function getIndex(): void + { + } +} diff --git "a/tests/_support/_controller/\316\246/Home.php" "b/tests/_support/_controller/\316\246/Home.php" new file mode 100644 index 000000000000..3ee7e7aea271 --- /dev/null +++ "b/tests/_support/_controller/\316\246/Home.php" @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace App\Controllers\Φ; + +use CodeIgniter\Controller; + +class Home extends Controller +{ + public function getIndex(): void + { + } +} diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index b7b5e092e609..505d1f5e5a7b 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -13,6 +13,7 @@ use CodeIgniter\Config\Services; use CodeIgniter\Exceptions\ConfigException; +use CodeIgniter\Exceptions\PageNotFoundException; use CodeIgniter\HTTP\Response; use CodeIgniter\Router\Exceptions\RedirectException; use CodeIgniter\Router\RouteCollection; @@ -25,6 +26,7 @@ use Config\Modules; use Config\Routing; use Tests\Support\Filters\Customfilter; +use Tests\Support\Filters\RedirectFilter; /** * @runTestsInSeparateProcesses @@ -926,4 +928,30 @@ public static function providePageCacheWithCacheQueryString(): iterable '$cacheQueryString=array' => [['important_parameter'], 3, $testingUrls], ]; } + + /** + * See https://github.com/codeigniter4/CodeIgniter4/issues/7205 + */ + public function testRunControllerNotFoundBeforeFilter(): void + { + $_SERVER['argv'] = ['index.php']; + $_SERVER['argc'] = 1; + + $_SERVER['REQUEST_URI'] = '/cannotFound'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + + // Inject mock router. + $routes = Services::routes(); + $routes->setAutoRoute(true); + + // Inject the before filter. + $filterConfig = config('Filters'); + $filterConfig->aliases['redirectFilter'] = RedirectFilter::class; + $filterConfig->globals['before'] = ['redirectFilter']; + Services::filters($filterConfig); + + $this->expectException(PageNotFoundException::class); + + $this->codeigniter->run($routes); + } } diff --git a/tests/system/Router/RouteCollectionTest.php b/tests/system/Router/RouteCollectionTest.php index d6b7e0dfe6ac..985326dcf66d 100644 --- a/tests/system/Router/RouteCollectionTest.php +++ b/tests/system/Router/RouteCollectionTest.php @@ -11,6 +11,7 @@ namespace CodeIgniter\Router; +use App\Controllers\Product; use CodeIgniter\Config\Services; use CodeIgniter\controller; use CodeIgniter\Exceptions\PageNotFoundException; @@ -1778,10 +1779,14 @@ public function testAutoRoutesControllerNameReturnsFQCN($namespace): void $routes->setAutoRoute(true); $routes->setDefaultNamespace($namespace); + copy(TESTPATH . '_support/_controller/Product.php', APPPATH . 'Controllers/Product.php'); + $router = new Router($routes, Services::request()); $router->handle('/product'); - $this->assertSame('\App\\Controllers\\Product', $router->controllerName()); + unlink(APPPATH . 'Controllers/Product.php'); + + $this->assertSame('\\' . Product::class, $router->controllerName()); } /** @@ -1797,10 +1802,14 @@ public function testRoutesControllerNameReturnsFQCN($namespace): void $routes->setDefaultNamespace($namespace); $routes->get('/product', 'Product'); + copy(TESTPATH . '_support/_controller/Product.php', APPPATH . 'Controllers/Product.php'); + $router = new Router($routes, Services::request()); $router->handle('/product'); - $this->assertSame('\App\\Controllers\\Product', $router->controllerName()); + unlink(APPPATH . 'Controllers/Product.php'); + + $this->assertSame('\\' . Product::class, $router->controllerName()); } public function testRoutePriorityDetected(): void diff --git a/tests/system/Router/RouterTest.php b/tests/system/Router/RouterTest.php index d0c9b5a664ab..60dd669172e5 100644 --- a/tests/system/Router/RouterTest.php +++ b/tests/system/Router/RouterTest.php @@ -197,14 +197,18 @@ public function testClosures(): void public function testAutoRouteFindsDefaultControllerAndMethod(): void { $this->collection->setAutoRoute(true); - $this->collection->setDefaultController('Test'); - $this->collection->setDefaultMethod('test'); + $this->collection->setDefaultController('Mycontroller'); + $this->collection->setDefaultMethod('getSomemethod'); $router = new Router($this->collection, $this->request); + copy(TESTPATH . '_support/_controller/Mycontroller.php', APPPATH . 'Controllers/Mycontroller.php'); + $router->autoRoute('/'); - $this->assertSame('Test', $router->controllerName()); - $this->assertSame('test', $router->methodName()); + unlink(APPPATH . 'Controllers/Mycontroller.php'); + + $this->assertSame('Mycontroller', $router->controllerName()); + $this->assertSame('getSomemethod', $router->methodName()); } public function testAutoRouteFindsControllerWithFileAndMethod(): void @@ -212,10 +216,14 @@ public function testAutoRouteFindsControllerWithFileAndMethod(): void $this->collection->setAutoRoute(true); $router = new Router($this->collection, $this->request); - $router->autoRoute('myController/someMethod'); + copy(TESTPATH . '_support/_controller/Mycontroller.php', APPPATH . 'Controllers/Mycontroller.php'); + + $router->autoRoute('mycontroller/getSomemethod'); + + unlink(APPPATH . 'Controllers/Mycontroller.php'); - $this->assertSame('MyController', $router->controllerName()); - $this->assertSame('someMethod', $router->methodName()); + $this->assertSame('Mycontroller', $router->controllerName()); + $this->assertSame('getSomemethod', $router->methodName()); } public function testAutoRouteFindsControllerWithFile(): void @@ -223,9 +231,13 @@ public function testAutoRouteFindsControllerWithFile(): void $this->collection->setAutoRoute(true); $router = new Router($this->collection, $this->request); - $router->autoRoute('myController'); + copy(TESTPATH . '_support/_controller/Mycontroller.php', APPPATH . 'Controllers/Mycontroller.php'); + + $router->autoRoute('mycontroller'); + + unlink(APPPATH . 'Controllers/Mycontroller.php'); - $this->assertSame('MyController', $router->controllerName()); + $this->assertSame('Mycontroller', $router->controllerName()); $this->assertSame('index', $router->methodName()); } @@ -236,12 +248,16 @@ public function testAutoRouteFindsControllerWithSubfolder(): void mkdir(APPPATH . 'Controllers/Subfolder'); - $router->autoRoute('subfolder/myController/someMethod'); + copy(TESTPATH . '_support/_controller/Subfolder/Mycontroller.php', APPPATH . 'Controllers/Subfolder/Mycontroller.php'); + + $router->autoRoute('subfolder/mycontroller/getSomemethod'); + + unlink(APPPATH . 'Controllers/Subfolder/Mycontroller.php'); rmdir(APPPATH . 'Controllers/Subfolder'); - $this->assertSame('MyController', $router->controllerName()); - $this->assertSame('someMethod', $router->methodName()); + $this->assertSame('Mycontroller', $router->controllerName()); + $this->assertSame('getSomemethod', $router->methodName()); } public function testAutoRouteFindsDashedSubfolder(): void @@ -251,9 +267,11 @@ public function testAutoRouteFindsDashedSubfolder(): void $router->setTranslateURIDashes(true); mkdir(APPPATH . 'Controllers/Dash_folder'); + copy(TESTPATH . '_support/_controller/Dash_folder/Mycontroller.php', APPPATH . 'Controllers/Dash_folder/Mycontroller.php'); $router->autoRoute('dash-folder/mycontroller/somemethod'); + unlink(APPPATH . 'Controllers/Dash_folder/Mycontroller.php'); rmdir(APPPATH . 'Controllers/Dash_folder'); $this->assertSame('Dash_folder/', $router->directory()); @@ -268,16 +286,16 @@ public function testAutoRouteFindsDashedController(): void $router->setTranslateURIDashes(true); mkdir(APPPATH . 'Controllers/Dash_folder'); - file_put_contents(APPPATH . 'Controllers/Dash_folder/Dash_controller.php', ''); + copy(TESTPATH . '_support/_controller/Dash_folder/Dash_controller.php', APPPATH . 'Controllers/Dash_folder/Dash_controller.php'); - $router->autoRoute('dash-folder/dash-controller/somemethod'); + $router->autoRoute('dash-folder/dash-controller/getSomemethod'); unlink(APPPATH . 'Controllers/Dash_folder/Dash_controller.php'); rmdir(APPPATH . 'Controllers/Dash_folder'); $this->assertSame('Dash_folder/', $router->directory()); $this->assertSame('Dash_controller', $router->controllerName()); - $this->assertSame('somemethod', $router->methodName()); + $this->assertSame('getSomemethod', $router->methodName()); } public function testAutoRouteFindsDashedMethod(): void @@ -287,16 +305,16 @@ public function testAutoRouteFindsDashedMethod(): void $router->setTranslateURIDashes(true); mkdir(APPPATH . 'Controllers/Dash_folder'); - file_put_contents(APPPATH . 'Controllers/Dash_folder/Dash_controller.php', ''); + copy(TESTPATH . '_support/_controller/Dash_folder/Dash_controller.php', APPPATH . 'Controllers/Dash_folder/Dash_controller.php'); - $router->autoRoute('dash-folder/dash-controller/dash-method'); + $router->autoRoute('dash-folder/dash-controller/getDash_method'); unlink(APPPATH . 'Controllers/Dash_folder/Dash_controller.php'); rmdir(APPPATH . 'Controllers/Dash_folder'); $this->assertSame('Dash_folder/', $router->directory()); $this->assertSame('Dash_controller', $router->controllerName()); - $this->assertSame('dash_method', $router->methodName()); + $this->assertSame('getDash_method', $router->methodName()); } public function testAutoRouteFindsDefaultDashFolder(): void @@ -306,9 +324,11 @@ public function testAutoRouteFindsDefaultDashFolder(): void $router->setTranslateURIDashes(true); mkdir(APPPATH . 'Controllers/Dash_folder'); + copy(TESTPATH . '_support/_controller/Dash_folder/Home.php', APPPATH . 'Controllers/Dash_folder/Home.php'); $router->autoRoute('dash-folder'); + unlink(APPPATH . 'Controllers/Dash_folder/Home.php'); rmdir(APPPATH . 'Controllers/Dash_folder'); $this->assertSame('Dash_folder/', $router->directory()); @@ -323,9 +343,11 @@ public function testAutoRouteFindsMByteDir(): void $router->setTranslateURIDashes(true); mkdir(APPPATH . 'Controllers/Φ'); + copy(TESTPATH . '_support/_controller/Φ/Home.php', APPPATH . 'Controllers/Φ/Home.php'); $router->autoRoute('Φ'); + unlink(APPPATH . 'Controllers/Φ/Home.php'); rmdir(APPPATH . 'Controllers/Φ'); $this->assertSame('Φ/', $router->directory()); @@ -339,11 +361,11 @@ public function testAutoRouteFindsMByteController(): void $router = new Router($this->collection, $this->request); $router->setTranslateURIDashes(true); - file_put_contents(APPPATH . 'Controllers/Φ', ''); + copy(TESTPATH . '_support/_controller/Φ.php', APPPATH . 'Controllers/Φ.php'); $router->autoRoute('Φ'); - unlink(APPPATH . 'Controllers/Φ'); + unlink(APPPATH . 'Controllers/Φ.php'); $this->assertSame('Φ', $router->controllerName()); $this->assertSame('index', $router->methodName()); @@ -732,7 +754,11 @@ public function testTranslateURIDashesForAutoRoute(): void $router = new Router($this->collection, $this->request); $router->setTranslateURIDashes(true); - $router->autoRoute('admin-user/show-list'); + copy(TESTPATH . '_support/_controller/Admin_user.php', APPPATH . 'Controllers/Admin_user.php'); + + $router->autoRoute('admin_user/show_list'); + + unlink(APPPATH . 'Controllers/Admin_user.php'); $this->assertSame('Admin_user', $router->controllerName()); $this->assertSame('show_list', $router->methodName()); @@ -746,10 +772,14 @@ public function testAutoRouteMatchesZeroParams(): void $this->collection->setAutoRoute(true); $router = new Router($this->collection, $this->request); - $router->autoRoute('myController/someMethod/0/abc'); + copy(TESTPATH . '_support/_controller/Mycontroller.php', APPPATH . 'Controllers/Mycontroller.php'); - $this->assertSame('MyController', $router->controllerName()); - $this->assertSame('someMethod', $router->methodName()); + $router->autoRoute('mycontroller/getSomemethod/0/abc'); + + $this->assertSame('Mycontroller', $router->controllerName()); + $this->assertSame('getSomemethod', $router->methodName()); + + unlink(APPPATH . 'Controllers/Mycontroller.php'); $expected = [ '0', @@ -817,9 +847,19 @@ public function testRouterPriorDirectory(): void $this->collection->setAutoRoute(true); $router = new Router($this->collection, $this->request); + mkdir(APPPATH . 'Controllers/foo'); + mkdir(APPPATH . 'Controllers/foo/bar'); + mkdir(APPPATH . 'Controllers/foo/bar/baz'); + copy(TESTPATH . '_support/_controller/foo/bar/baz/Some_controller.php', APPPATH . 'Controllers/foo/bar/baz/Some_controller.php'); + $router->setDirectory('foo/bar/baz', false, true); $router->handle('Some_controller/some_method/param1/param2/param3'); + unlink(APPPATH . 'Controllers/foo/bar/baz/Some_controller.php'); + rmdir(APPPATH . 'Controllers/foo/bar/baz'); + rmdir(APPPATH . 'Controllers/foo/bar'); + rmdir(APPPATH . 'Controllers/foo'); + $this->assertSame('foo/bar/baz/', $router->directory()); $this->assertSame('Some_controller', $router->controllerName()); $this->assertSame('some_method', $router->methodName()); diff --git a/user_guide_src/source/changelogs/v4.5.0.rst b/user_guide_src/source/changelogs/v4.5.0.rst index 2b6fc7299231..5100ded28a89 100644 --- a/user_guide_src/source/changelogs/v4.5.0.rst +++ b/user_guide_src/source/changelogs/v4.5.0.rst @@ -43,6 +43,8 @@ class, and even if it were extended, there is no way to replace it. Others ------ +- **AutoRouting Legacy:** Changed so that a ``PageNotFoundException`` is thrown + if the controller corresponding to the request URI does not exist. - **Logger:** The :php:func:`log_message()` function and the logger methods in ``CodeIgniter\Log\Logger`` now do not return ``bool`` values. The return types have been fixed to ``void`` to follow the PSR-3 interface. diff --git a/user_guide_src/source/incoming/controllers.rst b/user_guide_src/source/incoming/controllers.rst index 8bc9e90d581f..f53609023c52 100644 --- a/user_guide_src/source/incoming/controllers.rst +++ b/user_guide_src/source/incoming/controllers.rst @@ -412,6 +412,10 @@ without route definitions. The auto-routing is disabled by default. .. important:: Auto Routing (Legacy) routes a HTTP request with **any** HTTP method to a controller method. +.. important:: Since v4.5.0, if Auto Routing (Legacy) doesn't find the controller, + it will throw ``PageNotFoundException`` exception before the Controller Filters + execute. + Consider this URI:: example.com/index.php/helloworld/ diff --git a/user_guide_src/source/installation/upgrade_450.rst b/user_guide_src/source/installation/upgrade_450.rst index 56b1f37c011e..04607dde6489 100644 --- a/user_guide_src/source/installation/upgrade_450.rst +++ b/user_guide_src/source/installation/upgrade_450.rst @@ -126,6 +126,18 @@ Factories In the unlikely event, you have inherited the Factories, stop inheriting and copy the code into your Factories class. +Auto Routing (Legacy) +===================== + +In previous versions, the controller filters might be executed even when the +corresponding controller was not found. + +This bug has been fixed and now a ``PageNotFoundException`` will be thrown and +the filters will not be executed if the controller is not found. + +If you have code that depends on this bug, for example if you expect global filters +to be executed even for non-existent pages, please add the necessary routes. + Removed Deprecated Items ========================