Skip to content

Commit d139cad

Browse files
committed
feat(app): Add config setting to use controller attributes or not
1 parent 663daeb commit d139cad

File tree

6 files changed

+93
-17
lines changed

6 files changed

+93
-17
lines changed

app/Config/Routing.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ class Routing extends BaseRouting
9696
*/
9797
public bool $autoRoute = false;
9898

99+
/**
100+
* If TRUE, the system will look for attributes on controller
101+
* class and methods that can run before and after the
102+
* controller/method.
103+
*
104+
* If FALSE, will ignore any attributes.
105+
*/
106+
public bool $useControllerAttributes = true;
107+
99108
/**
100109
* For Defined Routes.
101110
* If TRUE, will enable the use of the 'prioritize' option

system/CodeIgniter.php

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -502,9 +502,11 @@ protected function handleRequest(?RouteCollectionInterface $routes, Cache $cache
502502
}
503503

504504
// Execute controller attributes' after() methods AFTER framework filters
505-
$this->benchmark->start('route_attributes_after');
506-
$this->response = $this->router->executeAfterAttributes($this->request, $this->response);
507-
$this->benchmark->stop('route_attributes_after');
505+
if (config('routing')->useControllerAttributes === true) {
506+
$this->benchmark->start('route_attributes_after');
507+
$this->response = $this->router->executeAfterAttributes($this->request, $this->response);
508+
$this->benchmark->stop('route_attributes_after');
509+
}
508510

509511
// Skip unnecessary processing for special Responses.
510512
if (
@@ -866,21 +868,23 @@ protected function startController()
866868

867869
// Execute route attributes' before() methods
868870
// This runs after routing/validation but BEFORE expensive controller instantiation
869-
$this->benchmark->start('route_attributes_before');
870-
$attributeResponse = $this->router->executeBeforeAttributes($this->request);
871-
$this->benchmark->stop('route_attributes_before');
872-
873-
// If attribute returns a Response, short-circuit
874-
if ($attributeResponse instanceof ResponseInterface) {
875-
$this->benchmark->stop('controller_constructor');
876-
$this->benchmark->stop('controller');
871+
if (config('routing')->useControllerAttributes === true) {
872+
$this->benchmark->start('route_attributes_before');
873+
$attributeResponse = $this->router->executeBeforeAttributes($this->request);
874+
$this->benchmark->stop('route_attributes_before');
875+
876+
// If attribute returns a Response, short-circuit
877+
if ($attributeResponse instanceof ResponseInterface) {
878+
$this->benchmark->stop('controller_constructor');
879+
$this->benchmark->stop('controller');
877880

878-
return $attributeResponse;
879-
}
881+
return $attributeResponse;
882+
}
880883

881-
// If attribute returns a modified Request, use it
882-
if ($attributeResponse instanceof Request) {
883-
$this->request = $attributeResponse;
884+
// If attribute returns a modified Request, use it
885+
if ($attributeResponse instanceof Request) {
886+
$this->request = $attributeResponse;
887+
}
884888
}
885889

886890
return null;

system/Config/Routing.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ class Routing extends BaseConfig
9696
*/
9797
public bool $autoRoute = false;
9898

99+
/**
100+
* If TRUE, the system will look for attributes on controller
101+
* class and methods that can run before and after the
102+
* controller/method.
103+
*
104+
* If FALSE, will ignore any attributes.
105+
*/
106+
public bool $useControllerAttributes = true;
107+
99108
/**
100109
* For Defined Routes.
101110
* If TRUE, will enable the use of the 'prioritize' option

system/Router/Router.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,11 @@ private function processRouteAttributes(): void
779779
{
780780
$this->routeAttributes = ['class' => [], 'method' => []];
781781

782+
// Skip if controller attributes are disabled in config
783+
if (config('routing')->useControllerAttributes === false) {
784+
return;
785+
}
786+
782787
// Skip if controller is a Closure
783788
if ($this->controller instanceof Closure) {
784789
return;

tests/system/CodeIgniterTest.php

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace CodeIgniter;
1515

1616
use App\Controllers\Home;
17+
use CodeIgniter\Config\Factories;
1718
use CodeIgniter\Config\Services;
1819
use CodeIgniter\Debug\Timer;
1920
use CodeIgniter\Exceptions\PageNotFoundException;
@@ -43,7 +44,7 @@
4344
*/
4445
#[BackupGlobals(true)]
4546
#[Group('Others')]
46-
#[RunTestsInSeparateProcesses]
47+
// #[RunTestsInSeparateProcesses]
4748
final class CodeIgniterTest extends CIUnitTestCase
4849
{
4950
private CodeIgniter $codeigniter;
@@ -1187,4 +1188,47 @@ public function testRouteAttributeCustomCacheKeyIntegration(): void
11871188
// Clear cache after test
11881189
cache()->clean();
11891190
}
1191+
1192+
public function testRouteAttributesDisabledInConfig(): void
1193+
{
1194+
Services::superglobals()->setServer('REQUEST_URI', '/attribute/filtered');
1195+
Services::superglobals()->setServer('SCRIPT_NAME', '/index.php');
1196+
Services::superglobals()->setServer('REQUEST_METHOD', 'GET');
1197+
1198+
// Disable route attributes in config BEFORE creating CodeIgniter instance
1199+
$routing = config('routing');
1200+
$routing->useControllerAttributes = false;
1201+
Factories::injectMock('config', 'routing', $routing);
1202+
1203+
// Register the test filter (even though attributes are disabled,
1204+
// we need it registered to avoid FilterException)
1205+
$filterConfig = config('Filters');
1206+
$filterConfig->aliases['testAttributeFilter'] = TestAttributeFilter::class;
1207+
service('filters', $filterConfig);
1208+
1209+
$routes = service('routes');
1210+
$routes->setAutoRoute(false);
1211+
1212+
// We're testing that a route defined normally will work,
1213+
// but the attributes on the controller method won't be processed
1214+
$routes->get('attribute/filtered', '\Tests\Support\Router\Controllers\AttributeController::filtered');
1215+
1216+
$router = service('router', $routes, service('incomingrequest'));
1217+
Services::injectMock('router', $router);
1218+
1219+
$config = new App();
1220+
$codeigniter = new MockCodeIgniter($config);
1221+
1222+
ob_start();
1223+
$codeigniter->run($routes);
1224+
$output = ob_get_clean();
1225+
1226+
1227+
// When useRouteAttributes is false, the filter attributes should NOT be processed
1228+
// So the filter should not have run
1229+
$this->assertStringNotContainsString('before_filter_ran', (string) $output);
1230+
$this->assertStringNotContainsString('after_filter_ran', (string) $output);
1231+
// But the controller method should still execute
1232+
$this->assertStringContainsString('Filtered', (string) $output);
1233+
}
11901234
}

user_guide_src/source/incoming/controller_attributes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ You can also apply the ``Filters`` attribute to a specific method within a contr
2525

2626
Class-level and method-level attributes can work together to provide a flexible way to manage your routes at the controller level.
2727

28+
Disabling Attributes
29+
--------------------
30+
31+
If you know that you will not be using attributes in your application, you can disable the feature by setting the ``$useControllerAttributes`` property in your ``app/Config/Routing.php`` file to ``false``.
32+
2833
Provided Attributes
2934
*******************
3035

0 commit comments

Comments
 (0)