Skip to content

Commit b7cc9b0

Browse files
committed
feat(app): Allow turning off defined routes and/or auto routing
1 parent 3473349 commit b7cc9b0

File tree

15 files changed

+528
-37
lines changed

15 files changed

+528
-37
lines changed

app/Config/Routing.php

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,27 @@
1818
*/
1919
class Routing extends BaseRouting
2020
{
21+
/**
22+
* If TRUE, the system will attempt to match the URI against
23+
* Controllers by matching each segment against folders/files
24+
* in APPPATH/Controllers, when a match wasn't found against
25+
* defined routes.
26+
*
27+
* If FALSE, will stop searching and do NO automatic routing.
28+
*
29+
* Default: false
30+
*/
31+
public bool $autoRoute = false;
32+
33+
/**
34+
* If TRUE, the system will use route definition files to
35+
* define routes. If FALSE, any routes defined in the Routes.php
36+
* files will be ignored.
37+
*
38+
* Default: true
39+
*/
40+
public bool $definedRoutes = true;
41+
2142
/**
2243
* For Defined Routes.
2344
* An array of files that contain route definitions.
@@ -86,16 +107,6 @@ class Routing extends BaseRouting
86107
*/
87108
public ?string $override404 = null;
88109

89-
/**
90-
* If TRUE, the system will attempt to match the URI against
91-
* Controllers by matching each segment against folders/files
92-
* in APPPATH/Controllers, when a match wasn't found against
93-
* defined routes.
94-
*
95-
* If FALSE, will stop searching and do NO automatic routing.
96-
*/
97-
public bool $autoRoute = false;
98-
99110
/**
100111
* For Defined Routes.
101112
* If TRUE, will enable the use of the 'prioritize' option

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
"phpstan analyse --ansi --generate-baseline=utils/phpstan-baseline/loader.neon",
121121
"split-phpstan-baseline utils/phpstan-baseline/loader.neon"
122122
],
123-
"phpstan:check": "vendor/bin/phpstan analyse --verbose --ansi",
123+
"phpstan:check": "vendor/bin/phpstan analyse --verbose --ansi --memory-limit=512M",
124124
"sa": "@analyze",
125125
"style": "@cs-fix",
126126
"test": "phpunit"

system/CodeIgniter.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,12 @@ protected function tryToRouteIt(?RouteCollectionInterface $routes = null)
785785
$this->benchmark->start('routing');
786786

787787
if (! $routes instanceof RouteCollectionInterface) {
788-
$routes = service('routes')->loadRoutes();
788+
$routes = Services::routes();
789+
790+
// Only load the routes if we are using definedRoutes
791+
if (config('Routing')->definedRoutes) {
792+
$routes->loadRoutes();
793+
}
789794
}
790795

791796
// $routes is defined in Config/Routes.php

system/Config/Routing.php

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,25 @@
1818
*/
1919
class Routing extends BaseConfig
2020
{
21+
/**
22+
* If TRUE, the system will attempt to match the URI against
23+
* Controllers by matching each segment against folders/files
24+
* in APPPATH/Controllers, when a match wasn't found against
25+
* defined routes.
26+
*
27+
* If FALSE, will stop searching and do NO automatic routing.
28+
*/
29+
public bool $autoRoute = false;
30+
31+
/**
32+
* If TRUE, the system will use route definition files to
33+
* define routes. If FALSE, any routes defined in the Routes.php
34+
* files will be ignored.
35+
*
36+
* Default: true
37+
*/
38+
public bool $definedRoutes = true;
39+
2140
/**
2241
* For Defined Routes.
2342
* An array of files that contain route definitions.
@@ -86,16 +105,6 @@ class Routing extends BaseConfig
86105
*/
87106
public ?string $override404 = null;
88107

89-
/**
90-
* If TRUE, the system will attempt to match the URI against
91-
* Controllers by matching each segment against folders/files
92-
* in APPPATH/Controllers, when a match wasn't found against
93-
* defined routes.
94-
*
95-
* If FALSE, will stop searching and do NO automatic routing.
96-
*/
97-
public bool $autoRoute = false;
98-
99108
/**
100109
* For Defined Routes.
101110
* If TRUE, will enable the use of the 'prioritize' option

system/Language/en/Router.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@
1616
'invalidParameter' => 'A parameter does not match the expected type.',
1717
'missingDefaultRoute' => 'Unable to determine what should be displayed. A default route has not been specified in the routing file.',
1818
'invalidDynamicController' => 'A dynamic controller is not allowed for security reasons. Route handler: "{0}"',
19-
'invalidControllerName' => 'The namespace delimiter is a backslash (\), not a slash (/). Route handler: "{0}"',
19+
'invalidControllerName' => 'The namespace delimiter is a backslash (\\), not a slash (/). Route handler: "{0}"',
20+
'noRoutingAvailable' => 'No routing is available. Both auto-routing and defined routes are disabled.',
2021
];

system/Router/Exceptions/RouterException.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,12 @@ public static function forInvalidControllerName(string $handler)
8080
{
8181
return new static(lang('Router.invalidControllerName', [$handler]));
8282
}
83+
84+
/**
85+
* Throw when no routing is available (both autoRoute and definedRoutes are false).
86+
*/
87+
public static function forNoRoutingAvailable(): self
88+
{
89+
return new static(lang('Router.noRoutingAvailable'));
90+
}
8391
}

system/Router/RouteCollection.php

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,15 @@ class RouteCollection implements RouteCollectionInterface
8686
*/
8787
protected $autoRoute = false;
8888

89+
/**
90+
* Whether to use route definition files to define routes.
91+
*
92+
* Not used here. Pass-thru value for Router class.
93+
*
94+
* @var bool
95+
*/
96+
protected $definedRoutes = true;
97+
8998
/**
9099
* A callable that will be shown
91100
* when the route cannot be matched.
@@ -291,13 +300,17 @@ public function __construct(FileLocatorInterface $locator, Modules $moduleConfig
291300
$this->translateURIDashes = $routing->translateURIDashes;
292301
$this->override404 = $routing->override404;
293302
$this->autoRoute = $routing->autoRoute;
303+
$this->definedRoutes = $routing->definedRoutes;
294304
$this->routeFiles = $routing->routeFiles;
295305
$this->prioritize = $routing->prioritize;
296306

297-
// Normalize the path string in routeFiles array.
298-
foreach ($this->routeFiles as $routeKey => $routesFile) {
299-
$realpath = realpath($routesFile);
300-
$this->routeFiles[$routeKey] = ($realpath === false) ? $routesFile : $realpath;
307+
// Only normalize route files if we're actually going to use them
308+
if ($this->definedRoutes) {
309+
// Normalize the path string in routeFiles array.
310+
foreach ($this->routeFiles as $routeKey => $routesFile) {
311+
$realpath = realpath($routesFile);
312+
$this->routeFiles[$routeKey] = ($realpath === false) ? $routesFile : $realpath;
313+
}
301314
}
302315
}
303316

@@ -310,6 +323,13 @@ public function __construct(FileLocatorInterface $locator, Modules $moduleConfig
310323
*/
311324
public function loadRoutes(string $routesFile = APPPATH . 'Config/Routes.php')
312325
{
326+
// Skip loading if defined routes are disabled
327+
if (! $this->definedRoutes) {
328+
$this->didDiscover = true;
329+
330+
return $this;
331+
}
332+
313333
if ($this->didDiscover) {
314334
return $this;
315335
}
@@ -356,6 +376,13 @@ protected function discoverRoutes()
356376
return;
357377
}
358378

379+
// Skip discovery if defined routes are disabled
380+
if (! $this->definedRoutes) {
381+
$this->didDiscover = true;
382+
383+
return;
384+
}
385+
359386
// We need this var in local scope
360387
// so route files can access it.
361388
$routes = $this;
@@ -552,6 +579,14 @@ public function shouldAutoRoute(): bool
552579
return $this->autoRoute;
553580
}
554581

582+
/**
583+
* Returns the flag that tells whether to use defined routes.
584+
*/
585+
public function shouldUseDefinedRoutes(): bool
586+
{
587+
return $this->definedRoutes;
588+
}
589+
555590
/**
556591
* Returns the raw array of available routes.
557592
*
@@ -560,6 +595,11 @@ public function shouldAutoRoute(): bool
560595
*/
561596
public function getRoutes(?string $verb = null, bool $includeWildcard = true): array
562597
{
598+
// Early exit if defined routes are disabled
599+
if (! $this->definedRoutes) {
600+
return [];
601+
}
602+
563603
if ((string) $verb === '') {
564604
$verb = $this->getHTTPVerb();
565605
}

system/Router/RouteCollectionInterface.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,13 @@ public function shouldTranslateURIDashes();
172172
*/
173173
public function shouldAutoRoute();
174174

175+
/**
176+
* Returns the flag that tells whether to use defined routes.
177+
*
178+
* @return bool
179+
*/
180+
public function shouldUseDefinedRoutes();
181+
175182
/**
176183
* Returns the raw array of available routes.
177184
*

system/Router/Router.php

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,29 +158,47 @@ public function __construct(RouteCollectionInterface $routes, ?Request $request
158158
$this->collection->setHTTPVerb($request->getMethod() === '' ? $_SERVER['REQUEST_METHOD'] : $request->getMethod());
159159

160160
$this->translateURIDashes = $this->collection->shouldTranslateURIDashes();
161+
}
161162

162-
if ($this->collection->shouldAutoRoute()) {
163+
/**
164+
* Gets the AutoRouter instance
165+
*/
166+
private function getAutoRouter(): AutoRouterInterface
167+
{
168+
if ($this->autoRouter === null) {
163169
$autoRoutesImproved = config(Feature::class)->autoRoutesImproved ?? false;
164170
if ($autoRoutesImproved) {
165171
assert($this->collection instanceof RouteCollection);
166172

173+
// Only get protected controllers if we're using defined routes
174+
$protectedControllers = $this->collection->shouldUseDefinedRoutes()
175+
? $this->collection->getRegisteredControllers('*')
176+
: [];
177+
167178
$this->autoRouter = new AutoRouterImproved(
168-
$this->collection->getRegisteredControllers('*'),
179+
$protectedControllers,
169180
$this->collection->getDefaultNamespace(),
170181
$this->collection->getDefaultController(),
171182
$this->collection->getDefaultMethod(),
172183
$this->translateURIDashes,
173184
);
174185
} else {
186+
// Only get CLI routes if we're using defined routes
187+
$cliRoutes = $this->collection->shouldUseDefinedRoutes()
188+
? $this->collection->getRoutes('CLI', false)
189+
: [];
190+
175191
$this->autoRouter = new AutoRouter(
176-
$this->collection->getRoutes('CLI', false),
192+
$cliRoutes,
177193
$this->collection->getDefaultNamespace(),
178194
$this->collection->getDefaultController(),
179195
$this->collection->getDefaultMethod(),
180196
$this->translateURIDashes,
181197
);
182198
}
183199
}
200+
201+
return $this->autoRouter;
184202
}
185203

186204
/**
@@ -209,6 +227,37 @@ public function handle(?string $uri = null)
209227
// Restart filterInfo
210228
$this->filtersInfo = [];
211229

230+
$useDefinedRoutes = $this->collection->shouldUseDefinedRoutes();
231+
$useAutoRoute = $this->collection->shouldAutoRoute();
232+
233+
// Let devs know if both are disabled
234+
if (! $useDefinedRoutes && ! $useAutoRoute) {
235+
throw RouterException::forNoRoutingAvailable();
236+
}
237+
238+
// Fast path 1: Auto-routing ONLY (no defined routes to check)
239+
if ($useAutoRoute && ! $useDefinedRoutes) {
240+
$this->autoRoute($uri);
241+
242+
return $this->controllerName();
243+
}
244+
245+
// Fast path 2: Defined routes ONLY (no auto-routing fallback)
246+
if ($useDefinedRoutes && ! $useAutoRoute) {
247+
if ($this->checkRoutes($uri)) {
248+
if ($this->collection->isFiltered($this->matchedRoute[0])) {
249+
$this->filtersInfo = $this->collection->getFiltersForRoute($this->matchedRoute[0]);
250+
}
251+
252+
return $this->controller;
253+
}
254+
255+
throw new PageNotFoundException(
256+
"Can't find a route for '{$this->collection->getHTTPVerb()}: {$uri}'.",
257+
);
258+
}
259+
260+
// Original path: BOTH enabled (check defined routes first, then auto-route)
212261
// Checks defined routes
213262
if ($this->checkRoutes($uri)) {
214263
if ($this->collection->isFiltered($this->matchedRoute[0])) {
@@ -361,8 +410,11 @@ public function setIndexPage($page): self
361410
*/
362411
public function setTranslateURIDashes(bool $val = false): self
363412
{
364-
if ($this->autoRouter instanceof AutoRouter) {
365-
$this->autoRouter->setTranslateURIDashes($val);
413+
// Need to get or create the AutoRouter instance
414+
$autoRouter = $this->collection->shouldAutoRoute() ? $this->getAutoRouter() : null;
415+
416+
if ($autoRouter instanceof AutoRouter) {
417+
$autoRouter->setTranslateURIDashes($val);
366418

367419
return $this;
368420
}
@@ -564,7 +616,7 @@ static function ($match) use ($matches) {
564616
public function autoRoute(string $uri)
565617
{
566618
[$this->directory, $this->controller, $this->method, $this->params]
567-
= $this->autoRouter->getRoute($uri, $this->collection->getHTTPVerb());
619+
= $this->getAutoRouter()->getRoute($uri, $this->collection->getHTTPVerb());
568620
}
569621

570622
/**
@@ -646,8 +698,11 @@ public function setDirectory(?string $dir = null, bool $append = false, bool $va
646698
$this->directory = null;
647699
}
648700

649-
if ($this->autoRouter instanceof AutoRouter) {
650-
$this->autoRouter->setDirectory($dir, $append, $validate);
701+
// Need to get or create the AutoRouter instance
702+
$autoRouter = $this->collection->shouldAutoRoute() ? $this->getAutoRouter() : null;
703+
704+
if ($autoRouter instanceof AutoRouter) {
705+
$autoRouter->setDirectory($dir, $append, $validate);
651706
}
652707
}
653708

0 commit comments

Comments
 (0)