Skip to content

Commit f801829

Browse files
authored
Merge pull request #5628 from kenjis/add-spark-routes-filters
feat: `spark routes` shows filters
2 parents eb5d4b2 + c65cb31 commit f801829

File tree

14 files changed

+581
-21
lines changed

14 files changed

+581
-21
lines changed

system/CodeIgniter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ protected function initializeKint()
286286
* @throws Exception
287287
* @throws RedirectException
288288
*
289-
* @return bool|mixed|RequestInterface|ResponseInterface
289+
* @return bool|mixed|RequestInterface|ResponseInterface|void
290290
*/
291291
public function run(?RouteCollectionInterface $routes = null, bool $returnResponse = false)
292292
{
@@ -703,7 +703,7 @@ public function displayPerformanceMetrics(string $output): string
703703
*
704704
* @throws RedirectException
705705
*
706-
* @return string|string[]|null
706+
* @return string|string[]|null Route filters, that is, the filters specified in the routes file
707707
*/
708708
protected function tryToRouteIt(?RouteCollectionInterface $routes = null)
709709
{

system/Commands/Utilities/Routes.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
use CodeIgniter\CLI\BaseCommand;
1616
use CodeIgniter\CLI\CLI;
1717
use CodeIgniter\Commands\Utilities\Routes\AutoRouteCollector;
18+
use CodeIgniter\Commands\Utilities\Routes\FilterCollector;
19+
use CodeIgniter\Commands\Utilities\Routes\SampleURIGenerator;
1820
use Config\Services;
1921

2022
/**
@@ -44,7 +46,7 @@ class Routes extends BaseCommand
4446
*
4547
* @var string
4648
*/
47-
protected $description = 'Displays all of user-defined routes. Does NOT display auto-detected routes.';
49+
protected $description = 'Displays all routes.';
4850

4951
/**
5052
* the Command's usage
@@ -86,18 +88,24 @@ public function run(array $params)
8688
'cli',
8789
];
8890

89-
$tbody = [];
91+
$tbody = [];
92+
$uriGenerator = new SampleURIGenerator();
93+
$filterCollector = new FilterCollector();
9094

9195
foreach ($methods as $method) {
9296
$routes = $collection->getRoutes($method);
9397

9498
foreach ($routes as $route => $handler) {
95-
// filter for strings, as callbacks aren't displayable
9699
if (is_string($handler) || $handler instanceof Closure) {
100+
$sampleUri = $uriGenerator->get($route);
101+
$filters = $filterCollector->get($method, $sampleUri);
102+
97103
$tbody[] = [
98104
strtoupper($method),
99105
$route,
100106
is_string($handler) ? $handler : '(Closure)',
107+
implode(' ', array_map('class_basename', $filters['before'])),
108+
implode(' ', array_map('class_basename', $filters['after'])),
101109
];
102110
}
103111
}
@@ -109,13 +117,23 @@ public function run(array $params)
109117
$collection->getDefaultController(),
110118
$collection->getDefaultMethod()
111119
);
112-
$tbody = [...$tbody, ...$autoRouteCollector->get()];
120+
$autoRoutes = $autoRouteCollector->get();
121+
122+
foreach ($autoRoutes as &$routes) {
123+
$filters = $filterCollector->get('get', $uriGenerator->get($routes[1]));
124+
$routes[] = implode(' ', array_map('class_basename', $filters['before']));
125+
$routes[] = implode(' ', array_map('class_basename', $filters['after']));
126+
}
127+
128+
$tbody = [...$tbody, ...$autoRoutes];
113129
}
114130

115131
$thead = [
116132
'Method',
117133
'Route',
118134
'Handler',
135+
'Before Filters',
136+
'After Filters',
119137
];
120138

121139
CLI::table($tbody, $thead);
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
/**
4+
* This file is part of CodeIgniter 4 framework.
5+
*
6+
* (c) CodeIgniter Foundation <admin@codeigniter.com>
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*/
11+
12+
namespace CodeIgniter\Commands\Utilities\Routes;
13+
14+
use CodeIgniter\Config\Services;
15+
use CodeIgniter\Filters\Filters;
16+
use CodeIgniter\HTTP\Request;
17+
use CodeIgniter\Router\Router;
18+
19+
/**
20+
* Collects filters for a route.
21+
*/
22+
final class FilterCollector
23+
{
24+
/**
25+
* @param string $method HTTP method
26+
* @param string $uri URI path to find filters for
27+
*
28+
* @return array{before: list<string>, after: list<string>} array of filter alias or classname
29+
*/
30+
public function get(string $method, string $uri): array
31+
{
32+
if ($method === 'cli') {
33+
return [
34+
'before' => [],
35+
'after' => [],
36+
];
37+
}
38+
39+
$request = Services::request(null, false);
40+
$request->setMethod($method);
41+
42+
$router = $this->createRouter($request);
43+
$filters = $this->createFilters($request);
44+
45+
$finder = new FilterFinder($router, $filters);
46+
47+
return $finder->find($uri);
48+
}
49+
50+
private function createRouter(Request $request): Router
51+
{
52+
return new Router(Services::routes(), $request);
53+
}
54+
55+
private function createFilters(Request $request): Filters
56+
{
57+
$config = config('Filters');
58+
59+
return new Filters($config, $request, Services::response());
60+
}
61+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
/**
4+
* This file is part of CodeIgniter 4 framework.
5+
*
6+
* (c) CodeIgniter Foundation <admin@codeigniter.com>
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*/
11+
12+
namespace CodeIgniter\Commands\Utilities\Routes;
13+
14+
use CodeIgniter\Filters\Filters;
15+
use CodeIgniter\Router\Exceptions\RedirectException;
16+
use CodeIgniter\Router\Router;
17+
use Config\Services;
18+
19+
/**
20+
* Finds filters.
21+
*/
22+
final class FilterFinder
23+
{
24+
private Router $router;
25+
private Filters $filters;
26+
27+
public function __construct(?Router $router = null, ?Filters $filters = null)
28+
{
29+
$this->router = $router ?? Services::router();
30+
$this->filters = $filters ?? Services::filters();
31+
}
32+
33+
private function getRouteFilters(string $uri): array
34+
{
35+
$this->router->handle($uri);
36+
37+
$multipleFiltersEnabled = config('Feature')->multipleFilters ?? false;
38+
if (! $multipleFiltersEnabled) {
39+
$filter = $this->router->getFilter();
40+
41+
return $filter === null ? [] : [$filter];
42+
}
43+
44+
return $this->router->getFilters();
45+
}
46+
47+
/**
48+
* @param string $uri URI path to find filters for
49+
*
50+
* @return array{before: list<string>, after: list<string>} array of filter alias or classname
51+
*/
52+
public function find(string $uri): array
53+
{
54+
$this->filters->reset();
55+
56+
// Add route filters
57+
try {
58+
$routeFilters = $this->getRouteFilters($uri);
59+
$this->filters->enableFilters($routeFilters, 'before');
60+
$this->filters->enableFilters($routeFilters, 'after');
61+
62+
$this->filters->initialize($uri);
63+
64+
return $this->filters->getFilters();
65+
} catch (RedirectException $e) {
66+
return [
67+
'before' => [],
68+
'after' => [],
69+
];
70+
}
71+
}
72+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
/**
4+
* This file is part of CodeIgniter 4 framework.
5+
*
6+
* (c) CodeIgniter Foundation <admin@codeigniter.com>
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*/
11+
12+
namespace CodeIgniter\Commands\Utilities\Routes;
13+
14+
use CodeIgniter\Config\Services;
15+
use CodeIgniter\Router\RouteCollection;
16+
17+
/**
18+
* Generate a sample URI path from route key regex.
19+
*/
20+
final class SampleURIGenerator
21+
{
22+
private RouteCollection $routes;
23+
24+
/**
25+
* Sample URI path for placeholder.
26+
*
27+
* @var array<string, string>
28+
*/
29+
private array $samples = [
30+
'any' => '123/abc',
31+
'segment' => 'abc_123',
32+
'alphanum' => 'abc123',
33+
'num' => '123',
34+
'alpha' => 'abc',
35+
'hash' => 'abc_123',
36+
];
37+
38+
public function __construct(?RouteCollection $routes = null)
39+
{
40+
$this->routes = $routes ?? Services::routes();
41+
}
42+
43+
/**
44+
* @param string $routeKey route key regex
45+
*
46+
* @return string sample URI path
47+
*/
48+
public function get(string $routeKey): string
49+
{
50+
$sampleUri = $routeKey;
51+
52+
foreach ($this->routes->getPlaceholders() as $placeholder => $regex) {
53+
$sample = $this->samples[$placeholder] ?? '::unknown::';
54+
55+
$sampleUri = str_replace('(' . $regex . ')', $sample, $sampleUri);
56+
}
57+
58+
// auto route
59+
return str_replace('[/...]', '/1/2/3/4/5', $sampleUri);
60+
}
61+
}

system/Filters/Filters.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ public function enableFilter(string $name, string $when = 'before')
355355
}
356356

357357
/**
358-
* Ensures that specific filters is on and enabled for the current request.
358+
* Ensures that specific filters are on and enabled for the current request.
359359
*
360360
* Filters can have "arguments". This is done by placing a colon immediately
361361
* after the filter name, followed by a comma-separated list of arguments that

system/Router/RouteCollection.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class RouteCollection implements RouteCollectionInterface
9090
* Defined placeholders that can be used
9191
* within the
9292
*
93-
* @var array
93+
* @var array<string, string>
9494
*/
9595
protected $placeholders = [
9696
'any' => '.*',
@@ -238,6 +238,18 @@ public function addPlaceholder($placeholder, ?string $pattern = null): RouteColl
238238
return $this;
239239
}
240240

241+
/**
242+
* For `spark routes`
243+
*
244+
* @return array<string, string>
245+
*
246+
* @internal
247+
*/
248+
public function getPlaceholders(): array
249+
{
250+
return $this->placeholders;
251+
}
252+
241253
/**
242254
* Sets the default namespace to use for Controllers when no other
243255
* namespace has been specified.
@@ -429,7 +441,7 @@ public function getRoutes(?string $verb = null): array
429441
$collection = [];
430442

431443
if (isset($this->routes[$verb])) {
432-
// Keep current verb's routes at the beginning so they're matched
444+
// Keep current verb's routes at the beginning, so they're matched
433445
// before any of the generic, "add" routes.
434446
if (isset($this->routes['*'])) {
435447
$extraRules = array_diff_key($this->routes['*'], $this->routes[$verb]);

system/Router/Router.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace CodeIgniter\Router;
1313

14+
use Closure;
1415
use CodeIgniter\Exceptions\PageNotFoundException;
1516
use CodeIgniter\HTTP\Request;
1617
use CodeIgniter\Router\Exceptions\RedirectException;
@@ -39,7 +40,7 @@ class Router implements RouterInterface
3940
/**
4041
* The name of the controller class.
4142
*
42-
* @var string
43+
* @var Closure|string
4344
*/
4445
protected $controller;
4546

@@ -181,7 +182,7 @@ public function handle(?string $uri = null)
181182
/**
182183
* Returns the filter info for the matched route, if any.
183184
*
184-
* @return string
185+
* @return string|null
185186
*
186187
* @deprecated Use getFilters()
187188
*/
@@ -203,7 +204,7 @@ public function getFilters(): array
203204
/**
204205
* Returns the name of the matched controller.
205206
*
206-
* @return mixed
207+
* @return Closure|string
207208
*/
208209
public function controllerName()
209210
{

0 commit comments

Comments
 (0)