Skip to content

Commit de3f3db

Browse files
committed
feat: spark filter:check shows filter classnames
1 parent 97e5ead commit de3f3db

File tree

4 files changed

+203
-8
lines changed

4 files changed

+203
-8
lines changed

system/Commands/Utilities/FilterCheck.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,28 @@ public function run(array $params)
125125

126126
CLI::table($tbody, $thead);
127127

128+
// Show filter classes
129+
$requiredFilterClasses = $filterCollector->getRequiredFilterClasses();
130+
$filterClasses = $filterCollector->getClasses($method, $route);
131+
132+
foreach ($requiredFilterClasses as $position => $classes) {
133+
foreach ($classes as $class) {
134+
$class = CLI::color($class, 'yellow');
135+
136+
$coloredRequiredFilterClasses[$position][] = $class;
137+
}
138+
}
139+
140+
$classList = [
141+
'before' => array_merge($coloredRequiredFilterClasses['before'], $filterClasses['before']),
142+
'after' => array_merge($filterClasses['after'], $coloredRequiredFilterClasses['after']),
143+
];
144+
145+
foreach ($classList as $position => $classes) {
146+
CLI::write(ucfirst($position) . ' Filter Classes:', 'cyan');
147+
CLI::write(implode('', $classes));
148+
}
149+
128150
return EXIT_SUCCESS;
129151
}
130152

system/Commands/Utilities/Routes/FilterCollector.php

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function __construct(
4343
* @param string $method HTTP verb like `GET`,`POST` or `CLI`.
4444
* @param string $uri URI path to find filters for
4545
*
46-
* @return array{before: list<string>, after: list<string>} array of filter alias or classname
46+
* @return array{before: list<string>, after: list<string>} array of alias/classname:args
4747
*/
4848
public function get(string $method, string $uri): array
4949
{
@@ -79,10 +79,52 @@ public function get(string $method, string $uri): array
7979
return $finder->find($uri);
8080
}
8181

82+
/**
83+
* Returns filter classes for the URI
84+
*
85+
* @param string $method HTTP verb like `GET`,`POST` or `CLI`.
86+
* @param string $uri URI path to find filters for
87+
*
88+
* @return array{before: list<string>, after: list<string>} array of classname:args
89+
*/
90+
public function getClasses(string $method, string $uri): array
91+
{
92+
if ($method === strtolower($method)) {
93+
@trigger_error(
94+
'Passing lowercase HTTP method "' . $method . '" is deprecated.'
95+
. ' Use uppercase HTTP method like "' . strtoupper($method) . '".',
96+
E_USER_DEPRECATED
97+
);
98+
}
99+
100+
/**
101+
* @deprecated 4.5.0
102+
* @TODO Remove this in the future.
103+
*/
104+
$method = strtoupper($method);
105+
106+
if ($method === 'CLI') {
107+
return [
108+
'before' => [],
109+
'after' => [],
110+
];
111+
}
112+
113+
$request = Services::incomingrequest(null, false);
114+
$request->setMethod($method);
115+
116+
$router = $this->createRouter($request);
117+
$filters = $this->createFilters($request);
118+
119+
$finder = new FilterFinder($router, $filters);
120+
121+
return $finder->findClasses($uri);
122+
}
123+
82124
/**
83125
* Returns Required Filters
84126
*
85-
* @return array{before: list<string>, after: list<string>} array of filter alias or classname
127+
* @return array{before: list<string>, after: list<string>} array of aliases
86128
*/
87129
public function getRequiredFilters(): array
88130
{
@@ -97,6 +139,24 @@ public function getRequiredFilters(): array
97139
return $finder->getRequiredFilters();
98140
}
99141

142+
/**
143+
* Returns Required Filter class list
144+
*
145+
* @return array{before: list<string>, after: list<string>} array of classnames
146+
*/
147+
public function getRequiredFilterClasses(): array
148+
{
149+
$request = Services::incomingrequest(null, false);
150+
$request->setMethod(Method::GET);
151+
152+
$router = $this->createRouter($request);
153+
$filters = $this->createFilters($request);
154+
155+
$finder = new FilterFinder($router, $filters);
156+
157+
return $finder->getRequiredFilterClasses();
158+
}
159+
100160
private function createRouter(Request $request): Router
101161
{
102162
$routes = service('routes');

system/Commands/Utilities/Routes/FilterFinder.php

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,20 @@ private function getRouteFilters(string $uri): array
4646
/**
4747
* @param string $uri URI path to find filters for
4848
*
49-
* @return array{before: list<string>, after: list<string>} array of filter alias or classname
49+
* @return array{before: list<string>, after: list<string>} array of alias/classname:args
5050
*/
5151
public function find(string $uri): array
5252
{
5353
$this->filters->reset();
5454

55-
// Add route filters
5655
try {
56+
// Add route filters
5757
$routeFilters = $this->getRouteFilters($uri);
58-
5958
$this->filters->enableFilters($routeFilters, 'before');
60-
6159
$oldFilterOrder = config(Feature::class)->oldFilterOrder ?? false;
6260
if (! $oldFilterOrder) {
6361
$routeFilters = array_reverse($routeFilters);
6462
}
65-
6663
$this->filters->enableFilters($routeFilters, 'after');
6764

6865
$this->filters->initialize($uri);
@@ -81,10 +78,66 @@ public function find(string $uri): array
8178
}
8279
}
8380

81+
/**
82+
* @param string $uri URI path to find filters for
83+
*
84+
* @return array{before: list<string>, after: list<string>} array of classname:args
85+
*/
86+
public function findClasses(string $uri): array
87+
{
88+
$this->filters->reset();
89+
90+
try {
91+
// Add route filters
92+
$routeFilters = $this->getRouteFilters($uri);
93+
$this->filters->enableFilters($routeFilters, 'before');
94+
$oldFilterOrder = config(Feature::class)->oldFilterOrder ?? false;
95+
if (! $oldFilterOrder) {
96+
$routeFilters = array_reverse($routeFilters);
97+
}
98+
$this->filters->enableFilters($routeFilters, 'after');
99+
100+
$this->filters->initialize($uri);
101+
102+
$filterClassList = $this->filters->getFiltersClass();
103+
104+
$filterClasses = [
105+
'before' => [],
106+
'after' => [],
107+
];
108+
109+
foreach ($filterClassList['before'] as $classInfo) {
110+
$classWithArguments = ($classInfo[1] === []) ? $classInfo[0]
111+
: $classInfo[0] . ':' . implode(',', $classInfo[1]);
112+
113+
$filterClasses['before'][] = $classWithArguments;
114+
}
115+
116+
foreach ($filterClassList['after'] as $classInfo) {
117+
$classWithArguments = ($classInfo[1] === []) ? $classInfo[0]
118+
: $classInfo[0] . ':' . implode(',', $classInfo[1]);
119+
120+
$filterClasses['after'][] = $classWithArguments;
121+
}
122+
123+
return $filterClasses;
124+
} catch (RedirectException) {
125+
return [
126+
'before' => [],
127+
'after' => [],
128+
];
129+
} catch (BadRequestException|PageNotFoundException) {
130+
return [
131+
'before' => ['<unknown>'],
132+
'after' => ['<unknown>'],
133+
];
134+
}
135+
}
136+
84137
/**
85138
* Returns Required Filters
86139
*
87-
* @return array{before: list<string>, after:list<string>}
140+
* @return array{before: list<string>, after:list<string>} array of aliases
88141
*/
89142
public function getRequiredFilters(): array
90143
{
@@ -96,4 +149,28 @@ public function getRequiredFilters(): array
96149
'after' => $requiredAfter,
97150
];
98151
}
152+
153+
/**
154+
* Returns Required Filter classes
155+
*
156+
* @return array{before: list<string>, after:list<string>}
157+
*/
158+
public function getRequiredFilterClasses(): array
159+
{
160+
$before = $this->filters->getRequiredClasses('before');
161+
$after = $this->filters->getRequiredClasses('after');
162+
163+
foreach ($before as $classInfo) {
164+
$requiredBefore[] = $classInfo[0];
165+
}
166+
167+
foreach ($after as $classInfo) {
168+
$requiredAfter[] = $classInfo[0];
169+
}
170+
171+
return [
172+
'before' => $requiredBefore,
173+
'after' => $requiredAfter,
174+
];
175+
}
99176
}

tests/system/Commands/Utilities/Routes/FilterFinderTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,42 @@ public function testFindGlobalsAndRouteFilters(): void
154154
$this->assertSame($expected, $filters);
155155
}
156156

157+
public function testFindGlobalsAndRouteFiltersWithArguments(): void
158+
{
159+
$collection = $this->createRouteCollection();
160+
$collection->get('admin', ' AdminController::index', ['filter' => 'honeypot:arg1,arg2']);
161+
$router = $this->createRouter($collection);
162+
$filters = $this->createFilters();
163+
164+
$finder = new FilterFinder($router, $filters);
165+
166+
$filters = $finder->find('admin');
167+
168+
$expected = [
169+
'before' => ['csrf', 'honeypot:arg1,arg2'],
170+
'after' => ['honeypot:arg1,arg2', 'toolbar'],
171+
];
172+
$this->assertSame($expected, $filters);
173+
}
174+
175+
public function testFindClassesGlobalsAndRouteFiltersWithArguments(): void
176+
{
177+
$collection = $this->createRouteCollection();
178+
$collection->get('admin', ' AdminController::index', ['filter' => 'honeypot:arg1,arg2']);
179+
$router = $this->createRouter($collection);
180+
$filters = $this->createFilters();
181+
182+
$finder = new FilterFinder($router, $filters);
183+
184+
$filters = $finder->findClasses('admin');
185+
186+
$expected = [
187+
'before' => [CSRF::class, Honeypot::class . ':arg1,arg2'],
188+
'after' => [Honeypot::class . ':arg1,arg2', DebugToolbar::class],
189+
];
190+
$this->assertSame($expected, $filters);
191+
}
192+
157193
public function testFindGlobalsAndRouteClassnameFilters(): void
158194
{
159195
$collection = $this->createRouteCollection();

0 commit comments

Comments
 (0)