diff --git a/system/Router/Attributes/Filter.php b/system/Router/Attributes/Filter.php index 33794e1711bf..130d2946f0dc 100644 --- a/system/Router/Attributes/Filter.php +++ b/system/Router/Attributes/Filter.php @@ -62,6 +62,6 @@ public function getFilters(): array return [$this->by]; } - return [$this->by => $this->having]; + return [$this->by . ':' . implode(',', $this->having)]; } } diff --git a/tests/_support/Router/Controllers/AttributeController.php b/tests/_support/Router/Controllers/AttributeController.php index 0c43404a2a8e..c0421ab9d601 100644 --- a/tests/_support/Router/Controllers/AttributeController.php +++ b/tests/_support/Router/Controllers/AttributeController.php @@ -41,6 +41,17 @@ public function filtered(): ResponseInterface return $this->response->setBody('Filtered: ' . $body); } + /** + * Test method with Filter attribute with parameters + */ + #[Filter(by: 'testAttributeFilter', having: ['arg1', 'arg2'])] + public function filteredWithParams(): ResponseInterface + { + $body = $this->request->getBody(); + + return $this->response->setBody('Filtered: ' . $body); + } + /** * Test method with Restrict attribute (environment) */ diff --git a/tests/_support/Router/Filters/TestAttributeFilter.php b/tests/_support/Router/Filters/TestAttributeFilter.php index dbafc230ec3e..a77ef72c95a7 100644 --- a/tests/_support/Router/Filters/TestAttributeFilter.php +++ b/tests/_support/Router/Filters/TestAttributeFilter.php @@ -21,17 +21,23 @@ class TestAttributeFilter implements FilterInterface { public function before(RequestInterface $request, $arguments = null) { + if ($arguments !== null) { + $arguments = '(' . implode(',', $arguments) . ')'; + } // Modify request body to indicate filter ran - $request->setBody('before_filter_ran:'); + $request->setBody('before_filter_ran' . $arguments . ':'); return $request; } public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) { + if ($arguments !== null) { + $arguments = '(' . implode(',', $arguments) . ')'; + } // Append to response body to indicate filter ran $body = $response->getBody(); - $response->setBody($body . ':after_filter_ran'); + $response->setBody($body . ':after_filter_ran' . $arguments); return $response; } diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index e3acab514cea..05e20cd7b361 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -1062,6 +1062,34 @@ public function testRouteAttributeFilterIntegration(): void $this->assertStringContainsString(':after_filter_ran', (string) $output); } + public function testRouteAttributeFilterWithParamsIntegration(): void + { + $_SERVER['argv'] = ['index.php', 'attribute/filteredWithParams']; + $_SERVER['argc'] = 2; + + Services::superglobals()->setServer('REQUEST_URI', '/attribute/filteredWithParams'); + Services::superglobals()->setServer('SCRIPT_NAME', '/index.php'); + + // Register the test filter + $filterConfig = config('Filters'); + $filterConfig->aliases['testAttributeFilter'] = TestAttributeFilter::class; + service('filters', $filterConfig); + + // Inject mock router + $routes = service('routes'); + $routes->get('attribute/filteredWithParams', '\Tests\Support\Router\Controllers\AttributeController::filteredWithParams'); + $router = service('router', $routes, service('incomingrequest')); + Services::injectMock('router', $router); + + ob_start(); + $this->codeigniter->run(); + $output = ob_get_clean(); + + // Verify filter ran before (modified request body) and after (appended to response) + $this->assertStringContainsString('Filtered: before_filter_ran(arg1,arg2):', (string) $output); + $this->assertStringContainsString(':after_filter_ran(arg1,arg2)', (string) $output); + } + public function testRouteAttributeRestrictIntegration(): void { $_SERVER['argv'] = ['index.php', 'attribute/restricted']; diff --git a/tests/system/Router/Attributes/FilterTest.php b/tests/system/Router/Attributes/FilterTest.php index 8a230c805287..33ee3ca89d19 100644 --- a/tests/system/Router/Attributes/FilterTest.php +++ b/tests/system/Router/Attributes/FilterTest.php @@ -90,8 +90,7 @@ public function testGetFiltersReturnsArrayWithFilterNameAndArguments(): void $filters = $filter->getFilters(); $this->assertCount(1, $filters); - $this->assertArrayHasKey('auth', $filters); - $this->assertSame(['admin'], $filters['auth']); + $this->assertSame('auth:admin', $filters[0]); } public function testGetFiltersReturnsArrayWithMultipleArguments(): void @@ -101,8 +100,7 @@ public function testGetFiltersReturnsArrayWithMultipleArguments(): void $filters = $filter->getFilters(); $this->assertCount(1, $filters); - $this->assertArrayHasKey('permission', $filters); - $this->assertSame(['posts.edit', 'posts.delete'], $filters['permission']); + $this->assertSame('permission:posts.edit,posts.delete', $filters[0]); } public function testGetFiltersWithEmptyHavingReturnsSimpleArray(): void @@ -135,13 +133,13 @@ public function testGetFiltersFormatIsConsistentAcrossInstances(): void $filters1 = $filterWithoutArgs->getFilters(); $filters2 = $filterWithArgs->getFilters(); - // Without args: simple array - $this->assertArrayNotHasKey('filter1', $filters1); - $this->assertContains('filter1', $filters1); + // Without args: + $this->assertCount(1, $filters1); + $this->assertSame('filter1', $filters1[0]); - // With args: associative array - $this->assertArrayHasKey('filter2', $filters2); - $this->assertIsArray($filters2['filter2']); + // With args: + $this->assertCount(1, $filters2); + $this->assertSame('filter2:arg1', $filters2[0]); } public function testFilterWithNumericArguments(): void @@ -150,8 +148,8 @@ public function testFilterWithNumericArguments(): void $filters = $filter->getFilters(); - $this->assertArrayHasKey('rate_limit', $filters); - $this->assertSame([100, 60], $filters['rate_limit']); + $this->assertCount(1, $filters); + $this->assertSame('rate_limit:100,60', $filters[0]); } public function testFilterWithMixedTypeArguments(): void @@ -160,8 +158,8 @@ public function testFilterWithMixedTypeArguments(): void $filters = $filter->getFilters(); - $this->assertArrayHasKey('custom', $filters); - $this->assertSame(['string', 123, true], $filters['custom']); + $this->assertCount(1, $filters); + $this->assertSame('custom:string,123,1', $filters[0]); } public function testFilterWithAssociativeArrayArguments(): void @@ -170,8 +168,8 @@ public function testFilterWithAssociativeArrayArguments(): void $filters = $filter->getFilters(); - $this->assertArrayHasKey('configured', $filters); - $this->assertSame(['option1' => 'value1', 'option2' => 'value2'], $filters['configured']); + $this->assertCount(1, $filters); + $this->assertSame('configured:value1,value2', $filters[0]); } public function testBeforeDoesNotModifyRequest(): void diff --git a/user_guide_src/source/incoming/controller_attributes.rst b/user_guide_src/source/incoming/controller_attributes.rst index 10e93c37cddf..842337dcf1b7 100644 --- a/user_guide_src/source/incoming/controller_attributes.rst +++ b/user_guide_src/source/incoming/controller_attributes.rst @@ -44,11 +44,15 @@ The ``Filters`` attribute allows you to specify one or more filters to be applie When filters are applied both by an attribute and in the filter configuration file, they will both be applied, but that could lead to unexpected results. +.. note:: + + Please remember that every parameter applied to the filter will be converted to a string. This behavior affects only filters. + Restrict -------- The ``Restrict`` attribute allows you to restrict access to the class or method based on the domain, the sub-domain, or -the environment the application is running in. Here's an exmaple of how to use the ``Restrict`` attribute: +the environment the application is running in. Here's an example of how to use the ``Restrict`` attribute: .. literalinclude:: controller_attributes/004.php