Skip to content

Commit b898685

Browse files
committed
feat!: can execute a filter more than once with differnet arguments
Changes array structures of $filters, $filtersClass properties.
1 parent d57c2fc commit b898685

File tree

4 files changed

+109
-137
lines changed

4 files changed

+109
-137
lines changed

phpstan-baseline.php

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5323,18 +5323,6 @@
53235323
'count' => 1,
53245324
'path' => __DIR__ . '/system/Filters/Filters.php',
53255325
];
5326-
$ignoreErrors[] = [
5327-
// identifier: missingType.iterableValue
5328-
'message' => '#^Method CodeIgniter\\\\Filters\\\\Filters\\:\\:getFilters\\(\\) return type has no value type specified in iterable type array\\.$#',
5329-
'count' => 1,
5330-
'path' => __DIR__ . '/system/Filters/Filters.php',
5331-
];
5332-
$ignoreErrors[] = [
5333-
// identifier: missingType.iterableValue
5334-
'message' => '#^Method CodeIgniter\\\\Filters\\\\Filters\\:\\:getFiltersClass\\(\\) return type has no value type specified in iterable type array\\.$#',
5335-
'count' => 1,
5336-
'path' => __DIR__ . '/system/Filters/Filters.php',
5337-
];
53385326
$ignoreErrors[] = [
53395327
// identifier: missingType.iterableValue
53405328
'message' => '#^Method CodeIgniter\\\\Filters\\\\Filters\\:\\:getRequiredFilters\\(\\) return type has no value type specified in iterable type array\\.$#',
@@ -5347,12 +5335,6 @@
53475335
'count' => 1,
53485336
'path' => __DIR__ . '/system/Filters/Filters.php',
53495337
];
5350-
$ignoreErrors[] = [
5351-
// identifier: missingType.iterableValue
5352-
'message' => '#^Method CodeIgniter\\\\Filters\\\\Filters\\:\\:registerArguments\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#',
5353-
'count' => 1,
5354-
'path' => __DIR__ . '/system/Filters/Filters.php',
5355-
];
53565338
$ignoreErrors[] = [
53575339
// identifier: missingType.iterableValue
53585340
'message' => '#^Method CodeIgniter\\\\Filters\\\\Filters\\:\\:runAfter\\(\\) has parameter \\$filterClasses with no value type specified in iterable type array\\.$#',
@@ -5377,18 +5359,6 @@
53775359
'count' => 1,
53785360
'path' => __DIR__ . '/system/Filters/Filters.php',
53795361
];
5380-
$ignoreErrors[] = [
5381-
// identifier: missingType.iterableValue
5382-
'message' => '#^Property CodeIgniter\\\\Filters\\\\Filters\\:\\:\\$filters type has no value type specified in iterable type array\\.$#',
5383-
'count' => 1,
5384-
'path' => __DIR__ . '/system/Filters/Filters.php',
5385-
];
5386-
$ignoreErrors[] = [
5387-
// identifier: missingType.iterableValue
5388-
'message' => '#^Property CodeIgniter\\\\Filters\\\\Filters\\:\\:\\$filtersClass type has no value type specified in iterable type array\\.$#',
5389-
'count' => 1,
5390-
'path' => __DIR__ . '/system/Filters/Filters.php',
5391-
];
53925362
$ignoreErrors[] = [
53935363
// identifier: missingType.iterableValue
53945364
'message' => '#^Method CodeIgniter\\\\Filters\\\\ForceHTTPS\\:\\:after\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#',

system/Filters/Filters.php

Lines changed: 95 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
namespace CodeIgniter\Filters;
1515

1616
use CodeIgniter\Config\Filters as BaseFiltersConfig;
17-
use CodeIgniter\Exceptions\ConfigException;
1817
use CodeIgniter\Filters\Exceptions\FilterException;
1918
use CodeIgniter\HTTP\RequestInterface;
2019
use CodeIgniter\HTTP\ResponseInterface;
@@ -66,25 +65,53 @@ class Filters
6665
protected $initialized = false;
6766

6867
/**
69-
* The processed filters that will
70-
* be used to check against.
68+
* The filter list to execute for the current request (URI path).
7169
*
70+
* This property is for display. Use $filtersClass to execute filters.
7271
* This does not include "Required Filters".
7372
*
74-
* @var array<string, array>
73+
* [
74+
* 'before' => [
75+
* 'alias',
76+
* 'alias:arg1',
77+
* 'alias:arg1,arg2',
78+
* ],
79+
* 'after' => [
80+
* 'alias',
81+
* 'alias:arg1',
82+
* 'alias:arg1,arg2',
83+
* ],
84+
* ]
85+
*
86+
* @var array{
87+
* before: list<string>,
88+
* after: list<string>
89+
* }
7590
*/
7691
protected $filters = [
7792
'before' => [],
7893
'after' => [],
7994
];
8095

8196
/**
82-
* The collection of filters' class names that will
83-
* be used to execute in each position.
97+
* The collection of filters' class names to execute for the current request
98+
* (URI path).
8499
*
85100
* This does not include "Required Filters".
86101
*
87-
* @var array<string, array>
102+
* [
103+
* 'before' => [
104+
* [classname, arguments],
105+
* ],
106+
* 'after' => [
107+
* [classname, arguments],
108+
* ],
109+
* ]
110+
*
111+
* @var array{
112+
* before: list<array{0: class-string, 1: list<string>}>,
113+
* after: list<array{0: class-string, 1: list<string>}>
114+
* }
88115
*/
89116
protected $filtersClass = [
90117
'before' => [],
@@ -95,13 +122,17 @@ class Filters
95122
* Any arguments to be passed to filters.
96123
*
97124
* @var array<string, list<string>|null> [name => params]
125+
*
126+
* @deprecated 4.6.0 No longer used.
98127
*/
99128
protected $arguments = [];
100129

101130
/**
102131
* Any arguments to be passed to filtersClass.
103132
*
104133
* @var array<class-string, list<string>|null> [classname => arguments]
134+
*
135+
* @deprecated 4.6.0 No longer used.
105136
*/
106137
protected $argumentsClass = [];
107138

@@ -193,17 +224,17 @@ public function run(string $uri, string $position = 'before')
193224
*/
194225
private function runBefore(array $filterClasses)
195226
{
196-
foreach ($filterClasses as $className) {
227+
foreach ($filterClasses as $filterClassInfo) {
228+
$className = $filterClassInfo[0];
229+
$arguments = ($filterClassInfo[1] === []) ? null : $filterClassInfo[1];
230+
197231
$class = new $className();
198232

199233
if (! $class instanceof FilterInterface) {
200234
throw FilterException::forIncorrectInterface($class::class);
201235
}
202236

203-
$result = $class->before(
204-
$this->request,
205-
$this->argumentsClass[$className] ?? null
206-
);
237+
$result = $class->before($this->request, $arguments);
207238

208239
if ($result instanceof RequestInterface) {
209240
$this->request = $result;
@@ -231,18 +262,17 @@ private function runBefore(array $filterClasses)
231262

232263
private function runAfter(array $filterClasses): ResponseInterface
233264
{
234-
foreach ($filterClasses as $className) {
265+
foreach ($filterClasses as $filterClassInfo) {
266+
$className = $filterClassInfo[0];
267+
$arguments = ($filterClassInfo[1] === []) ? null : $filterClassInfo[1];
268+
235269
$class = new $className();
236270

237271
if (! $class instanceof FilterInterface) {
238272
throw FilterException::forIncorrectInterface($class::class);
239273
}
240274

241-
$result = $class->after(
242-
$this->request,
243-
$this->response,
244-
$this->argumentsClass[$className] ?? null
245-
);
275+
$result = $class->after($this->request, $this->response, $arguments);
246276

247277
if ($result instanceof ResponseInterface) {
248278
$this->response = $result;
@@ -277,9 +307,11 @@ public function runRequired(string $position = 'before')
277307

278308
foreach ($filters as $alias) {
279309
if (is_array($aliases[$alias])) {
280-
$filterClasses[$position] = array_merge($filterClasses[$position], $aliases[$alias]);
310+
foreach ($this->config->aliases[$alias] as $class) {
311+
$filterClasses[$position][] = [$class, []];
312+
}
281313
} else {
282-
$filterClasses[$position][] = $aliases[$alias];
314+
$filterClasses[$position][] = [$aliases[$alias], []];
283315
}
284316
}
285317

@@ -406,6 +438,11 @@ public function initialize(?string $uri = null)
406438
// Set the toolbar filter to the last position to be executed
407439
$this->filters['after'] = $this->setToolbarToLast($this->filters['after']);
408440

441+
// Since some filters like rate limiters rely on being executed once a request,
442+
// we filter em here.
443+
$this->filters['before'] = array_unique($this->filters['before']);
444+
$this->filters['after'] = array_unique($this->filters['after']);
445+
409446
$this->processAliasesToClass('before');
410447
$this->processAliasesToClass('after');
411448

@@ -436,6 +473,11 @@ public function reset(): self
436473
/**
437474
* Returns the processed filters array.
438475
* This does not include "Required Filters".
476+
*
477+
* @return array{
478+
* before: list<string>,
479+
* after: list<string>
480+
* }
439481
*/
440482
public function getFilters(): array
441483
{
@@ -445,6 +487,11 @@ public function getFilters(): array
445487
/**
446488
* Returns the filtersClass array.
447489
* This does not include "Required Filters".
490+
*
491+
* @return array{
492+
* before: list<array{0: class-string, 1: list<string>}>,
493+
* after: list<array{0: class-string, 1: list<string>}>
494+
* }
448495
*/
449496
public function getFiltersClass(): array
450497
{
@@ -485,29 +532,31 @@ public function addFilter(string $class, ?string $alias = null, string $when = '
485532
* are passed to the filter when executed.
486533
*
487534
* @param string $name filter_name or filter_name:arguments like 'role:admin,manager'
535+
* or filter classname.
488536
*/
489537
private function enableFilter(string $name, string $when = 'before'): void
490538
{
491-
// Get arguments and clean name
492-
[$name, $arguments] = $this->getCleanName($name);
493-
$this->arguments[$name] = ($arguments !== []) ? $arguments : null;
494-
495-
if (class_exists($name)) {
496-
$this->config->aliases[$name] = $name;
497-
} elseif (! array_key_exists($name, $this->config->aliases)) {
498-
throw FilterException::forNoAlias($name);
539+
// Normalize the arguments.
540+
[$alias, $arguments] = $this->getCleanName($name);
541+
if ($arguments === []) {
542+
$filter = $alias;
543+
} else {
544+
$filter = $alias . ':' . implode(',', $arguments);
499545
}
500546

501-
$classNames = (array) $this->config->aliases[$name];
502-
503-
foreach ($classNames as $className) {
504-
$this->argumentsClass[$className] = $this->arguments[$name] ?? null;
547+
if (class_exists($alias)) {
548+
$this->config->aliases[$alias] = $alias;
549+
} elseif (! array_key_exists($alias, $this->config->aliases)) {
550+
throw FilterException::forNoAlias($alias);
505551
}
506552

507-
if (! isset($this->filters[$when][$name])) {
508-
$this->filters[$when][] = $name;
509-
$this->filtersClass[$when] = array_merge($this->filtersClass[$when], $classNames);
553+
if (! isset($this->filters[$when][$filter])) {
554+
$this->filters[$when][] = $filter;
510555
}
556+
557+
// Since some filters like rate limiters rely on being executed once a request,
558+
// we filter em here.
559+
$this->filters[$when] = array_unique($this->filters[$when]);
511560
}
512561

513562
/**
@@ -557,6 +606,8 @@ public function enableFilters(array $names, string $when = 'before')
557606
* Returns the arguments for a specified key, or all.
558607
*
559608
* @return array<string, string>|string
609+
*
610+
* @deprecated 4.6.0 Already does not work.
560611
*/
561612
public function getArguments(?string $key = null)
562613
{
@@ -692,27 +743,15 @@ protected function processFilters(?string $uri = null)
692743
$path = $settings['before'];
693744

694745
if ($this->pathApplies($uri, $path)) {
695-
// Get arguments and clean name
696-
[$name, $arguments] = $this->getCleanName($alias);
697-
698-
$filters['before'][] = $name;
699-
700-
$this->registerArguments($name, $arguments);
746+
$filters['before'][] = $alias;
701747
}
702748
}
703749

704750
if (isset($settings['after'])) {
705751
$path = $settings['after'];
706752

707753
if ($this->pathApplies($uri, $path)) {
708-
// Get arguments and clean name
709-
[$name, $arguments] = $this->getCleanName($alias);
710-
711-
$filters['after'][] = $name;
712-
713-
// The arguments may have already been registered in the before filter.
714-
// So disable check.
715-
$this->registerArguments($name, $arguments, false);
754+
$filters['after'][] = $alias;
716755
}
717756
}
718757
}
@@ -736,31 +775,6 @@ protected function processFilters(?string $uri = null)
736775
}
737776
}
738777

739-
/**
740-
* @param string $name filter alias
741-
* @param array $arguments filter arguments
742-
* @param bool $check if true, check if already defined
743-
*/
744-
private function registerArguments(string $name, array $arguments, bool $check = true): void
745-
{
746-
if ($arguments !== []) {
747-
if ($check && array_key_exists($name, $this->arguments)) {
748-
throw new ConfigException(
749-
'"' . $name . '" already has arguments: '
750-
. (($this->arguments[$name] === null) ? 'null' : implode(',', $this->arguments[$name]))
751-
);
752-
}
753-
754-
$this->arguments[$name] = $arguments;
755-
}
756-
757-
$classNames = (array) $this->config->aliases[$name];
758-
759-
foreach ($classNames as $className) {
760-
$this->argumentsClass[$className] = $this->arguments[$name] ?? null;
761-
}
762-
}
763-
764778
/**
765779
* Maps filter aliases to the equivalent filter classes
766780
*
@@ -772,32 +786,28 @@ protected function processAliasesToClass(string $position)
772786
{
773787
$filterClasses = [];
774788

775-
foreach ($this->filters[$position] as $alias => $rules) {
776-
if (is_numeric($alias) && is_string($rules)) {
777-
$alias = $rules;
778-
}
789+
foreach ($this->filters[$position] as $filter) {
790+
// Get arguments and clean alias
791+
[$alias, $arguments] = $this->getCleanName($filter);
779792

780793
if (! array_key_exists($alias, $this->config->aliases)) {
781794
throw FilterException::forNoAlias($alias);
782795
}
783796

784797
if (is_array($this->config->aliases[$alias])) {
785-
$filterClasses = [...$filterClasses, ...$this->config->aliases[$alias]];
798+
foreach ($this->config->aliases[$alias] as $class) {
799+
$filterClasses[] = [$class, $arguments];
800+
}
786801
} else {
787-
$filterClasses[] = $this->config->aliases[$alias];
802+
$filterClasses[] = [$this->config->aliases[$alias], $arguments];
788803
}
789804
}
790805

791-
// when using enableFilter() we already write the class name in $filterClasses as well as the
792-
// alias in $filters. This leads to duplicates when using route filters.
793806
if ($position === 'before') {
794807
$this->filtersClass[$position] = array_merge($filterClasses, $this->filtersClass[$position]);
795808
} else {
796809
$this->filtersClass[$position] = array_merge($this->filtersClass[$position], $filterClasses);
797810
}
798-
799-
// Since some filters like rate limiters rely on being executed once a request we filter em here.
800-
$this->filtersClass[$position] = array_values(array_unique($this->filtersClass[$position]));
801811
}
802812

803813
/**

0 commit comments

Comments
 (0)