Архитектура любого серверного веб-фреймворка, опирается на особенности протокола 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 возвращает количество переданных байт
    $response->getBody()->write("Hello, {$name}");
    return $response;
});
$app->run();

getBody() возвращает специальный объект-поток (stream). Этот объект можно изменять записывая туда данные.

Интерфейсы описанные в PSR7 эмулируют работу HTTP. С помощью их методов можно как извлечь любую информацию из запроса, так и создать любой ответ.

<?php

// Возвращает значение заголовка Host
$request->getHeader('Host');

// Проверяет был ли указан заголовок
$request->hasHeader('Accept');

Эти методы работают не только для запроса, но и для ответа. Дело в том, что оба эти интерфейса Request и Response, имеют общую часть, которая называется Message, другими словами, многие методы повторяются и одинаково работают в каждом из этих объектов.

Названия заголовков в PSR-7, как и в самом HTTP регистронезависимы. В тоже время в самом 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()

Самостоятельная работа

  1. Реализуйте код ошибки 404 в случае если обращение идет к несуществующему пользователю. Существование пользователя проверяйте через файл, который содержит всех пользователей.

Дополнительные материалы

  1. PSR7
Мы учим программированию с нуля до стажировки и работы. Попробуйте наш бесплатный курс «Введение в программирование» или полные программы обучения по Node, PHP, Python и Java.

Хекслет

Подробнее о том, почему наше обучение работает →