Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 77 additions & 129 deletions system/Commands/Utilities/FilterCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,74 +13,41 @@

namespace CodeIgniter\Commands\Utilities;

use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\AbstractCommand;
use CodeIgniter\CLI\Attributes\Command;
use CodeIgniter\CLI\CLI;
use CodeIgniter\CLI\Input\Argument;
use CodeIgniter\Commands\Utilities\Routes\FilterCollector;

/**
* Check filters for a route.
*/
class FilterCheck extends BaseCommand
#[Command(name: 'filter:check', description: 'Check filters for a route.', group: 'CodeIgniter')]
class FilterCheck extends AbstractCommand
{
/**
* The group the command is lumped under
* when listing commands.
*
* @var string
*/
protected $group = 'CodeIgniter';

/**
* The Command's name
*
* @var string
*/
protected $name = 'filter:check';

/**
* the Command's short description
*
* @var string
*/
protected $description = 'Check filters for a route.';

/**
* the Command's usage
*
* @var string
*/
protected $usage = 'filter:check <HTTP method> <route>';

/**
* the Command's Arguments
*
* @var array<string, string>
*/
protected $arguments = [
'method' => 'The HTTP method. GET, POST, PUT, etc.',
'route' => 'The route (URI path) to check filters.',
];

/**
* the Command's Options
*
* @var array<string, string>
*/
protected $options = [];
protected function configure(): void
{
$this
->addArgument(new Argument(
name: 'method',
description: 'The HTTP method. GET, POST, PUT, etc.',
required: true,
))
->addArgument(new Argument(
name: 'route',
description: 'The route (URI path) to check filters.',
required: true,
));
}

/**
* @return int exit code
*/
public function run(array $params)
protected function execute(array $arguments, array $options): int
{
if (! $this->checkParams($params)) {
return EXIT_ERROR;
}
$method = $arguments['method'];
assert(is_string($method));

$method = $params[0];
$route = $params[1];
$route = $arguments['route'];
assert(is_string($route));

// Load Routes
service('routes')->loadRoutes();

$filterCollector = new FilterCollector();
Expand All @@ -89,14 +56,10 @@ public function run(array $params)

// PageNotFoundException
if ($filters['before'] === ['<unknown>']) {
CLI::error(
"Can't find a route: " .
CLI::color(
'"' . strtoupper($method) . ' ' . $route . '"',
'black',
'light_gray',
),
);
CLI::error(sprintf(
"Can't find a route: %s",
CLI::color(sprintf('"%s %s"', strtoupper($method), $route), 'black', 'light_gray'),
));

return EXIT_ERROR;
}
Expand All @@ -107,23 +70,6 @@ public function run(array $params)
return EXIT_SUCCESS;
}

/**
* @param array<int|string, string|null> $params
*/
private function checkParams(array $params): bool
{
if (! isset($params[0], $params[1])) {
CLI::error('You must specify a HTTP verb and a route.');
CLI::write(' Usage: ' . $this->usage);
CLI::write('Example: filter:check GET /');
CLI::write(' filter:check PUT products/1');

return false;
}

return true;
}

/**
* @param array{before: list<string>, after: list<string>} $filters
*/
Expand All @@ -133,67 +79,69 @@ private function showTable(
string $method,
string $route,
): void {
$thead = [
'Method',
'Route',
'Before Filters',
'After Filters',
$merged = $this->mergeFilters($filterCollector->getRequiredFilters(), $filters);

$thead = ['Method', 'Route', 'Before Filters', 'After Filters'];
$tbody = [
[
strtoupper($method),
$route,
implode(' ', $merged['before']),
implode(' ', $merged['after']),
],
];

$required = $filterCollector->getRequiredFilters();
CLI::table($tbody, $thead);
}

$coloredRequired = $this->colorItems($required);
private function showFilterClasses(
FilterCollector $filterCollector,
string $method,
string $route,
): void {
$merged = $this->mergeFilters(
$filterCollector->getRequiredFilterClasses(),
$filterCollector->getClasses($method, $route),
);

$before = array_merge($coloredRequired['before'], $filters['before']);
$after = array_merge($filters['after'], $coloredRequired['after']);
$lastPosition = array_key_last($merged);

$tbody = [];
$tbody[] = [
strtoupper($method),
$route,
implode(' ', $before),
implode(' ', $after),
];
foreach ($merged as $position => $classes) {
CLI::write(sprintf('%s Filter Classes:', ucfirst($position)), 'cyan');
CLI::write(implode(' → ', $classes));

CLI::table($tbody, $thead);
if ($position !== $lastPosition) {
CLI::newLine();
}
}
}

/**
* Color all elements of the array.
* Merges the required filters (highlighted) with the route's filters,
* keeping required-before filters first and required-after filters last.
*
* @param array<array-key, mixed> $array
* @param array{before: list<string>, after: list<string>} $required
* @param array{before: list<string>, after: list<string>} $filters
*
* @return array<array-key, mixed>
* @return array{before: list<string>, after: list<string>}
*/
private function colorItems(array $array): array
private function mergeFilters(array $required, array $filters): array
{
return array_map(function ($item): array|string {
if (is_array($item)) {
return $this->colorItems($item);
}

return CLI::color($item, 'yellow');
}, $array);
}

private function showFilterClasses(
FilterCollector $filterCollector,
string $method,
string $route,
): void {
$requiredFilterClasses = $filterCollector->getRequiredFilterClasses();
$filterClasses = $filterCollector->getClasses($method, $route);

$coloredRequiredFilterClasses = $this->colorItems($requiredFilterClasses);

$classList = [
'before' => array_merge($coloredRequiredFilterClasses['before'], $filterClasses['before']),
'after' => array_merge($filterClasses['after'], $coloredRequiredFilterClasses['after']),
return [
'before' => array_merge($this->highlight($required['before']), $filters['before']),
'after' => array_merge($filters['after'], $this->highlight($required['after'])),
];
}

foreach ($classList as $position => $classes) {
CLI::write(ucfirst($position) . ' Filter Classes:', 'cyan');
CLI::write(implode(' → ', $classes));
}
/**
* Applies the highlight color to the given filter names.
*
* @param list<string> $items
*
* @return list<string>
*/
private function highlight(array $items): array
{
return array_map(static fn (string $item): string => CLI::color($item, 'yellow'), $items);
}
}
1 change: 1 addition & 0 deletions tests/system/Commands/Utilities/FilterCheckTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public function testFilterCheckDefinedRoute(): void
$this->assertStringContainsString(
'Before Filter Classes:
CodeIgniter\Filters\ForceHTTPS → CodeIgniter\Filters\PageCache

After Filter Classes:
CodeIgniter\Filters\PageCache → CodeIgniter\Filters\PerformanceMetrics → CodeIgniter\Filters\DebugToolbar',
(string) preg_replace('/\033\[.+?m/u', '', $this->getBuffer()),
Expand Down
Loading