Skip to content

Commit 1282721

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

14 files changed

Lines changed: 527 additions & 36 deletions

File tree

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
* If TRUE, the system will look for attributes on controller
101112
* class and methods that can run before and after the

system/CodeIgniter.php

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

799799
if (! $routes instanceof RouteCollectionInterface) {
800-
$routes = service('routes')->loadRoutes();
800+
$routes = Services::routes();
801+
802+
// Only load the routes if we are using definedRoutes
803+
if (config('Routing')->definedRoutes) {
804+
$routes->loadRoutes();
805+
}
801806
}
802807

803808
// $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
* If TRUE, the system will look for attributes on controller
101110
* class and methods that can run before and after the

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
@@ -170,29 +170,47 @@ public function __construct(RouteCollectionInterface $routes, ?Request $request
170170
$this->collection->setHTTPVerb($request->getMethod() === '' ? $_SERVER['REQUEST_METHOD'] : $request->getMethod());
171171

172172
$this->translateURIDashes = $this->collection->shouldTranslateURIDashes();
173+
}
173174

174-
if ($this->collection->shouldAutoRoute()) {
175+
/**
176+
* Gets the AutoRouter instance
177+
*/
178+
private function getAutoRouter(): AutoRouterInterface
179+
{
180+
if ($this->autoRouter === null) {
175181
$autoRoutesImproved = config(Feature::class)->autoRoutesImproved ?? false;
176182
if ($autoRoutesImproved) {
177183
assert($this->collection instanceof RouteCollection);
178184

185+
// Only get protected controllers if we're using defined routes
186+
$protectedControllers = $this->collection->shouldUseDefinedRoutes()
187+
? $this->collection->getRegisteredControllers('*')
188+
: [];
189+
179190
$this->autoRouter = new AutoRouterImproved(
180-
$this->collection->getRegisteredControllers('*'),
191+
$protectedControllers,
181192
$this->collection->getDefaultNamespace(),
182193
$this->collection->getDefaultController(),
183194
$this->collection->getDefaultMethod(),
184195
$this->translateURIDashes,
185196
);
186197
} else {
198+
// Only get CLI routes if we're using defined routes
199+
$cliRoutes = $this->collection->shouldUseDefinedRoutes()
200+
? $this->collection->getRoutes('CLI', false)
201+
: [];
202+
187203
$this->autoRouter = new AutoRouter(
188-
$this->collection->getRoutes('CLI', false),
204+
$cliRoutes,
189205
$this->collection->getDefaultNamespace(),
190206
$this->collection->getDefaultController(),
191207
$this->collection->getDefaultMethod(),
192208
$this->translateURIDashes,
193209
);
194210
}
195211
}
212+
213+
return $this->autoRouter;
196214
}
197215

198216
/**
@@ -221,6 +239,37 @@ public function handle(?string $uri = null)
221239
// Restart filterInfo
222240
$this->filtersInfo = [];
223241

242+
$useDefinedRoutes = $this->collection->shouldUseDefinedRoutes();
243+
$useAutoRoute = $this->collection->shouldAutoRoute();
244+
245+
// Let devs know if both are disabled
246+
if (! $useDefinedRoutes && ! $useAutoRoute) {
247+
throw RouterException::forNoRoutingAvailable();
248+
}
249+
250+
// Fast path 1: Auto-routing ONLY (no defined routes to check)
251+
if ($useAutoRoute && ! $useDefinedRoutes) {
252+
$this->autoRoute($uri);
253+
254+
return $this->controllerName();
255+
}
256+
257+
// Fast path 2: Defined routes ONLY (no auto-routing fallback)
258+
if ($useDefinedRoutes && ! $useAutoRoute) {
259+
if ($this->checkRoutes($uri)) {
260+
if ($this->collection->isFiltered($this->matchedRoute[0])) {
261+
$this->filtersInfo = $this->collection->getFiltersForRoute($this->matchedRoute[0]);
262+
}
263+
264+
return $this->controller;
265+
}
266+
267+
throw new PageNotFoundException(
268+
"Can't find a route for '{$this->collection->getHTTPVerb()}: {$uri}'.",
269+
);
270+
}
271+
272+
// Original path: BOTH enabled (check defined routes first, then auto-route)
224273
// Checks defined routes
225274
if ($this->checkRoutes($uri)) {
226275
if ($this->collection->isFiltered($this->matchedRoute[0])) {
@@ -388,8 +437,11 @@ public function setIndexPage($page): self
388437
*/
389438
public function setTranslateURIDashes(bool $val = false): self
390439
{
391-
if ($this->autoRouter instanceof AutoRouter) {
392-
$this->autoRouter->setTranslateURIDashes($val);
440+
// Need to get or create the AutoRouter instance
441+
$autoRouter = $this->collection->shouldAutoRoute() ? $this->getAutoRouter() : null;
442+
443+
if ($autoRouter instanceof AutoRouter) {
444+
$autoRouter->setTranslateURIDashes($val);
393445

394446
return $this;
395447
}
@@ -591,7 +643,7 @@ static function ($match) use ($matches) {
591643
public function autoRoute(string $uri)
592644
{
593645
[$this->directory, $this->controller, $this->method, $this->params]
594-
= $this->autoRouter->getRoute($uri, $this->collection->getHTTPVerb());
646+
= $this->getAutoRouter()->getRoute($uri, $this->collection->getHTTPVerb());
595647
}
596648

597649
/**
@@ -673,8 +725,11 @@ public function setDirectory(?string $dir = null, bool $append = false, bool $va
673725
$this->directory = null;
674726
}
675727

676-
if ($this->autoRouter instanceof AutoRouter) {
677-
$this->autoRouter->setDirectory($dir, $append, $validate);
728+
// Need to get or create the AutoRouter instance
729+
$autoRouter = $this->collection->shouldAutoRoute() ? $this->getAutoRouter() : null;
730+
731+
if ($autoRouter instanceof AutoRouter) {
732+
$autoRouter->setDirectory($dir, $append, $validate);
678733
}
679734
}
680735

0 commit comments

Comments
 (0)