Посредники
Посредник (middleware) — слой, через который запрос проходит по пути к обработчику, а ответ — по пути обратно. В посредники выносят то, что повторяется от маршрута к маршруту: проверку входа, запуск сессии, служебные заголовки. В Slim это главный способ расширять приложение.
Где лежат посредники
Классы посредников кладут в src/Middleware/, а подключают в отдельном файле config/middleware.php. Обе вещи создают вручную.
Первый посредник
// src/Middleware/ResponseTimeMiddleware.php
namespace App\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
final class ResponseTimeMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$start = microtime(true);
$response = $handler->handle($request);
$ms = round((microtime(true) - $start) * 1000, 1);
return $response->withHeader('X-Response-Time', $ms . ' ms');
}
}Главная строка — $handler->handle($request): она передаёт запрос дальше, к следующему посреднику или к действию, и возвращает готовый ответ. Код до неё выполняется перед действием, код после — когда ответ уже сформирован. Интерфейс MiddlewareInterface — часть стандарта PSR-15, поэтому в Slim работают и готовые посредники из сторонних пакетов.
Подключение
Создайте config/middleware.php:
// config/middleware.php
use App\Middleware\ResponseTimeMiddleware;
use Slim\App;
return function (App $app): void {
$app->add(ResponseTimeMiddleware::class);
};И подключите его в config/bootstrap.php перед маршрутами:
// config/bootstrap.php
(require __DIR__ . '/middleware.php')($app);
(require __DIR__ . '/routes.php')($app);Откройте любую страницу и посмотрите заголовки ответа на вкладке «Сеть» инструментов разработчика — среди них появится X-Response-Time.
Группа и отдельный маршрут
Посредник, добавленный через $app->add(), работает для всех запросов. Его можно привязать и уже:
// config/routes.php
$app->get('/posts', ListPostsAction::class)
->add(ResponseTimeMiddleware::class); // один маршрут
$app->group('/admin', function (RouteCollectorProxy $group) {
// маршруты раздела
})->add(ResponseTimeMiddleware::class); // вся группаОстановить запрос и передать данные
Посредник не обязан вызывать $handler->handle(): он может сам вернуть ответ, например перенаправление на страницу входа. Так устроена проверка доступа — она показана в уроке об аутентификации.
Чтобы передать действию данные, посредник добавляет к запросу атрибут:
return $handler->handle($request->withAttribute('started_at', $start));Действие прочитает значение через $request->getAttribute('started_at').
Порядок выполнения
Посредники образуют слои, как у луковицы. Добавленный последним оказывается снаружи и выполняется первым:
// config/middleware.php
$app->add(FirstMiddleware::class);
$app->add(SecondMiddleware::class);Запрос пройдёт через SecondMiddleware, затем через FirstMiddleware, затем попадёт в действие; ответ вернётся в обратном порядке. Посредники маршрута и группы всегда выполняются после посредников приложения.
Частая ошибка
Метод withAttribute(), как и все методы с with, возвращает новый запрос. Если вызвать его и передать дальше старый $request, действие не увидит атрибута. Передавайте в $handler->handle() именно тот объект, который вернул withAttribute().