Архитектура любого серверного веб-фреймворка опирается на особенности протокола HTTP. Сюда входят понятия запроса (request) и ответа (response). Это значит, что каждому веб-фреймворку приходится реализовывать эти объекты у себя, повторяя то, что уже было сделано в других местах.
Чтобы избежать такой ситуации, был разработан стандарт PSR7. Цель PSR-7 — предоставить общий набор интерфейсов для фреймворков, чтобы последние могли использовать одинаковые абстракции. Это позволит разработчикам писать переиспользуемый, независимый от фреймворка код. Сам стандарт объемный, и не имеет смысла его дублировать. В этом уроке мы поговорим только о ключевых особенностях.
Request и Response с точки зрения стандарта — это абстракция поверх механизмов, встроенных в сам PHP. Например, они полностью заменяют собой суперглобальные массивы, механизм загрузки файлов и многое другое.
Объекты запроса и ответа во фреймворке Slim имеют интерфейс, соответствующий стандарту PSR-7. Пример на главной странице фреймворка демонстрирует это:
<?php
use Slim\Factory\AppFactory;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
require 'vendor/autoload.php';
$app = AppFactory::create();
$app->get('/hello/{name}', function (Request $request, Response $response, array $args) {
$name = $args['name'];
// Эта функция write возвращает количество переданных байт
return $response->getBody()->write("Hello, {$name}");
});
$app->run();
getBody()
возвращает специальный объект-поток (stream). Этот объект можно изменять, записывая туда данные.
Интерфейсы, описанные в PSR7, эмулируют работу HTTP. С помощью их методов можно извлечь любую информацию из запроса и создать любой ответ.
<?php
// Возвращает значение заголовка Host
$request->getHeader('Host');
// Проверяет был ли указан заголовок
$request->hasHeader('Accept');
Эти методы работают не только для запроса, но и для ответа. Оба интерфейса Request и Response имеют общую часть, которая называется Message. Получается, что многие методы повторяются и одинаково работают в каждом из этих объектов.
Названия заголовков в PSR-7 регистронезависимы. В самом PHP заголовки всегда переводятся в верхний регистр и хранятся в массиве $_SERVER
с префиксом HTTP_
:
<?php
// Возвращает массив заголовков, в котором значения заголовков разделены по элементам массива
foreach ($request->getHeaders() as $name => $values) {
echo $name . ': ' . implode(', ', $values);
}
Response
Ответ аккумулирует внутри себя то, что отправится клиенту, но он изначально не пустой, а содержит некоторые разумные умолчания:
<?php
// Статус ответа. По умолчанию 200.
$status = $response->getStatusCode();
А вот с изменением все не так просто. Главная отличительная черта этого интерфейса в том, что он построен в иммутабельном (неизменяемом) стиле и реализует текучий интерфейс (fluent interface). Ответ невозможно изменить. Вместо этого всегда возвращается новый объект:
<?php
// response не меняется!
$newResponse = $response->withStatus(302);
$newResponse === $response; // false
По этой причине во фреймворках, поддерживающих стандарт PSR-7, обработчик запроса всегда должен вернуть объект ответа. Только в этом случае фреймворк узнает о том, как надо ответить на запрос:
<?php
$app->get('/something', function (Response $response) {
return $response->withStatus(500)
->withHeader('Content-Type', 'text/html');
});
Единственная часть в Response, которую можно менять, – тело ответа. Это связано с техническими особенностями работы потоков в самом PHP. Подробнее об этом можно прочитать в документации.
<?php
$app->get('/something', function (Response $response) {
$response->getBody()->write('То что отправится пользователю в теле ответа');
// Ответ все равно надо вернуть!
return $response;
});
При этом PSR7 достаточно низкоуровневый стандарт. У него нет цели сделать работу с объектами ответа и запроса максимально удобной и простой. Задача была в эмуляции поведения HTTP. Поэтому помимо реализации стандартных интерфейсов многие фреймворки создают дополнительные обертки поверх PSR7.
Эти обертки дают много прикладных полезных методов. Одну из таких оберток мы начали использовать с самого начала: Slim-Http. Вот лишь небольшой список полезных функций этой библиотеки, которые мы либо использовали, либо будем использовать:
Response::write($data)
— изменяет ответResponse::withRedirect($url, $status)
ServerRequest::getParam($key, $default)
ServerRequest::getParsedBody()
Самостоятельная работа
- Реализуйте код ошибки 404, если обращение идет к несуществующему пользователю — в адресной строке браузера по маршруту
users/{id}
введен несуществующий id.Существование пользователя проверяйте через файл, созданный в предыдущем уроке, который содержит всех пользователей.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.