Skip to content

Commit 6a64c4c

Browse files
authored
fix: controller attribute filters with parameters (codeigniter4#9769)
* fix: controller attribute filters with parameters * fix test
1 parent 05c7217 commit 6a64c4c

File tree

6 files changed

+67
-20
lines changed

6 files changed

+67
-20
lines changed

system/Router/Attributes/Filter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,6 @@ public function getFilters(): array
6262
return [$this->by];
6363
}
6464

65-
return [$this->by => $this->having];
65+
return [$this->by . ':' . implode(',', $this->having)];
6666
}
6767
}

tests/_support/Router/Controllers/AttributeController.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ public function filtered(): ResponseInterface
4141
return $this->response->setBody('Filtered: ' . $body);
4242
}
4343

44+
/**
45+
* Test method with Filter attribute with parameters
46+
*/
47+
#[Filter(by: 'testAttributeFilter', having: ['arg1', 'arg2'])]
48+
public function filteredWithParams(): ResponseInterface
49+
{
50+
$body = $this->request->getBody();
51+
52+
return $this->response->setBody('Filtered: ' . $body);
53+
}
54+
4455
/**
4556
* Test method with Restrict attribute (environment)
4657
*/

tests/_support/Router/Filters/TestAttributeFilter.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,23 @@ class TestAttributeFilter implements FilterInterface
2121
{
2222
public function before(RequestInterface $request, $arguments = null)
2323
{
24+
if ($arguments !== null) {
25+
$arguments = '(' . implode(',', $arguments) . ')';
26+
}
2427
// Modify request body to indicate filter ran
25-
$request->setBody('before_filter_ran:');
28+
$request->setBody('before_filter_ran' . $arguments . ':');
2629

2730
return $request;
2831
}
2932

3033
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
3134
{
35+
if ($arguments !== null) {
36+
$arguments = '(' . implode(',', $arguments) . ')';
37+
}
3238
// Append to response body to indicate filter ran
3339
$body = $response->getBody();
34-
$response->setBody($body . ':after_filter_ran');
40+
$response->setBody($body . ':after_filter_ran' . $arguments);
3541

3642
return $response;
3743
}

tests/system/CodeIgniterTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,34 @@ public function testRouteAttributeFilterIntegration(): void
10621062
$this->assertStringContainsString(':after_filter_ran', (string) $output);
10631063
}
10641064

1065+
public function testRouteAttributeFilterWithParamsIntegration(): void
1066+
{
1067+
$_SERVER['argv'] = ['index.php', 'attribute/filteredWithParams'];
1068+
$_SERVER['argc'] = 2;
1069+
1070+
Services::superglobals()->setServer('REQUEST_URI', '/attribute/filteredWithParams');
1071+
Services::superglobals()->setServer('SCRIPT_NAME', '/index.php');
1072+
1073+
// Register the test filter
1074+
$filterConfig = config('Filters');
1075+
$filterConfig->aliases['testAttributeFilter'] = TestAttributeFilter::class;
1076+
service('filters', $filterConfig);
1077+
1078+
// Inject mock router
1079+
$routes = service('routes');
1080+
$routes->get('attribute/filteredWithParams', '\Tests\Support\Router\Controllers\AttributeController::filteredWithParams');
1081+
$router = service('router', $routes, service('incomingrequest'));
1082+
Services::injectMock('router', $router);
1083+
1084+
ob_start();
1085+
$this->codeigniter->run();
1086+
$output = ob_get_clean();
1087+
1088+
// Verify filter ran before (modified request body) and after (appended to response)
1089+
$this->assertStringContainsString('Filtered: before_filter_ran(arg1,arg2):', (string) $output);
1090+
$this->assertStringContainsString(':after_filter_ran(arg1,arg2)', (string) $output);
1091+
}
1092+
10651093
public function testRouteAttributeRestrictIntegration(): void
10661094
{
10671095
$_SERVER['argv'] = ['index.php', 'attribute/restricted'];

tests/system/Router/Attributes/FilterTest.php

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,7 @@ public function testGetFiltersReturnsArrayWithFilterNameAndArguments(): void
9090
$filters = $filter->getFilters();
9191

9292
$this->assertCount(1, $filters);
93-
$this->assertArrayHasKey('auth', $filters);
94-
$this->assertSame(['admin'], $filters['auth']);
93+
$this->assertSame('auth:admin', $filters[0]);
9594
}
9695

9796
public function testGetFiltersReturnsArrayWithMultipleArguments(): void
@@ -101,8 +100,7 @@ public function testGetFiltersReturnsArrayWithMultipleArguments(): void
101100
$filters = $filter->getFilters();
102101

103102
$this->assertCount(1, $filters);
104-
$this->assertArrayHasKey('permission', $filters);
105-
$this->assertSame(['posts.edit', 'posts.delete'], $filters['permission']);
103+
$this->assertSame('permission:posts.edit,posts.delete', $filters[0]);
106104
}
107105

108106
public function testGetFiltersWithEmptyHavingReturnsSimpleArray(): void
@@ -135,13 +133,13 @@ public function testGetFiltersFormatIsConsistentAcrossInstances(): void
135133
$filters1 = $filterWithoutArgs->getFilters();
136134
$filters2 = $filterWithArgs->getFilters();
137135

138-
// Without args: simple array
139-
$this->assertArrayNotHasKey('filter1', $filters1);
140-
$this->assertContains('filter1', $filters1);
136+
// Without args:
137+
$this->assertCount(1, $filters1);
138+
$this->assertSame('filter1', $filters1[0]);
141139

142-
// With args: associative array
143-
$this->assertArrayHasKey('filter2', $filters2);
144-
$this->assertIsArray($filters2['filter2']);
140+
// With args:
141+
$this->assertCount(1, $filters2);
142+
$this->assertSame('filter2:arg1', $filters2[0]);
145143
}
146144

147145
public function testFilterWithNumericArguments(): void
@@ -150,8 +148,8 @@ public function testFilterWithNumericArguments(): void
150148

151149
$filters = $filter->getFilters();
152150

153-
$this->assertArrayHasKey('rate_limit', $filters);
154-
$this->assertSame([100, 60], $filters['rate_limit']);
151+
$this->assertCount(1, $filters);
152+
$this->assertSame('rate_limit:100,60', $filters[0]);
155153
}
156154

157155
public function testFilterWithMixedTypeArguments(): void
@@ -160,8 +158,8 @@ public function testFilterWithMixedTypeArguments(): void
160158

161159
$filters = $filter->getFilters();
162160

163-
$this->assertArrayHasKey('custom', $filters);
164-
$this->assertSame(['string', 123, true], $filters['custom']);
161+
$this->assertCount(1, $filters);
162+
$this->assertSame('custom:string,123,1', $filters[0]);
165163
}
166164

167165
public function testFilterWithAssociativeArrayArguments(): void
@@ -170,8 +168,8 @@ public function testFilterWithAssociativeArrayArguments(): void
170168

171169
$filters = $filter->getFilters();
172170

173-
$this->assertArrayHasKey('configured', $filters);
174-
$this->assertSame(['option1' => 'value1', 'option2' => 'value2'], $filters['configured']);
171+
$this->assertCount(1, $filters);
172+
$this->assertSame('configured:value1,value2', $filters[0]);
175173
}
176174

177175
public function testBeforeDoesNotModifyRequest(): void

user_guide_src/source/incoming/controller_attributes.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,15 @@ The ``Filters`` attribute allows you to specify one or more filters to be applie
4444

4545
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.
4646

47+
.. note::
48+
49+
Please remember that every parameter applied to the filter will be converted to a string. This behavior affects only filters.
50+
4751
Restrict
4852
--------
4953

5054
The ``Restrict`` attribute allows you to restrict access to the class or method based on the domain, the sub-domain, or
51-
the environment the application is running in. Here's an exmaple of how to use the ``Restrict`` attribute:
55+
the environment the application is running in. Here's an example of how to use the ``Restrict`` attribute:
5256

5357
.. literalinclude:: controller_attributes/004.php
5458

0 commit comments

Comments
 (0)