Skip to content

Commit 05bbe66

Browse files
authored
Merge pull request #7955 from kenjis/fix-filter-order
fix: filter exec order
2 parents c95a2a5 + 7687855 commit 05bbe66

File tree

9 files changed

+301
-28
lines changed

9 files changed

+301
-28
lines changed

app/Config/Feature.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,9 @@ class Feature extends BaseConfig
1313
* Use improved new auto routing instead of the default legacy version.
1414
*/
1515
public bool $autoRoutesImproved = false;
16+
17+
/**
18+
* Use filter execution order in 4.4 or before.
19+
*/
20+
public bool $oldFilterOrder = false;
1621
}

system/CodeIgniter.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use CodeIgniter\Router\Router;
3232
use Config\App;
3333
use Config\Cache;
34+
use Config\Feature;
3435
use Config\Kint as KintConfig;
3536
use Config\Services;
3637
use Exception;
@@ -446,7 +447,7 @@ protected function handleRequest(?RouteCollectionInterface $routes, Cache $cache
446447
return $response;
447448
}
448449

449-
$routeFilter = $this->tryToRouteIt($routes);
450+
$routeFilters = $this->tryToRouteIt($routes);
450451

451452
$uri = $this->determinePath();
452453

@@ -456,9 +457,14 @@ protected function handleRequest(?RouteCollectionInterface $routes, Cache $cache
456457

457458
// If any filters were specified within the routes file,
458459
// we need to ensure it's active for the current request
459-
if ($routeFilter !== null) {
460-
$filters->enableFilters($routeFilter, 'before');
461-
$filters->enableFilters($routeFilter, 'after');
460+
if ($routeFilters !== null) {
461+
$filters->enableFilters($routeFilters, 'before');
462+
463+
if (! config(Feature::class)->oldFilterOrder) {
464+
$routeFilters = array_reverse($routeFilters);
465+
}
466+
467+
$filters->enableFilters($routeFilters, 'after');
462468
}
463469

464470
// Run "before" filters

system/Commands/Utilities/Routes/FilterFinder.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use CodeIgniter\Filters\Filters;
1616
use CodeIgniter\HTTP\Exceptions\RedirectException;
1717
use CodeIgniter\Router\Router;
18+
use Config\Feature;
1819
use Config\Services;
1920

2021
/**
@@ -52,7 +53,13 @@ public function find(string $uri): array
5253
// Add route filters
5354
try {
5455
$routeFilters = $this->getRouteFilters($uri);
56+
5557
$this->filters->enableFilters($routeFilters, 'before');
58+
59+
if (! config(Feature::class)->oldFilterOrder) {
60+
$routeFilters = array_reverse($routeFilters);
61+
}
62+
5663
$this->filters->enableFilters($routeFilters, 'after');
5764

5865
$this->filters->initialize($uri);

system/Filters/Filters.php

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use CodeIgniter\Filters\Exceptions\FilterException;
1616
use CodeIgniter\HTTP\RequestInterface;
1717
use CodeIgniter\HTTP\ResponseInterface;
18+
use Config\Feature;
1819
use Config\Filters as FiltersConfig;
1920
use Config\Modules;
2021
use Config\Services;
@@ -245,9 +246,15 @@ public function initialize(?string $uri = null)
245246
return $this;
246247
}
247248

248-
$this->processGlobals($uri);
249-
$this->processMethods();
250-
$this->processFilters($uri);
249+
if (config(Feature::class)->oldFilterOrder) {
250+
$this->processGlobals($uri);
251+
$this->processMethods();
252+
$this->processFilters($uri);
253+
} else {
254+
$this->processFilters($uri);
255+
$this->processMethods();
256+
$this->processGlobals($uri);
257+
}
251258

252259
// Set the toolbar filter to the last position to be executed
253260
if (in_array('toolbar', $this->filters['after'], true)
@@ -436,6 +443,8 @@ protected function processGlobals(?string $uri = null)
436443
// Add any global filters, unless they are excluded for this URI
437444
$sets = ['before', 'after'];
438445

446+
$filters = [];
447+
439448
foreach ($sets as $set) {
440449
if (isset($this->config->globals[$set])) {
441450
// look at each alias in the group
@@ -455,11 +464,23 @@ protected function processGlobals(?string $uri = null)
455464
}
456465

457466
if ($keep) {
458-
$this->filters[$set][] = $alias;
467+
$filters[$set][] = $alias;
459468
}
460469
}
461470
}
462471
}
472+
473+
if (isset($filters['before'])) {
474+
if (config(Feature::class)->oldFilterOrder) {
475+
$this->filters['before'] = array_merge($this->filters['before'], $filters['before']);
476+
} else {
477+
$this->filters['before'] = array_merge($filters['before'], $this->filters['before']);
478+
}
479+
}
480+
481+
if (isset($filters['after'])) {
482+
$this->filters['after'] = array_merge($this->filters['after'], $filters['after']);
483+
}
463484
}
464485

465486
/**
@@ -477,7 +498,11 @@ protected function processMethods()
477498
$method = strtolower($this->request->getMethod()) ?? 'cli';
478499

479500
if (array_key_exists($method, $this->config->methods)) {
480-
$this->filters['before'] = array_merge($this->filters['before'], $this->config->methods[$method]);
501+
if (config(Feature::class)->oldFilterOrder) {
502+
$this->filters['before'] = array_merge($this->filters['before'], $this->config->methods[$method]);
503+
} else {
504+
$this->filters['before'] = array_merge($this->config->methods[$method], $this->filters['before']);
505+
}
481506
}
482507
}
483508

@@ -497,6 +522,8 @@ protected function processFilters(?string $uri = null)
497522
$uri = strtolower(trim($uri, '/ '));
498523

499524
// Add any filters that apply to this URI
525+
$filters = [];
526+
500527
foreach ($this->config->filters as $alias => $settings) {
501528
// Look for inclusion rules
502529
if (isset($settings['before'])) {
@@ -506,7 +533,7 @@ protected function processFilters(?string $uri = null)
506533
// Get arguments and clean name
507534
[$name, $arguments] = $this->getCleanName($alias);
508535

509-
$this->filters['before'][] = $name;
536+
$filters['before'][] = $name;
510537

511538
$this->registerArguments($name, $arguments);
512539
}
@@ -519,14 +546,30 @@ protected function processFilters(?string $uri = null)
519546
// Get arguments and clean name
520547
[$name, $arguments] = $this->getCleanName($alias);
521548

522-
$this->filters['after'][] = $name;
549+
$filters['after'][] = $name;
523550

524551
// The arguments may have already been registered in the before filter.
525552
// So disable check.
526553
$this->registerArguments($name, $arguments, false);
527554
}
528555
}
529556
}
557+
558+
if (isset($filters['before'])) {
559+
if (config(Feature::class)->oldFilterOrder) {
560+
$this->filters['before'] = array_merge($this->filters['before'], $filters['before']);
561+
} else {
562+
$this->filters['before'] = array_merge($filters['before'], $this->filters['before']);
563+
}
564+
}
565+
566+
if (isset($filters['after'])) {
567+
if (! config(Feature::class)->oldFilterOrder) {
568+
$filters['after'] = array_reverse($filters['after']);
569+
}
570+
571+
$this->filters['after'] = array_merge($this->filters['after'], $filters['after']);
572+
}
530573
}
531574

532575
/**
@@ -563,6 +606,8 @@ private function registerArguments(string $name, array $arguments, bool $check =
563606
*/
564607
protected function processAliasesToClass(string $position)
565608
{
609+
$filtersClass = [];
610+
566611
foreach ($this->filters[$position] as $alias => $rules) {
567612
if (is_numeric($alias) && is_string($rules)) {
568613
$alias = $rules;
@@ -573,14 +618,20 @@ protected function processAliasesToClass(string $position)
573618
}
574619

575620
if (is_array($this->config->aliases[$alias])) {
576-
$this->filtersClass[$position] = array_merge($this->filtersClass[$position], $this->config->aliases[$alias]);
621+
$filtersClass = array_merge($filtersClass, $this->config->aliases[$alias]);
577622
} else {
578-
$this->filtersClass[$position][] = $this->config->aliases[$alias];
623+
$filtersClass[] = $this->config->aliases[$alias];
579624
}
580625
}
581626

582627
// when using enableFilter() we already write the class name in $filtersClass as well as the
583628
// alias in $filters. This leads to duplicates when using route filters.
629+
if ($position === 'before') {
630+
$this->filtersClass[$position] = array_merge($filtersClass, $this->filtersClass[$position]);
631+
} else {
632+
$this->filtersClass[$position] = array_merge($this->filtersClass[$position], $filtersClass);
633+
}
634+
584635
// Since some filters like rate limiters rely on being executed once a request we filter em here.
585636
$this->filtersClass[$position] = array_values(array_unique($this->filtersClass[$position]));
586637
}

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

Lines changed: 132 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use CodeIgniter\Router\Router;
2424
use CodeIgniter\Test\CIUnitTestCase;
2525
use CodeIgniter\Test\ConfigFromArrayTrait;
26+
use Config\Feature;
2627
use Config\Filters as FiltersConfig;
2728
use Config\Modules;
2829
use Config\Routing;
@@ -145,7 +146,7 @@ public function testFindGlobalsAndRouteFilters(): void
145146
$filters = $finder->find('admin');
146147

147148
$expected = [
148-
'before' => ['honeypot', 'csrf'],
149+
'before' => ['csrf', 'honeypot'],
149150
'after' => ['honeypot', 'toolbar'],
150151
];
151152
$this->assertSame($expected, $filters);
@@ -163,7 +164,7 @@ public function testFindGlobalsAndRouteClassnameFilters(): void
163164
$filters = $finder->find('admin');
164165

165166
$expected = [
166-
'before' => [InvalidChars::class, 'csrf'],
167+
'before' => ['csrf', InvalidChars::class],
167168
'after' => [InvalidChars::class, 'toolbar'],
168169
];
169170
$this->assertSame($expected, $filters);
@@ -181,8 +182,135 @@ public function testFindGlobalsAndRouteMultipleFilters(): void
181182
$filters = $finder->find('admin');
182183

183184
$expected = [
184-
'before' => ['honeypot', InvalidChars::class, 'csrf'],
185-
'after' => ['honeypot', InvalidChars::class, 'toolbar'],
185+
'before' => ['csrf', 'honeypot', InvalidChars::class],
186+
'after' => [InvalidChars::class, 'honeypot', 'toolbar'],
187+
];
188+
$this->assertSame($expected, $filters);
189+
}
190+
191+
public function testFilterOrder()
192+
{
193+
$collection = $this->createRouteCollection([]);
194+
$collection->get('/', ' Home::index', ['filter' => ['route1', 'route2']]);
195+
$router = $this->createRouter($collection);
196+
$filters = $this->createFilters([
197+
'aliases' => [
198+
'global1' => 'Dummy',
199+
'global2' => 'Dummy',
200+
'method1' => 'Dummy',
201+
'method2' => 'Dummy',
202+
'filter1' => 'Dummy',
203+
'filter2' => 'Dummy',
204+
'route1' => 'Dummy',
205+
'route2' => 'Dummy',
206+
],
207+
'globals' => [
208+
'before' => [
209+
'global1',
210+
'global2',
211+
],
212+
'after' => [
213+
'global2',
214+
'global1',
215+
],
216+
],
217+
'methods' => [
218+
'get' => ['method1', 'method2'],
219+
],
220+
'filters' => [
221+
'filter1' => ['before' => '*', 'after' => '*'],
222+
'filter2' => ['before' => '*', 'after' => '*'],
223+
],
224+
]);
225+
226+
$finder = new FilterFinder($router, $filters);
227+
228+
$filters = $finder->find('/');
229+
230+
$expected = [
231+
'before' => [
232+
'global1',
233+
'global2',
234+
'method1',
235+
'method2',
236+
'filter1',
237+
'filter2',
238+
'route1',
239+
'route2',
240+
],
241+
'after' => [
242+
'route2',
243+
'route1',
244+
'filter2',
245+
'filter1',
246+
'global2',
247+
'global1',
248+
],
249+
];
250+
$this->assertSame($expected, $filters);
251+
}
252+
253+
public function testFilterOrderWithOldFilterOrder()
254+
{
255+
$feature = config(Feature::class);
256+
$feature->oldFilterOrder = true;
257+
258+
$collection = $this->createRouteCollection([]);
259+
$collection->get('/', ' Home::index', ['filter' => ['route1', 'route2']]);
260+
$router = $this->createRouter($collection);
261+
$filters = $this->createFilters([
262+
'aliases' => [
263+
'global1' => 'Dummy',
264+
'global2' => 'Dummy',
265+
'method1' => 'Dummy',
266+
'method2' => 'Dummy',
267+
'filter1' => 'Dummy',
268+
'filter2' => 'Dummy',
269+
'route1' => 'Dummy',
270+
'route2' => 'Dummy',
271+
],
272+
'globals' => [
273+
'before' => [
274+
'global1',
275+
'global2',
276+
],
277+
'after' => [
278+
'global1',
279+
'global2',
280+
],
281+
],
282+
'methods' => [
283+
'get' => ['method1', 'method2'],
284+
],
285+
'filters' => [
286+
'filter1' => ['before' => '*', 'after' => '*'],
287+
'filter2' => ['before' => '*', 'after' => '*'],
288+
],
289+
]);
290+
291+
$finder = new FilterFinder($router, $filters);
292+
293+
$filters = $finder->find('/');
294+
295+
$expected = [
296+
'before' => [
297+
'route1',
298+
'route2',
299+
'global1',
300+
'global2',
301+
'method1',
302+
'method2',
303+
'filter1',
304+
'filter2',
305+
],
306+
'after' => [
307+
'route1',
308+
'route2',
309+
'global1',
310+
'global2',
311+
'filter1',
312+
'filter2',
313+
],
186314
];
187315
$this->assertSame($expected, $filters);
188316
}

0 commit comments

Comments
 (0)