Skip to content

Commit 1d2489c

Browse files
authored
refactor: migrate routes command as modern command (#10457)
1 parent 583bb04 commit 1d2489c

1 file changed

Lines changed: 140 additions & 147 deletions

File tree

system/Commands/Utilities/Routes.php

Lines changed: 140 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@
1313

1414
namespace CodeIgniter\Commands\Utilities;
1515

16-
use CodeIgniter\CLI\BaseCommand;
16+
use CodeIgniter\CLI\AbstractCommand;
17+
use CodeIgniter\CLI\Attributes\Command;
1718
use CodeIgniter\CLI\CLI;
19+
use CodeIgniter\CLI\Input\Option;
1820
use CodeIgniter\Commands\Utilities\Routes\AutoRouteCollector;
1921
use CodeIgniter\Commands\Utilities\Routes\AutoRouterImproved\AutoRouteCollector as AutoRouteCollectorImproved;
2022
use CodeIgniter\Commands\Utilities\Routes\FilterCollector;
2123
use CodeIgniter\Commands\Utilities\Routes\SampleURIGenerator;
2224
use CodeIgniter\Router\DefinedRouteCollector;
25+
use CodeIgniter\Router\RouteCollection;
2326
use CodeIgniter\Router\Router;
2427
use Config\Feature;
2528
use Config\Routing;
@@ -29,196 +32,186 @@
2932
* that can be discovered, and will include routes that are not defined
3033
* in routes files, but are instead discovered through auto-routing.
3134
*/
32-
class Routes extends BaseCommand
35+
#[Command(name: 'routes', description: 'Displays all routes.', group: 'CodeIgniter')]
36+
class Routes extends AbstractCommand
3337
{
34-
/**
35-
* The group the command is lumped under
36-
* when listing commands.
37-
*
38-
* @var string
39-
*/
40-
protected $group = 'CodeIgniter';
41-
42-
/**
43-
* The Command's name
44-
*
45-
* @var string
46-
*/
47-
protected $name = 'routes';
48-
49-
/**
50-
* the Command's short description
51-
*
52-
* @var string
53-
*/
54-
protected $description = 'Displays all routes.';
55-
56-
/**
57-
* the Command's usage
58-
*
59-
* @var string
60-
*/
61-
protected $usage = 'routes';
62-
63-
/**
64-
* the Command's Arguments
65-
*
66-
* @var array<string, string>
67-
*/
68-
protected $arguments = [];
38+
private SampleURIGenerator $uriGenerator;
39+
private FilterCollector $filterCollector;
6940

70-
/**
71-
* the Command's Options
72-
*
73-
* @var array<string, string>
74-
*/
75-
protected $options = [
76-
'--sort-by-handler' => 'Sort by handler.',
77-
'--host' => 'Specify hostname in request URI.',
78-
];
41+
protected function configure(): void
42+
{
43+
$this
44+
->addOption(new Option(
45+
name: 'sort-by-handler',
46+
description: 'Sort by handler.',
47+
))
48+
->addOption(new Option(
49+
name: 'host',
50+
description: 'Specify hostname in request URI.',
51+
requiresValue: true,
52+
default: '',
53+
));
54+
}
7955

80-
/**
81-
* Displays the help for the spark cli script itself.
82-
*/
83-
public function run(array $params)
56+
protected function execute(array $arguments, array $options): int
8457
{
85-
$sortByHandler = array_key_exists('sort-by-handler', $params);
58+
$sortByHandler = $options['sort-by-handler'] !== false;
8659

87-
$host = $params['host'] ?? null;
60+
$host = $options['host'];
61+
assert(is_string($host));
8862

89-
// Set HTTP_HOST
90-
if ($host !== null) {
63+
if ($host !== '') {
9164
service('superglobals')->setServer('HTTP_HOST', $host);
9265
}
9366

9467
$collection = service('routes')->loadRoutes();
9568

96-
// Reset HTTP_HOST
97-
if ($host !== null) {
69+
if ($host !== '') {
9870
service('superglobals')->unsetServer('HTTP_HOST');
9971
}
10072

101-
$methods = Router::HTTP_METHODS;
102-
103-
$tbody = [];
104-
$uriGenerator = new SampleURIGenerator();
105-
$filterCollector = new FilterCollector();
73+
$this->uriGenerator = new SampleURIGenerator();
74+
$this->filterCollector = new FilterCollector();
10675

107-
$definedRouteCollector = new DefinedRouteCollector($collection);
76+
$tbody = $this->collectDefinedRoutes($collection);
10877

109-
foreach ($definedRouteCollector->collect() as $route) {
110-
$sampleUri = $uriGenerator->get($route['route']);
111-
$filters = $filterCollector->get($route['method'], $sampleUri);
112-
113-
$routeName = ($route['route'] === $route['name']) ? '»' : $route['name'];
78+
if ($collection->shouldAutoRoute()) {
79+
$tbody = [...$tbody, ...$this->collectAutoRoutes($collection)];
80+
}
11481

115-
$tbody[] = [
116-
strtoupper($route['method']),
117-
$route['route'],
118-
$routeName,
119-
$route['handler'],
120-
implode(' ', array_map(class_basename(...), $filters['before'])),
121-
implode(' ', array_map(class_basename(...), $filters['after'])),
122-
];
82+
if ($sortByHandler) {
83+
usort($tbody, static fn (array $route1, array $route2): int => strcmp($route1[3], $route2[3]));
12384
}
12485

125-
if ($collection->shouldAutoRoute()) {
126-
$autoRoutesImproved = config(Feature::class)->autoRoutesImproved;
127-
128-
if ($autoRoutesImproved) {
129-
$autoRouteCollector = new AutoRouteCollectorImproved(
130-
$collection->getDefaultNamespace(),
131-
$collection->getDefaultController(),
132-
$collection->getDefaultMethod(),
133-
$methods,
134-
$collection->getRegisteredControllers('*'),
135-
);
136-
137-
$autoRoutes = $autoRouteCollector->get();
138-
139-
// Check for Module Routes.
140-
$routingConfig = config(Routing::class);
141-
142-
if ($routingConfig instanceof Routing) {
143-
foreach ($routingConfig->moduleRoutes as $uri => $namespace) {
144-
$autoRouteCollector = new AutoRouteCollectorImproved(
145-
$namespace,
146-
$collection->getDefaultController(),
147-
$collection->getDefaultMethod(),
148-
$methods,
149-
$collection->getRegisteredControllers('*'),
150-
$uri,
151-
);
152-
153-
$autoRoutes = [...$autoRoutes, ...$autoRouteCollector->get()];
154-
}
155-
}
156-
} else {
157-
$autoRouteCollector = new AutoRouteCollector(
158-
$collection->getDefaultNamespace(),
159-
$collection->getDefaultController(),
160-
$collection->getDefaultMethod(),
161-
);
162-
163-
$autoRoutes = $autoRouteCollector->get();
164-
165-
foreach ($autoRoutes as &$routes) {
166-
// There is no `AUTO` method, but it is intentional not to get route filters.
167-
$filters = $filterCollector->get('AUTO', $uriGenerator->get($routes[1]));
168-
169-
$routes[] = implode(' ', array_map(class_basename(...), $filters['before']));
170-
$routes[] = implode(' ', array_map(class_basename(...), $filters['after']));
171-
}
172-
}
173-
174-
$tbody = [...$tbody, ...$autoRoutes];
86+
if ($host !== '') {
87+
CLI::write(sprintf('Host: %s', $host));
17588
}
17689

177-
$thead = [
90+
CLI::table($tbody, [
17891
'Method',
17992
'Route',
18093
'Name',
18194
$sortByHandler ? 'Handler ↓' : 'Handler',
18295
'Before Filters',
18396
'After Filters',
184-
];
97+
]);
18598

186-
// Sort by Handler.
187-
if ($sortByHandler) {
188-
usort($tbody, static fn ($handler1, $handler2): int => strcmp($handler1[3], $handler2[3]));
189-
}
99+
return $this->showRequiredFilters();
100+
}
190101

191-
if ($host !== null) {
192-
CLI::write('Host: ' . $host);
193-
}
102+
/**
103+
* @return list<list<string>>
104+
*/
105+
private function collectDefinedRoutes(RouteCollection $collection): array
106+
{
107+
$tbody = [];
194108

195-
CLI::table($tbody, $thead);
109+
foreach ((new DefinedRouteCollector($collection))->collect() as $route) {
110+
$filters = $this->filterCollector->get(
111+
$route['method'],
112+
$this->uriGenerator->get($route['route']),
113+
);
196114

197-
return $this->showRequiredFilters();
115+
$tbody[] = [
116+
strtoupper($route['method']),
117+
$route['route'],
118+
$route['route'] === $route['name'] ? '»' : $route['name'],
119+
$route['handler'],
120+
$this->basenames($filters['before']),
121+
$this->basenames($filters['after']),
122+
];
123+
}
124+
125+
return $tbody;
198126
}
199127

200-
private function showRequiredFilters(): int
128+
/**
129+
* @return list<list<string>>
130+
*/
131+
private function collectAutoRoutes(RouteCollection $collection): array
201132
{
202-
$filterCollector = new FilterCollector();
133+
if (config(Feature::class)->autoRoutesImproved) {
134+
return $this->collectImprovedAutoRoutes($collection);
135+
}
203136

204-
$required = $filterCollector->getRequiredFilters();
137+
$autoRoutes = (new AutoRouteCollector(
138+
$collection->getDefaultNamespace(),
139+
$collection->getDefaultController(),
140+
$collection->getDefaultMethod(),
141+
))->get();
205142

206-
$filters = [];
143+
foreach ($autoRoutes as &$route) {
144+
// There is no `AUTO` method, but it is intentional not to get route filters.
145+
$filters = $this->filterCollector->get('AUTO', $this->uriGenerator->get($route[1]));
207146

208-
foreach ($required['before'] as $filter) {
209-
$filters[] = CLI::color($filter, 'yellow');
147+
$route[] = $this->basenames($filters['before']);
148+
$route[] = $this->basenames($filters['after']);
210149
}
211150

212-
CLI::write('Required Before Filters: ' . implode(', ', $filters));
151+
return $autoRoutes;
152+
}
213153

214-
$filters = [];
154+
/**
155+
* @return list<list<string>>
156+
*/
157+
private function collectImprovedAutoRoutes(RouteCollection $collection): array
158+
{
159+
$autoRoutes = (new AutoRouteCollectorImproved(
160+
$collection->getDefaultNamespace(),
161+
$collection->getDefaultController(),
162+
$collection->getDefaultMethod(),
163+
Router::HTTP_METHODS,
164+
$collection->getRegisteredControllers('*'),
165+
))->get();
166+
167+
$routingConfig = config(Routing::class);
168+
169+
if (! $routingConfig instanceof Routing) {
170+
return $autoRoutes;
171+
}
215172

216-
foreach ($required['after'] as $filter) {
217-
$filters[] = CLI::color($filter, 'yellow');
173+
foreach ($routingConfig->moduleRoutes as $uri => $namespace) {
174+
$moduleRoutes = (new AutoRouteCollectorImproved(
175+
$namespace,
176+
$collection->getDefaultController(),
177+
$collection->getDefaultMethod(),
178+
Router::HTTP_METHODS,
179+
$collection->getRegisteredControllers('*'),
180+
$uri,
181+
))->get();
182+
183+
$autoRoutes = [...$autoRoutes, ...$moduleRoutes];
218184
}
219185

220-
CLI::write(' Required After Filters: ' . implode(', ', $filters));
186+
return $autoRoutes;
187+
}
188+
189+
/**
190+
* @param list<string> $filters
191+
*/
192+
private function basenames(array $filters): string
193+
{
194+
return implode(' ', array_map(class_basename(...), $filters));
195+
}
196+
197+
private function showRequiredFilters(): int
198+
{
199+
$required = (new FilterCollector())->getRequiredFilters();
200+
201+
CLI::write(sprintf('Required Before Filters: %s', $this->highlight($required['before'])));
202+
CLI::write(sprintf(' Required After Filters: %s', $this->highlight($required['after'])));
221203

222204
return EXIT_SUCCESS;
223205
}
206+
207+
/**
208+
* @param list<string> $filters
209+
*/
210+
private function highlight(array $filters): string
211+
{
212+
return implode(', ', array_map(
213+
static fn (string $filter): string => CLI::color($filter, 'yellow'),
214+
$filters,
215+
));
216+
}
224217
}

0 commit comments

Comments
 (0)