# Маршрутизатор HTTP-запросов для PHP
Пакет HttpSoft\Router обеспечивает удобное управление маршрутизацией HTTP-запросов.
Этот пакет поддерживает интерфейсы PSR-7 и PSR-15.
Для этого пакета требуется PHP версии 7.4 или более поздней.
Установка пакета:
composer require httpsoft/http-router
# API
- HttpSoft\Router\Route — класс, представляющий один маршрут.
- HttpSoft\Router\RouteCollection — коллекция маршрутов, реализующая HttpSoft\Router\RouteCollectionInterface.
- HttpSoft\Router\RouteCollector — класс, который предоставляет методы для создания и внедрения маршрутов.
Исключения:
- HttpSoft\Router\Exception\InvalidRouteParameterException.
- HttpSoft\Router\Exception\RouteAlreadyExistsException.
- HttpSoft\Router\Exception\RouteNotFoundException.
Посредники:
- HttpSoft\Router\Middleware\RouteMatchMiddleware.
- HttpSoft\Router\Middleware\RouteDispatchMiddleware.
# Использование
use HttpSoft\Router\RouteCollector;
/**
* @var mixed $handler
*/
$router = new RouteCollector();
# Определение маршрутов
$router->get('home', '/', $handler);
$router->post('logout', '/logout', $handler);
$router->add('login', '/login', $handler, ['GET', 'POST']);
$router->delete('post.delete', '/post/delete/{id}', $handler);
$router->get('post.view', '/post/{slug}{format}', $handler);
При добавлении параметра-плейсхолдера {token}
в путь по умолчанию будет использоваться регулярное выражение [^/]+
, которое соответствует любым символам, кроме слэша, указывающего на следующий сегмент пути.
Чтобы указать пользовательские регулярные выражения для плейсхолдеров, используйте метод HttpSoft\Router\Route::tokens()
.
$router->delete('post.delete', '/post/delete/{id}', $handler)->tokens(['id' => '\d+']);
$router->get('post.view', '/post/{slug}{format}', $handler)
->tokens(['slug' => '[\w\-]+', 'format' => '\.[a-zA-z]{3,}'])
;
Вы можете установить для токена значение по умолчанию, используя метод HttpSoft\Router\Route::defaults()
.
$router->get('post.view', '/post/{slug}{format}', $handler)
->tokens(['slug' => '[\w\-]+', 'format' => '\.[a-zA-z]{3,}'])
->defaults(['format' => '.html'])
;
// '/post/post-slug.html'
$router->routes()->path('post.view', ['slug' => 'post-slug']);
# Необязательные параметры
Токены маршрута, заключенные в [...]
, считаются необязательными.
$router->get('post.list', '/posts{[page]}', $handler)
->tokens(['page' => '\d+'])
;
// '/posts/33'
$router->routes()->path('post.list', ['page' => 33]);
// '/posts'
$router->routes()->path('post.list');
Необязательные параметры ДОЛЖНЫ находиться только в самом конце маршрута.
Обратите внимание, что ведущий слэш не нужно помещать перед токеном необязательного параметра.
Также можно установить значение по умолчанию для токена необязательного параметра.
$router->get('post.list', '/posts{[page]}', $handler)
->tokens(['page' => '\d+'])
->defaults(['page' => '1'])
;
// '/posts/1'
$router->routes()->path('post.list');
// '/posts'
$router->routes()->path('post.list', ['page' => null]);
Иногда бывает полезно иметь маршрут с несколькими необязательными параметрами подряд.
// `{[year/month/day]}` эквивалентно `{[/year/month/day]}`
$router->get('post.archive', '/post/archive{[year/month/day]}', $handler)
->tokens(['year' => '\d{4}', 'month' => '\d{2}', 'day' => '\d{2}'])
;
// '/post/archive'
$router->routes()->path('post.archive', ['year' => null, 'month' => null, 'day' => null]);
// или
$router->routes()->path('post.archive');
// '/post/archive/2020/09/12'
$router->routes()->path('post.archive', ['year' => '2020', 'month' => '09', 'day' => '12']);
// '/post/archive/2020/09'
$router->routes()->path('post.archive', ['year' => '2020', 'month' => '09']);
// '/post/archive/2020'
$router->routes()->path('post.archive', ['year' => '2020']);
# Соответствие хоста
Используя метод HttpSoft\Router\Route::host()
, вы можете указать конкретный хост для каждого маршрута.
// Только для example.com
$router->get('page', '/page', $handler)
->host('example.com')
;
// Только для subdomain.example.com
$router->get('page', '/page', $handler)
->host('subdomain.example.com')
;
// Только для shop.example.com или blog.example.com
$router->get('page', '/page', $handler)
->host('(shop|blog).example.com')
;
// Для любого поддомена
$router->get('page', '/page', $handler)
->host('(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?).example.com')
;
Обратите внимание, что все точки в регулярном выражении будут автоматически экранированы.
# Группы маршрутов
Можно указать маршруты внутри группы. Все маршруты, определенные внутри группы, будут иметь общий префикс пути.
$router->group('/post', static function (RouteCollector $router) use ($handler): void {
// '/post/post-slug'
$router->get('post.view', '/{slug}', $handler)->tokens(['slug' => '[\w-]+']);
// '/post' or '/post/2'
$router->get('post.list', '/list{[page]}', $handler)->tokens(['page' => '\d+']);
// '/post/delete/13'
$router->delete('post.delete', '/delete/{id}', DeleteHandler::class)->tokens(['id' => '\d+']);
// '/post/create/11' or '/post/update/12'
$router->add('post.edit', '/(create|update)/{id}', EditHandler::class, ['GET', 'POST'])
->tokens(['id' => '\d+'])
;
});
Результат будет эквивалентен:
$router->get('post.view', '/post/{slug}', $handler)->tokens(['slug' => '[\w-]+']);
$router->get('post.list', '/post/list{[page]}', $handler)->tokens(['page' => '\d+']);
$router->delete('post.delete', '/post/delete/{id}', DeleteHandler::class)->tokens(['id' => '\d+']);
$router->add('post.edit', '/post/(create|update)/{id}', CreateHandler::class, ['GET', 'POST'])
->tokens(['id' => '\d+'])
;
Также поддерживаются вложенные группы, в этом случае префиксы всех вложенных групп объединяются.
Подробнее об определении маршрутов смотрите в описании следующих классов:
# Генерация URI
Для генерации путей используйте метод HttpSoft\Router\RouteCollection::path()
.
$router->get('home', '/', $handler);
$router->routes()->path('home'); // '/'
$router->get('page', '/{slug}', $handler)->tokens(['slug' => '[\w\-]+']);
$router->routes()->path('page', ['slug' => 'page-slug']); // '/page-slug'
Для генерации URL используйте метод HttpSoft\Router\RouteCollection::url()
.
$router->get('home', '/', $handler)->host('(shop|blog).example.com');
$router->routes()->url('home', []); // '/'
$router->routes()->url('home', [], 'shop.example.com'); // '//shop.example.com'
$router->routes()->url('home', [], 'blog.example.com', true); // 'https://blog.example.com'
$router->routes()->url('home', [], 'shop.example.com', false); // 'http://shop.example.com'
$router->get('page', '/{slug}', $handler)->tokens(['slug' => '[\w\-]+']);
$router->routes()->url('page', ['slug' => 'page-slug']); // '/page-slug'
$router->routes()->url('page', ['slug' => 'page-slug'], 'any-host.com'); // '//any-host.com/page-slug'
Подробнее о генерации URI смотрите в описании следующих классов:
# Сопоставление маршрутов
Для сопоставления маршрутов рекомендуется использовать посредников.
В простейшем варианте ваше приложение может делать что-то вроде следующего:
/**
* @var HttpSoft\Router\RouteCollector $router
* @var Psr\Http\Message\ServerRequestInterface $request
*/
if (!$route = $router->routes()->match($request, false)) {
// 404 Not Found
}
if (!$route->isAllowedMethod($request->getMethod())) {
// 405 Method Not Allowed
}
// 200 OK
return $route;
Подробнее о сопоставлении маршрутов смотрите в описании следующих классов: