RouterRegister Help

Defining Middleware Attributes

Middleware to be applied to a route can be defined using Router Register's Middleware Attribute or by defining middleware attributes in the application.

Using application defined middleware attributes will make the source code cleaner and gives IDE code completion and type checking.

AccessChecker Middleware Example

As an example, consider middleware to provide access checking to ensure that the current user has permission to perform the requested action. If the user does not have permission, the access checker redirects the user to a safe location.

The access checker middleware will be something like:

final class AccessChecker implements Psr\Http\Server\MiddlewareInterface { private ?string $permission = null; private ?string $route = null; public function __construct( private AccessCheckerInterface $accessChecker, private CurrentUser $currentUser, private ResponseFactoryInterface $responseFactory, ) { } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { if ($this->accessChecker->userHasPermission($this->currentUser->getId(), $this->permission)) { return $handler->handle($request); } return $this ->responseFactory ->createResponse(Status::FOUND) ->withHeader( Header::LOCATION, $this ->urlGenerator ->generate($this->route) ) ; } public function withPermission(string $permission): self { $new = clone $this; $new->permission = $permission; return $new; } public function withRedirectRoute(string $route): self { $new = clone $this; $new->route = $route; return $new; } }

Using the Middleware Attribute

Using the AccessChecker middleware with Router Register's Middleware attribute will look like:

final class MyClass { #[Get(MyClassRoute::view)] #[Middleware( 'fn (' . AccessChecker::class . ' $checker) => $checker' . "->withPermission('my-class.view')" . "->withRedirectRoute('safe.location')" )] public function view(): ResponseInterface { // Action code } }

This works and is OK for a middleware that is only used once, but the middleware definition is a lot of code, especially if repeated for many actions.

A better solution is to define an application middleware attribute; it will reduce the amount of code and make it more descriptive.

Defining an Application Middleware Attribute

Application middleware attributes must implement BeastBytes\Router\Register\Attribute\MiddlewareInterface which requires that the attribute has a `getMiddleware()` method that returns the middleware string.

For the access checker, this will look like:

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] final class AccessCheck implements BeastBytes\Router\Register\Attribute\MiddlewareInterface { public function __construct( private readonly string $permission, private readonly string $redirectRoute, private readonly bool $disable = false, ) { } public function getMiddleware(): string { return 'fn (' . AccessChecker::class . ' $checker) => $checker' . "->withPermission('" . $this->permission . "')" . "->withRedirectRoute('" . $this->redirectRoute . "'')"; } public function disable(): bool { return $this->disable; } }

Then, to add the middleware to the route definition:

final class MyClass { #[Get(MyClassRoute::view)] #[AccessCheck(permission: 'my-class.view', redirectRoute: 'safe.location')] public function view(): ResponseInterface { // Action code } }

Less code, more readable code, more reusable code, and there will be IDE code completion and type checking.

The resulting route definition is the same in both cases.

Last modified: 19 November 2025