diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 6bf5b5046c72..33c035cc3e5c 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -154,6 +154,11 @@ class CodeIgniter */ protected ?string $context = null; + /** + * Whether to enable Control Filters. + */ + protected bool $enableFilters = true; + /** * Constructor. */ @@ -401,6 +406,14 @@ private function isWeb(): bool return $this->context === 'web'; } + /** + * Disables Controller Filters. + */ + public function disableFilters(): void + { + $this->enableFilters = false; + } + /** * Handles the main request logic and fires the controller. * @@ -415,35 +428,37 @@ protected function handleRequest(?RouteCollectionInterface $routes, Cache $cache $uri = $this->determinePath(); - // Start up the filters - $filters = Services::filters(); - - // If any filters were specified within the routes file, - // we need to ensure it's active for the current request - if ($routeFilter !== null) { - $multipleFiltersEnabled = config('Feature')->multipleFilters ?? false; - if ($multipleFiltersEnabled) { - $filters->enableFilters($routeFilter, 'before'); - $filters->enableFilters($routeFilter, 'after'); - } else { - // for backward compatibility - $filters->enableFilter($routeFilter, 'before'); - $filters->enableFilter($routeFilter, 'after'); + if ($this->enableFilters) { + // Start up the filters + $filters = Services::filters(); + + // If any filters were specified within the routes file, + // we need to ensure it's active for the current request + if ($routeFilter !== null) { + $multipleFiltersEnabled = config('Feature')->multipleFilters ?? false; + if ($multipleFiltersEnabled) { + $filters->enableFilters($routeFilter, 'before'); + $filters->enableFilters($routeFilter, 'after'); + } else { + // for backward compatibility + $filters->enableFilter($routeFilter, 'before'); + $filters->enableFilter($routeFilter, 'after'); + } } - } - // Run "before" filters - $this->benchmark->start('before_filters'); - $possibleResponse = $filters->run($uri, 'before'); - $this->benchmark->stop('before_filters'); + // Run "before" filters + $this->benchmark->start('before_filters'); + $possibleResponse = $filters->run($uri, 'before'); + $this->benchmark->stop('before_filters'); - // If a ResponseInterface instance is returned then send it back to the client and stop - if ($possibleResponse instanceof ResponseInterface) { - return $returnResponse ? $possibleResponse : $possibleResponse->send(); - } + // If a ResponseInterface instance is returned then send it back to the client and stop + if ($possibleResponse instanceof ResponseInterface) { + return $returnResponse ? $possibleResponse : $possibleResponse->send(); + } - if ($possibleResponse instanceof Request) { - $this->request = $possibleResponse; + if ($possibleResponse instanceof Request) { + $this->request = $possibleResponse; + } } $returned = $this->startController(); @@ -470,22 +485,28 @@ protected function handleRequest(?RouteCollectionInterface $routes, Cache $cache // so it can be used with the output. $this->gatherOutput($cacheConfig, $returned); - $filters->setResponse($this->response); + if ($this->enableFilters) { + $filters = Services::filters(); + $filters->setResponse($this->response); - // After filter debug toolbar requires 'total_execution'. - $this->totalTime = $this->benchmark->getElapsedTime('total_execution'); + // After filter debug toolbar requires 'total_execution'. + $this->totalTime = $this->benchmark->getElapsedTime('total_execution'); - // Run "after" filters - $this->benchmark->start('after_filters'); - $response = $filters->run($uri, 'after'); - $this->benchmark->stop('after_filters'); + // Run "after" filters + $this->benchmark->start('after_filters'); + $response = $filters->run($uri, 'after'); + $this->benchmark->stop('after_filters'); - if ($response instanceof ResponseInterface) { - $this->response = $response; + if ($response instanceof ResponseInterface) { + $this->response = $response; + } } // Skip unnecessary processing for special Responses. - if (! $response instanceof DownloadResponse && ! $response instanceof RedirectResponse) { + if ( + ! $this->response instanceof DownloadResponse + && ! $this->response instanceof RedirectResponse + ) { // Cache it without the performance metrics replaced // so that we can have live speed updates along the way. // Must be run after filters to preserve the Response headers. diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index ca95d403d6d5..418b79f14af7 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -245,6 +245,33 @@ public function testControllersRunFilterByClassName() $this->resetServices(); } + public function testDisableControllerFilters() + { + $_SERVER['argv'] = ['index.php', 'pages/about']; + $_SERVER['argc'] = 2; + + $_SERVER['REQUEST_URI'] = '/pages/about'; + + // Inject mock router. + $routes = Services::routes(); + $routes->add( + 'pages/about', + static fn () => Services::incomingrequest()->getBody(), + ['filter' => Customfilter::class] + ); + $router = Services::router($routes, Services::incomingrequest()); + Services::injectMock('router', $router); + + ob_start(); + $this->codeigniter->disableFilters(); + $this->codeigniter->run(); + $output = ob_get_clean(); + + $this->assertStringContainsString('', $output); + + $this->resetServices(); + } + public function testResponseConfigEmpty() { $_SERVER['argv'] = ['index.php', '/'];