brickhouse / routing
Routing mechanism for Brickhouse applications
Requires
- php: ^8.4
Requires (Dev)
- pestphp/pest: ^3.7
- pestphp/pest-plugin-type-coverage: ^3.2
- phpbench/phpbench: ^1.3
- phpstan/phpstan: ^2.0
This package is auto-updated.
Last update: 2025-01-08 11:48:43 UTC
README
This library is a regex-based routing mechanism for PHP applications. It is heavily based on nikic' FastRoute project.
Installation
To install the library, you need to require the package via composer:
composer require brickhouse/routing
Usage
To add routes to the dispatcher, you can create a RouteCollector
like so:
use Brickhouse\Routing; $collector = new RouteCollector(); $collector->addRoute('GET', '/', fn() => 'Hello World!'); $collector->addRoute('GET', '/posts/:id', fn() => render_post()); $collector->addRoute('POST', '/posts', fn() => create_post());
The collector can then be passed to a dispatcher, which can match the routes:
$dispatcher = new Dispatcher($collector); $route = $dispatcher->dispatch('GET', '/');
When a route is matched, $route
will be an array of the route handler, as well as the parameters in the route. If no route was matched, $route
is null
.
Defining routes
Required parameters
Parameters can be defined by adding a colon to the beginning of a route segment, like so:
$collector->addRoute('GET', '/posts/:id', '...');
When the route is matched, the captured parameter value will be returned in the dispatched route:
[$handler, $parameters] = $dispatcher->dispatch('GET', '/posts/routing'); echo $parameters; // ['id' => 'routing']
You may define as many parameters in a single route as required:
$collector->addRoute('GET', '/users/:user/posts/:post', '...');
Route parameters names consist of alphanumeric ([A-Za-z0-9]
) characters and underscores (_
).
Optional parameters
Parameters can also be marked as optional, allowing the route to be dispatched by multiple routes. Optional parameters are defined by placing a ?
-mark after the colon:
$collector->addRoute('GET', '/user/:?id', '...');
When the parameter is omitted from the matched, it will be unset in the matched route:
[$handler, $parameters] = $dispatcher->dispatch('GET', '/user'); echo $parameters; // []
Catch-all parameters
Parameters can be extended to catch all subsequent segments in the path. Catch-all parameters are defined by replacing the colon with an asterisk (*
):
$collector->addRoute('GET', '/posts/*slug', '...');
When using a catch-all parameter, all subsequent segments are returned as a single string.
$dispatcher->dispatch('GET', '/posts/a'); // $parameters => ['slug' => 'a'] $dispatcher->dispatch('GET', '/posts/a/b'); // $parameters => ['slug' => 'a/b'] $dispatcher->dispatch('GET', '/posts/a/b/c'); // $parameters => ['slug' => 'a/b/c']
Catch-all parameters also require a value to be given before matching:
$route = $dispatcher->dispatch('GET', '/posts'); // $route => null
To allow the catch-all to match routes without giving the parameter, you can mark it as optional:
$collector->addRoute('GET', '/posts/*?slug', '...'); // ... $dispatcher->dispatch('GET', '/posts'); // $parameters => [] $dispatcher->dispatch('GET', '/posts/a'); // $parameters => ['slug' => 'a']
Custom pattern constraints
You may write your own constraints for route parameters using regular expressions. Constraints are defined per-argument and can be written like so:
$collector->addRoute('GET', '/user/:user_id', '...', ['user_id' => '\d+']);
This limits which values can be matched with the route. If the route doesn't match, it'll continue looking for one that does.
$route = $dispatcher->dispatch('GET', '/user/423'); // $route => ['...', ['user_id' => '423']] $route = $dispatcher->dispatch('GET', '/user/admin'); // $route => null
Performance
Performance is measured using PHPBench, running the benchmarks in bench.
All setups are different, but this bench was run on a Mac Mini M4 (16GB, Sequoia 15.3), with PHP 8.4.1 where both XDebug and OPCache disabled.
License
Brickhouse Routing is open-sourced software licensed under the MIT license.