From ec326ad24bef0fd256a83a4a04286cb2f82c6d4d Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Fri, 10 Oct 2025 23:34:46 +0200 Subject: [PATCH 1/6] Fixing tests after routing options PR was merged --- system/Router/Router.php | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/system/Router/Router.php b/system/Router/Router.php index f2b291e58bbb..c37820a9d7d1 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -221,6 +221,44 @@ public function handle(?string $uri = null) // Restart filterInfo $this->filtersInfo = []; +<<<<<<< HEAD +======= + $useDefinedRoutes = $this->collection->shouldUseDefinedRoutes(); + $useAutoRoute = $this->collection->shouldAutoRoute(); + + // Let devs know if both are disabled + if (! $useDefinedRoutes && ! $useAutoRoute) { + throw RouterException::forNoRoutingAvailable(); + } + + // Fast path 1: Auto-routing ONLY (no defined routes to check) + if ($useAutoRoute && ! $useDefinedRoutes) { + $this->autoRoute($uri); + + $this->processRouteAttributes(); + + return $this->controllerName(); + } + + // Fast path 2: Defined routes ONLY (no auto-routing fallback) + if ($useDefinedRoutes && ! $useAutoRoute) { + if ($this->checkRoutes($uri)) { + if ($this->collection->isFiltered($this->matchedRoute[0])) { + $this->filtersInfo = $this->collection->getFiltersForRoute($this->matchedRoute[0]); + } + + $this->processRouteAttributes(); + + return $this->controller; + } + + throw new PageNotFoundException( + "Can't find a route for '{$this->collection->getHTTPVerb()}: {$uri}'.", + ); + } + + // Original path: BOTH enabled (check defined routes first, then auto-route) +>>>>>>> 8f675dc60a (Fixing tests after routing options PR was merged) // Checks defined routes if ($this->checkRoutes($uri)) { if ($this->collection->isFiltered($this->matchedRoute[0])) { From f8df0c62b30e0ea7a265aa3ad81ecba1d5a8f54f Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Tue, 7 Oct 2025 23:36:00 +0200 Subject: [PATCH 2/6] PHPStand baseline --- utils/phpstan-baseline/loader.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/phpstan-baseline/loader.neon b/utils/phpstan-baseline/loader.neon index c1fa33e7414b..2d0fd9f4e2e0 100644 --- a/utils/phpstan-baseline/loader.neon +++ b/utils/phpstan-baseline/loader.neon @@ -1,4 +1,4 @@ -# total 2767 errors +# total 2808 errors includes: - argument.type.neon From fb390c5c3c55ab24de6c372c64eaaa920f5aa40e Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Tue, 7 Oct 2025 23:40:49 +0200 Subject: [PATCH 3/6] Rector improvements --- system/Router/Router.php | 10 +- .../system/Router/RoutingOptimizationTest.php | 217 ++++++++++++++++++ 2 files changed, 223 insertions(+), 4 deletions(-) create mode 100644 tests/system/Router/RoutingOptimizationTest.php diff --git a/system/Router/Router.php b/system/Router/Router.php index c37820a9d7d1..406162148976 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -171,7 +171,12 @@ public function __construct(RouteCollectionInterface $routes, ?Request $request $this->translateURIDashes = $this->collection->shouldTranslateURIDashes(); - if ($this->collection->shouldAutoRoute()) { + /** + * Gets the AutoRouter instance + */ + private function getAutoRouter(): AutoRouterInterface + { + if (!$this->autoRouter instanceof AutoRouterInterface) { $autoRoutesImproved = config(Feature::class)->autoRoutesImproved ?? false; if ($autoRoutesImproved) { assert($this->collection instanceof RouteCollection); @@ -221,8 +226,6 @@ public function handle(?string $uri = null) // Restart filterInfo $this->filtersInfo = []; -<<<<<<< HEAD -======= $useDefinedRoutes = $this->collection->shouldUseDefinedRoutes(); $useAutoRoute = $this->collection->shouldAutoRoute(); @@ -258,7 +261,6 @@ public function handle(?string $uri = null) } // Original path: BOTH enabled (check defined routes first, then auto-route) ->>>>>>> 8f675dc60a (Fixing tests after routing options PR was merged) // Checks defined routes if ($this->checkRoutes($uri)) { if ($this->collection->isFiltered($this->matchedRoute[0])) { diff --git a/tests/system/Router/RoutingOptimizationTest.php b/tests/system/Router/RoutingOptimizationTest.php new file mode 100644 index 000000000000..ad261aa9c13c --- /dev/null +++ b/tests/system/Router/RoutingOptimizationTest.php @@ -0,0 +1,217 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Router; + +use CodeIgniter\Exceptions\PageNotFoundException; +use CodeIgniter\HTTP\IncomingRequest; +use CodeIgniter\HTTP\Method; +use CodeIgniter\Router\Controllers\Mycontroller; +use CodeIgniter\Test\CIUnitTestCase; +use Config\Feature; +use Config\Modules; +use Config\Routing; +use PHPUnit\Framework\Attributes\Group; + +/** + * Integration tests for routing optimization features. + * Tests the complete flow from config to execution with different routing modes. + * + * @internal + */ +#[Group('Others')] +final class RoutingOptimizationTest extends CIUnitTestCase +{ + private IncomingRequest $request; + + protected function setUp(): void + { + parent::setUp(); + + $featureConfig = config(Feature::class); + $featureConfig->autoRoutesImproved = true; + + $this->request = service('request'); + $this->request->setMethod(Method::GET); + } + + private function createRouteCollection(Routing $routingConfig): RouteCollection + { + $moduleConfig = new Modules(); + $moduleConfig->enabled = false; + + $routingConfig->defaultNamespace = 'CodeIgniter\\Router\\Controllers'; + + $collection = new RouteCollection(service('locator'), $moduleConfig, $routingConfig); + $collection->setHTTPVerb(Method::GET); + + return $collection; + } + + /** + * Test auto-routing only mode (definedRoutes = false) + * This should skip all route file loading and discovery + */ + public function testAutoRoutingOnlyMode(): void + { + $routingConfig = new Routing(); + $routingConfig->autoRoute = true; + $routingConfig->definedRoutes = false; + + $collection = $this->createRouteCollection($routingConfig); + + // Add a defined route (should be ignored) + $collection->get('ignored', 'Ignored::method'); + + // Verify no routes are returned + $this->assertSame([], $collection->getRoutes()); + + // Create router and test auto-routing + $router = new Router($collection, $this->request); + $router->handle('mycontroller'); + + $this->assertSame('\\' . Mycontroller::class, $router->controllerName()); + $this->assertSame('getIndex', $router->methodName()); + } + + /** + * Test defined routes only mode (autoRoute = false) + * This should skip AutoRouter instantiation entirely + */ + public function testDefinedRoutesOnlyMode(): void + { + $routingConfig = new Routing(); + $routingConfig->autoRoute = false; + $routingConfig->definedRoutes = true; + + $collection = $this->createRouteCollection($routingConfig); + $collection->get('products', 'Products::list'); + + // Verify route is available + $routes = $collection->getRoutes(); + $this->assertArrayHasKey('products', $routes); + + // Create router and test defined routing + $router = new Router($collection, $this->request); + $router->handle('products'); + + $this->assertSame('\CodeIgniter\Router\Controllers\Products', $router->controllerName()); + $this->assertSame('list', $router->methodName()); + } + + /** + * Test that defined routes only mode throws when no route matches + * (no fallback to auto-routing) + */ + public function testDefinedRoutesOnlyModeThrowsOnNoMatch(): void + { + $this->expectException(PageNotFoundException::class); + $this->expectExceptionMessage("Can't find a route for 'GET: nonexistent'"); + + $routingConfig = new Routing(); + $routingConfig->autoRoute = false; + $routingConfig->definedRoutes = true; + + $collection = $this->createRouteCollection($routingConfig); + $router = new Router($collection, $this->request); + + // Should throw immediately without trying auto-routing + $router->handle('nonexistent'); + } + + /** + * Test both modes enabled (traditional behavior) + * Should check defined routes first, then fall back to auto-routing + */ + public function testBothModesEnabled(): void + { + $routingConfig = new Routing(); + $routingConfig->autoRoute = true; + $routingConfig->definedRoutes = true; + + $collection = $this->createRouteCollection($routingConfig); + $collection->get('users', 'Users::index'); + + $router = new Router($collection, $this->request); + + // Test defined route takes precedence + $router->handle('users'); + $this->assertSame('\CodeIgniter\Router\Controllers\Users', $router->controllerName()); + $this->assertSame('index', $router->methodName()); + + // Test fallback to auto-routing + $router->handle('mycontroller'); + $this->assertSame(Mycontroller::class, $router->controllerName()); + $this->assertSame('getIndex', $router->methodName()); + } + + /** + * Test that route file loading is skipped when definedRoutes = false + */ + public function testRouteFileLoadingSkipped(): void + { + $routingConfig = new Routing(); + $routingConfig->autoRoute = true; + $routingConfig->definedRoutes = false; + $routingConfig->routeFiles = [APPPATH . 'Config/Routes.php']; + + $collection = $this->createRouteCollection($routingConfig); + + // Call loadRoutes - should return early + $collection->loadRoutes(); + + // Verify routes were not loaded + $this->assertSame([], $collection->getRoutes()); + } + + /** + * Test that route discovery is skipped when definedRoutes = false + */ + public function testRouteDiscoverySkipped(): void + { + $routingConfig = new Routing(); + $routingConfig->autoRoute = true; + $routingConfig->definedRoutes = false; + + $moduleConfig = new Modules(); + $moduleConfig->enabled = true; // Enable discovery + + $collection = new RouteCollection(service('locator'), $moduleConfig, $routingConfig); + + // Verify discovery doesn't happen and routes remain empty + $this->assertSame([], $collection->getRoutes()); + } + + /** + * Test configuration flags are properly stored + */ + public function testConfigurationFlags(): void + { + // Test defaults + $defaultConfig = new Routing(); + $collection = $this->createRouteCollection($defaultConfig); + + $this->assertFalse($collection->shouldAutoRoute()); + $this->assertTrue($collection->shouldUseDefinedRoutes()); + + // Test custom values + $customConfig = new Routing(); + $customConfig->autoRoute = true; + $customConfig->definedRoutes = false; + + $collection = $this->createRouteCollection($customConfig); + + $this->assertTrue($collection->shouldAutoRoute()); + $this->assertFalse($collection->shouldUseDefinedRoutes()); + } +} From 7e209dd33305607dd817dec1af9d01c73b546429 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Tue, 7 Oct 2025 23:44:26 +0200 Subject: [PATCH 4/6] CS fixes --- system/Router/Router.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Router/Router.php b/system/Router/Router.php index 406162148976..3b5bc9a53c31 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -176,7 +176,7 @@ public function __construct(RouteCollectionInterface $routes, ?Request $request */ private function getAutoRouter(): AutoRouterInterface { - if (!$this->autoRouter instanceof AutoRouterInterface) { + if (! $this->autoRouter instanceof AutoRouterInterface) { $autoRoutesImproved = config(Feature::class)->autoRoutesImproved ?? false; if ($autoRoutesImproved) { assert($this->collection instanceof RouteCollection); From 269049a9c77f7bd0fca5d75cb69e01a87c01a2b8 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Tue, 7 Oct 2025 23:54:10 +0200 Subject: [PATCH 5/6] Fix test --- tests/system/Router/RoutingOptimizationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/Router/RoutingOptimizationTest.php b/tests/system/Router/RoutingOptimizationTest.php index ad261aa9c13c..b68713c03f4f 100644 --- a/tests/system/Router/RoutingOptimizationTest.php +++ b/tests/system/Router/RoutingOptimizationTest.php @@ -151,7 +151,7 @@ public function testBothModesEnabled(): void // Test fallback to auto-routing $router->handle('mycontroller'); - $this->assertSame(Mycontroller::class, $router->controllerName()); + $this->assertSame('\\' . Mycontroller::class, $router->controllerName()); $this->assertSame('getIndex', $router->methodName()); } From cbbe65614dbab1b76e8784859989c30ebc78cf75 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Wed, 8 Oct 2025 21:58:11 +0200 Subject: [PATCH 6/6] review comment fix --- user_guide_src/source/changelogs/v4.7.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/source/changelogs/v4.7.0.rst b/user_guide_src/source/changelogs/v4.7.0.rst index d91e9e2fd604..e3571c024e22 100644 --- a/user_guide_src/source/changelogs/v4.7.0.rst +++ b/user_guide_src/source/changelogs/v4.7.0.rst @@ -81,6 +81,7 @@ Libraries - **Email:** Added support for choosing the SMTP authorization method. You can change it via ``Config\Email::$SMTPAuthMethod`` option. - **Image:** The ``ImageMagickHandler`` has been rewritten to rely solely on the PHP ``imagick`` extension. - **Image:** Added ``ImageMagickHandler::clearMetadata()`` method to remove image metadata for privacy protection. +- **Routing:** Added support for choosing to run only defined routes, only auto-routing, or both. You can change it via ``Config\Routing::$definedRoutes`` and ``Config\Routing::$autoRoute`` options. - **Time:** added methods ``Time::addCalendarMonths()`` and ``Time::subCalendarMonths()`` Commands