diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 8c02a4acd331..41e76267a448 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -15,8 +15,9 @@ class Filters extends BaseConfig * Configures aliases for Filter classes to * make reading things nicer and simpler. * - * @var array - * @phpstan-var array + * @var array|string> [filter_name => classname] + * or [filter_name => [classname1, classname2, ...]] + * @phpstan-var array> */ public array $aliases = [ 'csrf' => CSRF::class, diff --git a/system/Test/FilterTestTrait.php b/system/Test/FilterTestTrait.php index 4901a585ce20..084a6cd082fc 100644 --- a/system/Test/FilterTestTrait.php +++ b/system/Test/FilterTestTrait.php @@ -132,26 +132,68 @@ protected function getFilterCaller($filter, string $position): Closure throw new RuntimeException("No filter found with alias '{$filter}'"); } - $filter = $this->filtersConfig->aliases[$filter]; + $filterClasses = $this->filtersConfig->aliases[$filter]; } - // Get an instance - $filter = new $filter(); + $filterClasses = (array) $filterClasses; } - if (! $filter instanceof FilterInterface) { - throw FilterException::forIncorrectInterface(get_class($filter)); + foreach ($filterClasses as $class) { + // Get an instance + $filter = new $class(); + + if (! $filter instanceof FilterInterface) { + throw FilterException::forIncorrectInterface(get_class($filter)); + } } $request = clone $this->request; if ($position === 'before') { - return static fn (?array $params = null) => $filter->before($request, $params); + return static function (?array $params = null) use ($filterClasses, $request) { + foreach ($filterClasses as $class) { + $filter = new $class(); + + $result = $filter->before($request, $params); + + // @TODO The following logic is in Filters class. + // Should use Filters class. + if ($result instanceof RequestInterface) { + $request = $result; + + continue; + } + if ($result instanceof ResponseInterface) { + return $result; + } + if (empty($result)) { + continue; + } + } + + return $result; + }; } $response = clone $this->response; - return static fn (?array $params = null) => $filter->after($request, $response, $params); + return static function (?array $params = null) use ($filterClasses, $request, $response) { + foreach ($filterClasses as $class) { + $filter = new $class(); + + $result = $filter->after($request, $response, $params); + + // @TODO The following logic is in Filters class. + // Should use Filters class. + if ($result instanceof ResponseInterface) { + $response = $result; + + continue; + } + } + + return $result; + }; } /** diff --git a/tests/system/Test/FilterTestTraitTest.php b/tests/system/Test/FilterTestTraitTest.php index e2d5a423637f..bebec3857030 100644 --- a/tests/system/Test/FilterTestTraitTest.php +++ b/tests/system/Test/FilterTestTraitTest.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Test; use CodeIgniter\HTTP\RequestInterface; +use Config\Services; use Tests\Support\Filters\Customfilter; /** @@ -62,12 +63,23 @@ public function testGetCallerInvalidPosition(): void $this->getFilterCaller('test-customfilter', 'banana'); } + public function testCallerSupportArray(): void + { + $this->filtersConfig->aliases['test-customfilter'] = [Customfilter::class]; + + $caller = $this->getFilterCaller('test-customfilter', 'before'); + $result = $caller(); + + $this->assertSame('http://hellowworld.com', $result->getBody()); + } + public function testCallerUsesClonedInstance(): void { $caller = $this->getFilterCaller('test-customfilter', 'before'); $result = $caller(); $this->assertSame('http://hellowworld.com', $result->getBody()); + $this->assertNull(Services::response()->getBody()); $this->resetServices(); }