Popularity
2.5
Stable
Activity
6.0
Declining
133
4
9

Description

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

Programming language: PHP
License: MIT License
Tags: Routers     Router     Psr7     Psr15    

HTTP router alternatives and similar libraries

Based on the "Routers" category.
Alternatively, view http-router alternatives based on common mentions on social networks and blogs.

Do you think we are missing an alternative of HTTP router or a related project?

Add another 'Routers' Library

README

HTTP router for PHP 7.1+ (incl. PHP 8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger)

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


Installation

composer require 'sunrise/http-router:^2.8'

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 Sunrise\Http\Message\ResponseFactory;
use Sunrise\Http\Router\RequestHandler\CallableRequestHandler;
use Sunrise\Http\Router\RouteCollector;
use Sunrise\Http\Router\Router;
use Sunrise\Http\ServerRequest\ServerRequestFactory;

use function Sunrise\Http\Router\emit;

$collector = new RouteCollector();

$collector->get('home', '/', new CallableRequestHandler(function ($request) {
    return (new ResponseFactory)->createJsonResponse(200, [
        'status' => 'ok',
    ]);
}));

$router = new Router();
$router->addRoute(...$collector->getCollection()->all());

$request = ServerRequestFactory::fromGlobals();
$response = $router->handle($request);

emit($response);

Examples of using

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

Strategy loading routes from configs

use Sunrise\Http\Router\Loader\CollectableFileLoader;
use Sunrise\Http\Router\Router;

$loader = new CollectableFileLoader();
$loader->attach('routes/api.php');
$loader->attach('routes/admin.php');
$loader->attach('routes/public.php');

// or attach a directory...
// [!] available from version 2.2
$loader->attach('routes');

// or attach an array...
// [!] available from version 2.4
$loader->attachArray([
    'routes/api.php',
    'routes/admin.php',
    'routes/public.php',
]);

$router = new Router();
$router->load($loader);

// if the router matching should be isolated for top middlewares...
// for example for error handling...
// [!] available from version 2.8
$response = $router->run($request);

// if the router is used as a request handler
$response = $router->handle($request);

// if the router is used as middleware
$response = $router->process($request, $handler);
/** @var Sunrise\Http\Router\RouteCollector $this */

$this->get('home', '/', new CallableRequestHandler(function ($request) {
    return (new ResponseFactory)->createJsonResponse(200);
}));

Strategy loading routes from descriptors (annotations or attributes)

use Doctrine\Common\Annotations\AnnotationRegistry;
use Sunrise\Http\Router\Loader\DescriptorDirectoryLoader;
use Sunrise\Http\Router\Router;

// necessary if you will use annotations (annotations isn't attributes)...
AnnotationRegistry::registerLoader('class_exists');

$loader = new DescriptorDirectoryLoader();
$loader->attach('src/Controller');

// or attach an array
// [!] available from version 2.4
$loader->attachArray([
    'src/Controller',
    'src/Bundle/BundleName/Controller',
]);

$router = new Router();
$router->load($loader);

// if the router matching should be isolated for top middlewares...
// for example for error handling...
// [!] available from version 2.8
$response = $router->run($request);

// if the router is used as a request handler
$response = $router->handle($request);

// if the router is used as middleware
$response = $router->process($request, $handler);

Without loading strategy

use App\Controller\HomeController;
use Sunrise\Http\Router\RouteCollector;
use Sunrise\Http\Router\Router;

$collector = new RouteCollector();
$collector->get('home', '/', new HomeController());

$router = new Router();
$router->addRoute(...$collector->getCollection()->all());

// if the router matching should be isolated for top middlewares...
// for example for error handling...
// [!] available from version 2.8
$response = $router->run($request);

// if the router is used as a request handler
$response = $router->handle($request);

// if the router is used as middleware
$response = $router->process($request, $handler);

Error handling example

use Sunrise\Http\Message\ResponseFactory;
use Sunrise\Http\Router\Exception\MethodNotAllowedException;
use Sunrise\Http\Router\Exception\RouteNotFoundException;
use Sunrise\Http\Router\Middleware\CallableMiddleware;
use Sunrise\Http\Router\RequestHandler\CallableRequestHandler;
use Sunrise\Http\Router\RouteCollector;
use Sunrise\Http\Router\Router;
use Sunrise\Http\ServerRequest\ServerRequestFactory;

use function Sunrise\Http\Router\emit;

$collector = new RouteCollector();

$collector->get('home', '/', new CallableRequestHandler(function ($request) {
    return (new ResponseFactory)->createJsonResponse(200);
}));

$router = new Router();
$router->addRoute(...$collector->getCollection()->all());

$router->addMiddleware(new CallableMiddleware(function ($request, $handler) {
    try {
        return $handler->handle($request);
    } catch (MethodNotAllowedException $e) {
        return (new ResponseFactory)->createResponse(405);
    } catch (RouteNotFoundException $e) {
        return (new ResponseFactory)->createResponse(404);
    } catch (Throwable $e) {
        return (new ResponseFactory)->createResponse(500);
    }
}));

emit($router->run(ServerRequestFactory::fromGlobals()));

Route Annotation Example

Minimal annotation view
/**
 * @Route(
 *   name="api_v1_entry_update",
 *   path="/api/v1/entry/{id<@uuid>}(/{optionalAttribute})",
 *   methods={"PATCH"},
 * )
 */
final class EntryUpdateRequestHandler implements RequestHandlerInterface
Full annotation
/**
 * @Route(
 *   name="api_v1_entry_update",
 *   host="api.host",
 *   path="/api/v1/entry/{id<@uuid>}(/{optionalAttribute})",
 *   methods={"PATCH"},
 *   middlewares={
 *     "App\Middleware\CorsMiddleware",
 *     "App\Middleware\ApiAuthMiddleware",
 *   },
 *   attributes={
 *     "optionalAttribute": "defaultValue",
 *   },
 *   summary="Updates an entry by UUID",
 *   description="Here you can describe the method in more detail...",
 *   tags={"api", "entry"},
 *   priority=0,
 * )
 */
final class EntryUpdateRequestHandler implements RequestHandlerInterface

Route Attribute Example

Minimal attribute view
use Sunrise\Http\Router\Attribute\Route;

#[Route(
    name: 'api_v1_entry_update',
    path: '/api/v1/entry/{id<@uuid>}(/{optionalAttribute})',
    methods: ['PATCH'],
)]
final class EntryUpdateRequestHandler implements RequestHandlerInterface
Full attribute
use Sunrise\Http\Router\Attribute\Route;

#[Route(
    name: 'api_v1_entry_update',
    host: 'api.host',
    path: '/api/v1/entry/{id<@uuid>}(/{optionalAttribute})',
    methods: ['PATCH'],
    middlewares: [
        \App\Middleware\CorsMiddleware::class,
        \App\Middleware\ApiAuthMiddleware::class,
    ],
    attributes: [
        'optionalAttribute' => 'defaultValue',
    ],
    summary: 'Updates an entry by UUID',
    description: 'Here you can describe the method in more detail...',
    tags: ['api', 'entry'],
    priority: 0,
)]
final class EntryUpdateRequestHandler implements RequestHandlerInterface

Useful to know

OpenApi (Swagger)

composer require 'sunrise/http-router-openapi:^1.1'

Generation documentation for Swagger (OAS)

use Sunrise\Http\Router\OpenApi\Object\Info;
use Sunrise\Http\Router\OpenApi\OpenApi;

$openApi = new OpenApi(new Info('0.0.1', 'API'));

$openApi->addRoute(...$router->getRoutes());

$openApi->toArray();

Validation a request body via Swagger documentation

use Sunrise\Http\Router\OpenApi\Middleware\RequestBodyValidationMiddleware;

$route->addMiddleware(new RequestBodyValidationMiddleware());

or using annotations:

/**
 * @Route(
 *   name="foo",
 *   path="/foo",
 *   methods={"post"},
 *   middlewares={
 *     "Sunrise\Http\Router\OpenApi\Middleware\RequestBodyValidationMiddleware",
 *   },
 * )
 */

Generation a route URI

$uri = $router->generateUri('route.name', [
    'attribute' => 'value',
], true);

Run a route

$response = $router->getRoute('route.name')->handle($request);

Route grouping

$collector->group(function ($collector) {
    $collector->group(function ($collector) {
        $collector->group(function ($collector) {
            $collector->get('api.entry.read', '/{id<\d+>}', ...)
                ->addMiddleware(...); // add the middleware(s) to the route...
        })
        ->addPrefix('/entry') // add the prefix to the group...
        ->unshiftMiddleware(...); // add the middleware(s) to the group...
    })
    ->addPrefix('/v1') // add the prefix to the group...
    ->unshiftMiddleware(...); // add the middleware(s) to the group...
})
->addPrefix('/api') // add the prefix to the group...
->unshiftMiddleware(...); // add the middleware(s) to the group...

Route patterns

$collector->get('api.entry.read', '/api/v1/entry/{id<\d+>}(/{optional<\w+>})');

Hosts (available from version 2.6.0)

Note: if you don't assign a host for a route, it will be available on any hosts!

// move the hosts table into the settings...
$router->addHost('public.host', 'www.example.com');
$router->addHost('admin.host', 'secret.example.com');
$router->addHost('api.host', 'api.example.com');

// the route will available only on the `secret.example.com` host...
$route->setHost('admin.host');

// routes in the group will available on the `secret.example.com` host...
$collector->group(function ($collector) {
    // some code...
})
->setHost('admin.host');

Test run

composer test

Useful links


*Note that all licence references and agreements mentioned in the HTTP router README section above are relevant to that project's source code only.