Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Стандарт PSR7 Веб-разработка на PHP

Архитектура любого серверного веб-фреймворка опирается на особенности протокола 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()

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

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

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

  1. PSR7

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
Занимайтесь созданием сайтов, веб-приложений, сервисов и их интеграцией с внутренними бизнес-системами на бекенд-языке PHP
10 месяцев
с нуля
Старт 21 ноября

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»