Skip to content
Very fast HTTP router with annotations support for PHP 7.1+ based on PSR-7 and PSR-15
Branch: master
Clone or download
fenric Merge pull request #21 from sunrise-php/renovate/phpunit-phpunit-7.x
Update dependency phpunit/phpunit to v7.5.11
Latest commit 85837a0 May 20, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
functions Minor changes Dec 21, 2018
src MethodNotAllowedException->getAllowedMethodsAsString() May 20, 2019
tests MethodNotAllowedException->getAllowedMethodsAsString() May 20, 2019
.editorconfig
.gitignore
.scrutinizer.yml
.travis.yml Some vendor dependencies don't support PHP-8 Feb 10, 2019
LICENSE Initial commit Dec 9, 2018
README.md Update README.md Mar 25, 2019
composer.json
phpunit.xml.dist Minor changes Jan 21, 2019
renovate.json

README.md

Fast HTTP router with annotations support for PHP 7.1+ based on PSR-7 and PSR-15

Gitter Build Status Scrutinizer Code Quality Code Coverage Latest Stable Version Total Downloads License

Awards

SymfonyInsight

Benchmark (1k iterations with 1k routes)

You can see it here

+-----------+------+--------------+-------+
| subject   | its  | mean         | diff  |
+-----------+------+--------------+-------+
| Sunrise   | 1000 | 17,856.609μs | 1.00x |
| FastRoute | 1000 | 20,920.968μs | 1.17x |
| Aura      | 1000 | 44,480.588μs | 2.49x |
| Zend      | 1000 | 96,778.725μs | 5.42x |
+-----------+------+--------------+-------+

Installation (via composer)

composer require sunrise/http-router

How to use?

Study sunrise/awesome-skeleton to understand how this can be used.

Annotations

This package has annotation support, all the details are here.

QuickStart

The example uses other sunrise packages, but you can use for example zend/diactoros, or any other.

composer require sunrise/http-message sunrise/http-server-request
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Sunrise\Http\Message\ResponseFactory;
use Sunrise\Http\Router\Exception\MethodNotAllowedException;
use Sunrise\Http\Router\Exception\RouteNotFoundException;
use Sunrise\Http\Router\RouteCollection;
use Sunrise\Http\Router\Router;
use Sunrise\Http\ServerRequest\ServerRequestFactory;

class DemoMiddleware implements MiddlewareInterface
{
    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler) : ResponseInterface
    {
        $response = $handler->handle($request);

        $response->getBody()->write(sprintf('URI: "%s"; Attributes: "%s"',
            $request->getUri(), print_r($request->getAttributes(), true)
        ));

        return $response;
    }
}

$routes = new RouteCollection();

$routes->get('home', '/')
->addMiddleware(new DemoMiddleware);

$routes->group('/api', function($routes)
{
    $routes->group('/v1', function($routes)
    {
        $routes->post('resource.create', '/resource')
        ->addMiddleware(new DemoMiddleware);

        $routes->patch('resource.update', '/resource/{id}')
        ->addPattern('id', '\d+')
        ->addMiddleware(new DemoMiddleware);

        $routes->delete('resource.delete', '/resource/{id}')
        ->addPattern('id', '\d+')
        ->addMiddleware(new DemoMiddleware);

        $routes->get('resource.read', '/resource/{id}')
        ->addPattern('id', '\d+')
        ->addMiddleware(new DemoMiddleware);

        $routes->get('resource.all', '/resource')
        ->addMiddleware(new DemoMiddleware);
    });
});

$router = new Router();
$router->addRoutes($routes);

try
{
    $response = $router->handle(ServerRequestFactory::fromGlobals());
}
catch (MethodNotAllowedException $e)
{
    $response = (new ResponseFactory)->createResponse(405)
    ->withHeader('allow', implode(',', $e->getAllowedMethods()));

    $response->getBody()->write($response->getReasonPhrase());
}
catch (RouteNotFoundException $e)
{
    $response = (new ResponseFactory)->createResponse(404);

    $response->getBody()->write($response->getReasonPhrase());
}

$headers = $response->getHeaders();

foreach ($headers as $name => $values)
{
    foreach ($values as $value)
    {
        header(sprintf('%s: %s', $name, $value), false);
    }
}

header(sprintf('HTTP/%s %d %s',
    $response->getProtocolVersion(),
    $response->getStatusCode(),
    $response->getReasonPhrase()
), true);

echo $response->getBody();

Adding a route to the collection

Adds a new route to the collection
$route = $routes->route('route.id', '/route/path', ['HEAD', 'GET']);
Adds a new route to the collection that will respond to HEAD requests
$route = $routes->head('route.id', '/route/path');
Adds a new route to the collection that will respond to GET requests
$route = $routes->get('route.id', '/route/path');
Adds a new route to the collection that will respond to POST requests
$route = $routes->post('route.id', '/route/path');
Adds a new route to the collection that will respond to PUT requests
$route = $routes->put('route.id', '/route/path');
Adds a new route to the collection that will respond to PATCH requests
$route = $routes->patch('route.id', '/route/path');
Adds a new route to the collection that will respond to DELETE requests
$route = $routes->delete('route.id', '/route/path');
Adds a new route to the collection that will respond to PURGE requests
$route = $routes->purge('route.id', '/route/path');
Adds a new route to the collection that will respond to safe requests
$route = $routes->safe('route.id', '/route/path');
Adds a new route to the collection that will respond to any requests
$route = $routes->any('route.id', '/route/path');

Route grouping

// Add a route to the collection with the path: /foo/bar/baz/qux
$routes->group('/foo', function($routes)
{
    $routes->group('/bar', function($routes)
    {
        $routes->group('/baz', function($routes)
        {
            $route = $routes->get('qux', '/qux');
        });
    });
});

Route patterns

$route = $routes->any('resource.action', '/resource/{action}(/{id})')
->addPattern('action', 'create|update|delete|read|all')
->addPattern('id', '\d+');

Route middlewares

$route
->addMiddleware(new FooMiddleware)
->addMiddleware(new BarMiddleware)
->addMiddleware(new BazMiddleware);

Router middlewares

$router
->addMiddleware(new FooMiddleware)
->addMiddleware(new BarMiddleware)
->addMiddleware(new BazMiddleware);

Router matching

$route = $router->match($request);

Useful Middlewares

Error handling (whoops)

composer require middlewares/whoops
$router->addMiddleware(new \Middlewares\Whoops());

Payload (payload)

composer require middlewares/payload
$router->addMiddleware(new \Middlewares\JsonPayload());
$router->addMiddleware(new \Middlewares\UrlEncodePayload());

Encoding (encoder)

composer require middlewares/encoder
$router->addMiddleware(new \Middlewares\GzipEncoder());

Awesome PSR-15 Middlewares

https://github.com/middlewares

Test run

php vendor/bin/phpunit

Benchmark run

https://github.com/sunrise-php/http-router-benchmark

Api documentation

https://phpdoc.fenric.ru/

Useful links

You can’t perform that action at this time.