Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

HTTP Router

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

This package provides convenient management of HTTP request routing with support for PSR-7 and PSR-15.



This package requires PHP version 7.4 or later.

composer require httpsoft/http-router


use HttpSoft\Router\RouteCollector;

 * @var mixed $handler

$router = new RouteCollector();

// Defining routes.
$router->get('home', '/', $handler);
$router->post('logout', '/logout', $handler);
$router->add('login', '/login', $handler, ['GET', 'POST']);

// Custom regular expressions for placeholder parameter tokens.
$router->delete('post.delete', '/post/delete/{id}', $handler)->tokens(['id' => '\d+']);

// Generate path '/post/delete/25'
$router->routes()->path('post.delete', ['id' => 25]);
// Generate url '//'
$router->routes()->url('post.delete', ['id' => 25], '');
// Generate url ''
$router->routes()->url('post.delete', ['id' => 25], '', true);

Set the parameter to the default value.

$router->get('post.view', '/post/{slug}{format}', $handler)
    ->tokens(['slug' => '[\w\-]+', 'format' => '\.[a-zA-z]{3,}'])
    ->defaults(['format' => '.html'])

// Generate path '/post/post-slug.html'.
$router->routes()->path('post.view', ['slug' => 'post-slug']);

Tokens of the route enclosed in [...] are considered optional.

$router->get('post.list', '/posts{[page]}', $handler)
    ->tokens(['page' => '\d+'])

// '/posts/33'
$router->routes()->path('post.list', ['page' => 33]);
// '/posts'

If necessary, you can specify a specific host for route matching.

// Only for
$router->get('page', '/page', $handler)

// Only for
$router->get('page', '/page', $handler)

// Only for or
$router->get('page', '/page', $handler)

You can specify routes inside of a group.

$router->group('/post', static function (RouteCollector $router): void {
    // '/post/post-slug'
    $router->get('post.view', '/{slug}', ViewHandler::class)->tokens(['slug' => '[\w-]+']);
    // '/post' or '/post/2'
    $router->get('post.list', '/list{[page]}', ListHandler::class)->tokens(['page' => '\d+']);

// The result will be equivalent to:

$router->get('post.view', '/post/{slug}', ViewHandler::class)->tokens(['slug' => '[\w-]+']);
$router->get('post.list', '/post/list{[page]}', ListHandler::class)->tokens(['page' => '\d+']);

Check matching routes.

 * @var mixed $handler
 * @var Psr\Http\Message\UriInterface $uri
 * @var Psr\Http\Message\ServerRequestInterface $request

$router->get('page', '/page/{id}', $handler)->tokens(['id' => '\d+']);

// Match
$route = $router->routes()->match($request->withUri($uri->withPath('/page/11')));
$route->getMatchedParameters(); // ['id' => '11']

// Mismatch
$router->routes()->match($request->withUri($uri->withPath('/page/slug'))); // null