Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 34 additions & 5 deletions system/Router/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -803,8 +803,8 @@ private function processRouteAttributes(): void
if ($instance instanceof RouteAttributeInterface) {
$this->routeAttributes['class'][] = $instance;
}
} catch (Throwable) {
log_message('error', 'Failed to instantiate attribute: ' . $attribute->getName());
} catch (Throwable $e) {
$this->logRouteAttributeInstantiationFailure($attribute->getName(), $this->controller, null, $e);
}
}

Expand All @@ -823,14 +823,43 @@ private function processRouteAttributes(): void
if ($instance instanceof RouteAttributeInterface) {
$this->routeAttributes['method'][] = $instance;
}
} catch (Throwable) {
// Skip attributes that fail to instantiate
log_message('error', 'Failed to instantiate attribute: ' . $attribute->getName());
} catch (Throwable $e) {
$this->logRouteAttributeInstantiationFailure($attribute->getName(), $this->controller, $this->method, $e);
}
}
}
}

/**
* Logs an attribute instantiation failure with the resolved route context.
*
* @param string $attributeName Fully qualified attribute class name.
* @param string $controller Resolved controller class name.
* @param string|null $method Resolved controller method name, if applicable.
*/
private function logRouteAttributeInstantiationFailure(
string $attributeName,
string $controller,
?string $method,
Throwable $e,
): void {
$location = $controller;

if ($method !== null && $method !== '') {
$location .= '::' . $method . '()';
}

log_message(
'error',
'Failed to instantiate route attribute "{attribute}" on "{location}": {message}',
[
'attribute' => $attributeName,
'location' => $location,
'message' => $e->getMessage(),
],
);
}

/**
* Execute beforeController() on all route attributes.
* Called by CodeIgniter before controller execution.
Expand Down
27 changes: 27 additions & 0 deletions tests/_support/Router/Controllers/InvalidAttributeController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace Tests\Support\Router\Controllers;

use CodeIgniter\Controller;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Router\Attributes\Filter;

class InvalidAttributeController extends Controller
{
#[Filter(by: ['auth', 'csrf'])]
public function invalidMultipleFilters(): ResponseInterface
{
return $this->response->setBody('Invalid attributes');
}
}
25 changes: 25 additions & 0 deletions tests/system/Router/RouterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use CodeIgniter\HTTP\Exceptions\RedirectException;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\Method;
use CodeIgniter\Router\Attributes\Filter;
use CodeIgniter\Router\Exceptions\RouterException;
use CodeIgniter\Test\CIUnitTestCase;
use Config\App;
Expand All @@ -29,6 +30,7 @@
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use Tests\Support\Filters\Customfilter;
use Tests\Support\Router\Controllers\InvalidAttributeController;

/**
* @internal
Expand Down Expand Up @@ -1003,4 +1005,27 @@ public function testRoutePlaceholderAnyWithMultipleSegmentsParamTrue(): void
$this->assertSame('productLookup', $router->methodName());
$this->assertSame(['123/456'], $router->params());
}

public function testLogsRouteAttributeInstantiationFailureWithContext(): void
{
$collection = clone $this->collection;
$collection->resetRoutes();
$collection->get(
'invalid-attribute',
'Tests\Support\Router\Controllers\InvalidAttributeController::invalidMultipleFilters',
);

$router = new Router($collection, $this->request);

$router->handle('invalid-attribute');

$this->assertSame(
'\\' . InvalidAttributeController::class,
$router->controllerName(),
);
$this->assertSame('invalidMultipleFilters', $router->methodName());
$this->assertSame([], $router->getFilters());
$this->assertLogContains('error', 'Failed to instantiate route attribute "' . Filter::class . '" on "\Tests\Support\Router\Controllers\InvalidAttributeController::invalidMultipleFilters()":');
$this->assertLogContains('error', 'must be of type string');
}
}
5 changes: 3 additions & 2 deletions user_guide_src/source/incoming/controller_attributes/003.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ public function api()
{
}

// Multiple filters can be applied
#[Filter(by: ['auth', 'csrf'])]
// Multiple filters can be applied by repeating the attribute
#[Filter(by: 'auth')]
#[Filter(by: 'csrf')]
public function admin()
{
}
Expand Down
Loading