Эта статья была рецензирована Эндрю Картером . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!
Группа по взаимодействию PHP Framework (PHP-FIG) относительно недавно одобрила еще одно предложение, PSR-7: интерфейс HTTP-сообщений . Документ кристаллизует HTTP-сообщения в 7 интерфейсов, которые должна реализовывать библиотека PHP, если они подписываются на спецификацию. В PSR-7 By Example Мэтью Вейер О’Финни, редактор PSR, дает интересный обзор спецификации. Так что же это?
Если вы bbc.co.uk
в своем браузере, вы сразу перейдете на домашнюю страницу BBC, но между моментом, когда браузер отправил HTTP-запрос на сервер и получил ответ, мог произойти ряд вещей.
Вот пример необработанного запроса.
GET / HTTP/1.1 Host: bbc.co.uk User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) Accept: */* Referer:
Он всегда состоит из строки запроса (GET / HTTP / 1.1), ряда строк поля заголовка, перечисленных как <key>: value
, одной пустой строки и необязательного тела для любой полезной нагрузки данных (например, параметров запроса или данных публикации). ).
Пустая строка после нуля или более строк заголовка должна быть CRLF сама по себе. Это означает 2 символа — ASCII 13 (возврат каретки), за которым следует ASCII 10 (перевод строки) или \r\n
.
Давайте отправим этот запрос из командной строки через curl и посмотрим ответ:
curl -i -H "Host: bbc.co.uk" -H "User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" -H "Accept: */*" -X GET http://bbc.co.uk HTTP/1.1 301 Moved Permanently Content-Type: text/html Date: Sun, 02 Oct 2016 20:49:42 GMT Location: http://www.bbc.co.uk/ Connection: Keep-Alive Content-Length: 0
Переехал? Был редирект. Тогда давайте проследим путь и сделаем запрос к http://www.bbc.co.uk/
:
curl -i -H "Host: www.bbc.co.uk" -H "User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" -H "Accept: */*" -X GET http://www.bbc.co.uk | less HTTP/1.1 200 OK Server: nginx Content-Type: text/html; charset=utf-8 ETag: W/"29c44-MXo+6t7MoPRad358MSWqwA" X-Frame-Options: SAMEORIGIN x-origin-route: xrt-ext Content-Length: 171076 Date: Sun, 02 Oct 2016 20:54:27 GMT Connection: keep-alive Set-Cookie: BBC-UID=15c73fe11704a0731344da5ec13869204c1a22a0c7b444d60a708762e631ac0c0Mozilla/5.0%20(compatible%3b%20MSIE%209.0%3b%20Windows%20NT%206.1%3b%20Trident/5.0); expires=Thu, 01-Oct-20 20:54:27 GMT; path=/; domain=.bbc.co.uk X-Cache-Action: HIT X-Cache-Hits: 1223 X-Cache-Age: 55 Cache-Control: private, max-age=0, must-revalidate Vary: Accept-Encoding, X-CDN, X-BBC-Edge-Scheme <!DOCTYPE html> <!--[if lte IE 9]> <html lang="en-GB" class="no-js no-flexbox no-flexboxlegacy"> <![endif]--> <!--[if gt IE 9]><!--> <html lang="en-GB" class="no-js"> <!--<![endif]--> <head>
Это больше походит на это. Первая строка, HTTP/1.1 200 OK
, это строка состояния. Затем у нас есть заголовки, похожие на запросы: <key>: value
, пустая строка и тело ответа. Примечание: мы пропустили вывод через less
чтобы мы могли видеть первую часть ответа.
Запросы и ответы можно разбить на строку сообщения, несколько строк заголовка и строки тела. Общности могут быть абстрагированы в интерфейсе ( MessageInterface
), который запрос ( RequestInterface
) и ответ ( ResponseInterface
) могут расширять с их отличным видом сообщения HTTP.
PHP работает не только в веб-среде, а веб-запросы могли исходить из API. ServerRequestInterface
был разработан, чтобы заботиться о других типах HTTP-запросов.
Три других интерфейса являются дополнительной абстракцией определенных аспектов в сообщениях. Оглядываясь назад на строку сообщения запроса:
GET / HTTP/1.1
Это включает в себя:
-
МЕТОД: Хотя RFC 2616 определяет безопасные и идемпотентные типы методов, для общих применений достаточно идентифицировать их по имени — GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD и TRACE. CONNECT зарезервирован для использования с прокси, который может динамически переключаться в туннель, например, туннелирование SSL.
-
TARGET: Это URI или наша цель запроса, и здесь все становится немного интереснее:
- origin-form — путь и строка запроса URI. Строка запроса может присутствовать или не присутствовать.
- Абсолютная форма — абсолютный URI.
- author-form — авторитетная часть URI, состоящая максимум из 3-х частей — user-info (необязательно), хост и порт (необязательно). Информация пользователя может также потребовать пароль — user: password. Мы получаем шаблон user: password @ host: port. Информация о пользователе может также потребовать
- звездочка-форма — просто строка, *
В итоге получается
scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]
. Эта часть сообщения запроса была абстрагирована отUriInterface
. -
ВЕРСИЯ: Здесь ограниченный выбор, поскольку HTTP / 1.1 является текущей версией. До этого у нас был HTTP / 1.0, а следующая черновая версия — HTTP / 2.0
Кроме того, загрузка файлов требует особого внимания. В средах, отличных от SAPI, переменная среды $_FILES
пуста, а в некоторых ситуациях, таких как запросы не POST, $_FILES
не заполняется вообще. UploadedFileInterface
был разработан для обеспечения более согласованного взаимодействия с файлами.
Сообщение (запрос или ответ) должно быть в состоянии эффективно обрабатывать большие данные как для клиента, так и для сервера. PHP имеет встроенные потоки с 4.3.0. Часть StreamInterface
PSR-7 предоставляет оболочку для общих операций и сериализации всего потока в строку.
проблемы
Путь к PSR-7 был проложен сильными дебатами и различными мнениями на каждом этапе пути.
-
Неизменяемые объекты — неизменность или изменчивость объектов была одной из самых горячих спорных точек, и PSR-7 в конечном итоге согласился на это:
Предложение моделирует сообщения и URI как объекты значений.
Сообщения — это значения, в которых идентичность представляет собой совокупность всех частей сообщения; изменение любого аспекта сообщения — это, по сути, новое сообщение. Это само определение объекта значения. Практика, при которой изменения приводят к появлению нового экземпляра, называется неизменностью и представляет собой функцию, предназначенную для обеспечения целостности заданного значения.
Это означает, что каждый раз, когда вы вносите какие-либо изменения в объект сообщения, вы получаете новую копию. Сложность URI, заголовков и потоков требует гарантии того, что все коллабораторы предложат полное принятие неизменности разработчикам интерфейсов.
С неизменяемостью любое изменение состояния требует назначения результата.
$request = $request->setHeader('Cache-Control', 'public');
Любой метод, который изменяет состояние текущего сообщения, возвращает его экземпляр с внесенными изменениями. До тех пор, пока присваивается результат, вы можете связать любое количество изменений в похожий синтаксис.
$request = $request ->setHeader('Cache-Control', 'public') ->addHeader('Cache-Control', 'max-age=18600') ->setStatus(200);
Методы, имеющие префикс «with», должны соответствовать требованию неизменности. Проблема заключается в том, что поддержка изменчивости в одном из интерфейсов сообщений означает принудительное применение его во всех других интерфейсах.
С другой стороны, было подчеркнуто, что объекты PSR-7 не являются неизменными, как они обычно думают. Стоит отметить, что подобные реализации HTTP-объектов в Ruby и Node изменчивы по своему дизайну. Итак, PHP в хорошей компании.
-
Номенклатура — эти объекты разработаны как интерфейсы. Разве называть это MessageInterface лишним? Подпись метода, принимающая запрос и ответ, оказывается слишком длинной. Сравните следующее:
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) : ResponseInterface { }
public function __invoke(ServerRequest $request, Response $response, callable $next) : Response { }
Псевдоним — это рекомендуемое решение для тех, кто предпочитает удалять суффиксы интерфейса.
-
Промежуточное программное обеспечение — у нас есть интерфейсы сообщений, которые как ингредиенты для приготовления торта, идеально продуманные. Я хочу съесть торт, но я не знаю, как испечь. Как насчет рецепта? PSR-7 предписывает только стандарт для определения запроса и ответа. Как мы переходим от запроса к ответу? Тот «посредник», который выполняет работу между запросом и ответом, называется промежуточным ПО.
Следующим шагом будет унификация того, как подключить интерфейсы PSR-7, чтобы приложения и платформы, соответствующие им, могли быть полностью заменены. Эта часть обсуждения, которая ограничивается предоставлением шаблона для совместимой реализации PSR-7, была перенесена на отдельную PSR-15: промежуточное ПО HTTP.
использование
В ряде библиотек и сред добавлена поддержка PSR-7 по-разному.
-
Symfony — Компонент HttpFoundation является одной из самых популярных абстракций ООП спецификации HTTP до PHP-FIG. С появлением PSR-7 Symfony выбрал мост PSR-7, который преобразует объекты
HttpFoundation
из объектов в объекты, реализующие интерфейсы сообщений PSR-7, и обратно. -
Zend Framework (ZF) — они разработали пакет Composer,
zendframework/zend-diactoros
, для реализации интерфейсов HTTP-сообщений, что неудивительно, поскольку редактор PSR-7 является лидером проекта ZF. Diactoros идет дальше, включив сервер, аналогичный серверу http.Server в NodeJS. -
Slim — PSR 7 и Value Objects описывает гибкий подход, который заставляет Slim принять любую реализацию PSR-7. То есть, если того, что предоставлено Слимом, недостаточно.
-
Guzzle — Будучи клиентской библиотекой HTTP, PSR-7 имеет большое значение для этой библиотеки. Они
guzzlehttp/psr7
Composerguzzlehttp/psr7
, реализацию сообщений PSR-7, на которую они полагаются. Guzzle и PSR-7 дают превосходный обзор их взгляда на спецификацию. -
Aura — Они включили Aura.Router , реализацию PSR-7 для веб-маршрутизаторов, в свою коллекцию независимых библиотечных пакетов. Все объекты маршрутизатора управляются
RouterContainer
из которого вы извлекаете экземпляр объектаMap
. Каждый метод этого объектаMap
может принимать интерфейс сообщения HTTP в качестве одного из своих аргументов. -
HTTPlug — пакет интерфейсов, позволяющий разработчикам библиотек и приложений создавать HTTP-клиенты, полностью совместимые с PSR-7. HttpClient отправляет запрос PSR-7 и возвращает ответ PSR-7. См. Освобождение от Guzzle5 с PHP-HTTP и HTTPlug для практического использования этого пакета.
Packagist имеет список реализаций PSR-7 с широким диапазоном популярности или признания. Тем не менее, разработчик по-прежнему имеет следующие варианты использования:
-
Direct — Как указано в спецификации, в то время как Psr \ Http \ Message \ MessageInterface МОЖЕТ быть реализован напрямую, разработчики ДОЛЖНЫ реализовать Psr \ Http \ Message \ RequestInterface и Psr \ Http \ Message \ ResponseInterface. Самый простой способ — установить пакет с помощью Composer.
composer require psr/http-message
-
Косвенный — интерфейсы используются косвенно через адаптер. Например, поддержка PSR-7 в Symfony осуществляется через HTTP Message Bridge PSR, библиотеку, предназначенную для преобразования объектов
Request
иResponse
Symfony в объекты, полностью совместимые с PSR-7, и обратно из PSR-7 обратно в объекты Symfony. -
Частичный — вас могут заинтересовать более общие интерфейсы, такие как StreamInterface, UriInterface и UploadedFileInterface. Ничто не мешает вам реализовать их в не-HTTP-контексте сообщений. Пакет доступен на Packagist, а Composer — ваш друг.
Вывод
Следует приветствовать сообщество PHP за то, что мы собрались вместе на фундаментальном принципе того, как мы взаимодействуем с HTTP-запросами и ответами и управляем ими. PSR-15 выходит за рамки этого, и интенсивность дебатов вокруг промежуточного программного обеспечения не исчезнет очень скоро, и при этом проект не должен быть принят быстро. Тем временем PSR-7 существует для всех.
Что вы думаете о PSR-7? Используете ли вы его и подписываетесь на него, или вы чувствуете, что это просто слой усложнения? Мы хотели бы услышать от вас!