A PSR-7 and PSR-15 compliant router.

1.2.1 2021-08-14 10:53 UTC

This package is auto-updated.

Last update: 2024-10-15 15:20:56 UTC


README

Overview

Router is a HTTP request router, using PSR-7 requests and responses, and delegating to PSR-15 middlewares and request handlers. It requires a PSR-11 DI container.

Usage

Instantiate the router:

$router = new Router();

Give it a PSR-11 DI container:

$router->setDI($myContainer);

Configure routes:

$router->map('get', '/home', HomeController::class);

You can set individual middleware processors for an individual route:

$router->map('get', '/home', HomeController::class)
    ->middleware(MyMiddleware::class);
    

You can also create groups of middleware and add those to routes:

$router->middleware('common')
    ->add(CommonMiddleware1::class)
    ->add(CommonMiddleware2::class);
    
$router->map('get', '/home', HomeController::class)
    ->middlewareGroup('common')
    ->middleware(MyMiddleware::class);
    

The group doesn't have to exist, letting you toggle a set of middleware for a set of routes:

if ($condition) {
    $router->middleware('option1')
        ->add(OptionalMiddleware1::class)
        ->add(OptionalMiddleware2::class);
}

$router->map('get', '/route1', Route1Controller::class)
    ->middlewareGroup('option1');
$router->map('get', '/route2', Route2Controller::class)
    ->middlewareGroup('option2');
    

The implementation accepts middleware as a shorthand for middlewareGroup, if and only if the group already exists, and the DI container has no named instance with that name. Otherwise, the router can't tell the difference between a named instance in the DI container and a group name. This shorthand will be removed in a future version.

You can also set global groups of middleware. These will be run for all requests:

$router->middleware()
    ->add(GlobalMiddleware1::class)
    ->add(GlobalMiddleware2::class);
    

Route Matching Rules

Exact Match

The second parameter of Router::map is a string which is matched against the path part of the request URI.

At its simplest, it can be an exact string match:

/an/example/exact/path/match

This will match the path, regardless of the host, scheme, query string or fragment.

Parameter Match

When matching parameters, we consider the path split into parts by slashes. Any whole part which starts with { and ends with } can match any value for the corresponding part of the URI path. For example:

/entries/{year}/{month}/{day} will match /entries/2021/08/14 and modify the request to contain attributes:

'year' => '2021'
'month' => '08'
'day' => '14'

It's important to note that only full path parts can be matched, not partial ones. None of the following are valid:

/blah{suffix}
/{prefix}blah
/bl{infix}ah
/{slash/inside}

Multi Parameter Match

Using * as a match part allows for matching of a variable number of parts of the request URI path.

/blah/* will match /blah/a/b/c and create the following attribute:

'*0' => [ 'a', 'b', 'c' ]

More than one wildcard can be used. They will produce attributes with an incrementing number in their name:

/blah/*/x/* will match /blah/a/b/x/c/d and create the following attributes:

'*0' => [ 'a', 'b' ]
'*1' => [ 'c', 'd' ]

Single parameters can be mixed with multi parameters. Single can directly precede multi but not directly follow one, e.g. /blah/{param1}/*/x/{param2} will match /blah/a/b/c/x/y and create the attributes:

'param1' => 'a'
'*0' => [ 'b', 'c' ]
'param2' => 'y'

But /blah/*/{param} will never match anything, as the final part of any URI path will always match the * and never {param}.