2017 © Pedro Peláez
 

library router

image

stratify/router

  • Saturday, August 5, 2017
  • by mnapoli
  • Repository
  • 2 Watchers
  • 6 Stars
  • 377 Installations
  • PHP
  • 2 Dependents
  • 0 Suggesters
  • 1 Forks
  • 0 Open issues
  • 13 Versions
  • 2 % Grown

The README.md

Routers implemented as a PSR-7 middleware., (*1)

This package provides 3 routers:, (*2)

  • a "classic" router for web applications
  • a REST router for building REST APIs
  • a "prefix" router that routes to sub-middlewares based on URL prefixes

Everything is a middleware

Each router is a middleware, which means it can be used as many times as needed in a middleware stack. That also means that if a router doesn't match the request to a route, it's not an error: it will simply call the $next middleware., (*3)

Additionally, controllers (aka route handlers) are also middlewares. Routers are simply routing the flow of the request to a sub-branch of middlewares. That allows to remove the specific concept of "route middlewares"., (*4)

Here is an example of using several routers in one application, as well as using middlewares in routes:, (*5)

use Stratify\Http\Middleware\MiddlewarePipe;
use Stratify\Router\PrefixRouter;
use Stratify\Router\Router;

$app = new MiddlewarePipe([
    new ErrorHandlerMiddleware(), // a middleware that applies to the whole stack

    new PrefixRouter([

        // The blog has its own router
        '/blog/' => new Router([
            '/blog/' => new HomeController(),
            '/blog/{article}' => new ArticleController(),
        ]),

        // The API has its own router with its own middlewares (authentication for example)
        '/api/' => new MiddlewarePipe([
            new HttpBasicAuthMiddleware(), // authentication only for the API
            new Router([
                '/api/articles' => new ArticleController(),
                '/api/users' => new UserController(),
            ]),
        ]),

    ]),
]);

Please keep in mind this example is over-simplified. It's perfectly possible to benefit from dependency injection, lazy instantiation of controllers/middlewares as well as a simpler API for configuring the application; that's what the Stratify framework brings on top of the router., (*6)

Installation

composer require stratify/router

Classic router

This "classic" router is very similar to other PHP routers. It is built on top of Aura.Router., (*7)

The router takes a map of URLs to callables, for example:, (*8)

$router = new Router([
    '/' => function (...) {
        ...
    },
]);

By default only the HTTP GET method will be allowed., (*9)

Placeholders can be used in route paths and fetched from request attributes or injected as parameters:, (*10)

    '/article/{id}' => function ($id) {
        ...
    },
    '/category/{id}' => function (ServerRequestInterface $request) {
        $id = $request->getAttribute('id');
        ...
    },

Routes can be configured in more details using the route() helper., (*11)

  • placeholder formats (using a regex):, (*12)

    use function \Stratify\Router\route;
    
    $router = new Router([
        '/{id}' => route(function () { … })
            ->pattern('id', '\d+') // the placeholder must be a number
    ]);
    
  • optional placeholders:, (*13)

        '/export.{format}' => route(/* callable */)
            ->optional('format', 'json'),
    
  • accepted HTTP methods:, (*14)

        '/subscribe' => route(/* callable */)
            ->method('POST'),
    

HTTP resources

You can define separate handlers for each HTTP method using the resource() helper:, (*15)

use function \Stratify\Router\resource;

$router = new Router([
    '/' => resource([
        'get' => function () { … },
        'post' => function () { … },
    ]),
]);

However if you plan on using all HTTP methods you might want to use the RestRouter instead., (*16)

Controllers

Controllers, aka "route handlers", can be any PHP callables (closures, object methods, invokable objects, …)., (*17)

The callable can decide which parameters it will take. The router will detect what to provide based on the callable's parameters. Parameters can be:, (*18)

  • the PSR-7 request: ServerRequestInterface $request; in that case the parameter must be named $request
  • any request attribute, this includes:
    • route placeholders: /order/{orderId} => you can have a $orderId parameter (parameter names must be identical to the route placeholder name)
    • or any attribute defined by a previous middleware (e.g. if an authentication middleware defines a user attribute, you can have a $user parametre)
  • the next callable to call ($next parameter), see below

Since a controller/route handler is a middleware, it can also have the middleware signature:, (*19)

function (ServerRequestInterface $request, callable $next) {
}

Controller responses

Controllers, like middlewares, are expected to return PSR-7 response objects., (*20)

However, in order to ease development, they can also return strings: those will be automatically turned into 200 HTML responses., (*21)

function () {
    return 'Hello world!';
}

// Same as
function () {
    return new HtmlResponse('Hello world!');
}

REST router

The REST router behaves exactly like the classic router (the same rules applies for controllers) except that it allows to register handlers more easily for HTTP resources., (*22)

$router = new RestRouter([
    '/articles' => new ArticleController,
]);

This is equivalent to this mapping with the classic router:, (*23)

$router = new RestRouter([
    '/articles' => resource([
        'get' => [new ArticleController, 'index'],
        'post' => [new ArticleController, 'post'],
    ]),
    '/articles/{id}' => resource([
        'get' => [new ArticleController, 'get'],
        'put' => [new ArticleController, 'put'],
        'delete' => [new ArticleController, 'delete'],
    ]),
]);

Here is an example of a REST controller:, (*24)

class ArticleController
{
    public function index()
    {
        return new HtmlResponse('Index');
    }

    public function post()
    {
        return new HtmlResponse('POST');
    }

    public function get(ServerRequestInterface $request)
    {
        return new HtmlResponse('GET '.$request->getAttribute('id'));
    }

    public function put(ServerRequestInterface $request)
    {
        return new HtmlResponse('PUT '.$request->getAttribute('id'));
    }

    public function delete(ServerRequestInterface $request)
    {
        return new HtmlResponse('DELETE '.$request->getAttribute('id'));
    }
}

Prefix router

The PrefixRouter is a very simple and very fast router that routes based on URL prefixes., (*25)

$router = new PrefixRouter([
    '/api/' => /* API middleware stack */,
    '/admin/' => /* Admin middleware stack */,
    '/' => /* Public website middleware stack */,
]);

The first prefix to match is used. Each route handler must be a middleware, i.e. a callable whose signature matches:, (*26)

function (ServerRequestInterface $request, callable $next) : ResponseInterface {
}

The prefix router is useful to separate several parts of a large application that do not need the same middlewares, for example:, (*27)

  • authentication required for all the "admin" part, but no authentication on the public website
  • cache for the public website but no caching in the backend
  • content negotiation and token authentication for an API, but not for the rest of the application
  • etc.

The Versions

05/02 2016
10/09 2015