brickhouse/routing

Routing mechanism for Brickhouse applications

dev-main 2025-01-08 11:44 UTC

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.