Статьи

От HTTP-сообщений до PSR-7: что это такое?

Эта статья была рецензирована Эндрю Картером . Спасибо всем рецензентам 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 по-разному.

  1. Symfony — Компонент HttpFoundation является одной из самых популярных абстракций ООП спецификации HTTP до PHP-FIG. С появлением PSR-7 Symfony выбрал мост PSR-7, который преобразует объекты HttpFoundation из объектов в объекты, реализующие интерфейсы сообщений PSR-7, и обратно.

  2. Zend Framework (ZF) — они разработали пакет Composer, zendframework/zend-diactoros , для реализации интерфейсов HTTP-сообщений, что неудивительно, поскольку редактор PSR-7 является лидером проекта ZF. Diactoros идет дальше, включив сервер, аналогичный серверу http.Server в NodeJS.

  3. Slim — PSR 7 и Value Objects описывает гибкий подход, который заставляет Slim принять любую реализацию PSR-7. То есть, если того, что предоставлено Слимом, недостаточно.

  4. Guzzle — Будучи клиентской библиотекой HTTP, PSR-7 имеет большое значение для этой библиотеки. Они guzzlehttp/psr7 Composer guzzlehttp/psr7 , реализацию сообщений PSR-7, на которую они полагаются. Guzzle и PSR-7 дают превосходный обзор их взгляда на спецификацию.

  5. Aura — Они включили Aura.Router , реализацию PSR-7 для веб-маршрутизаторов, в свою коллекцию независимых библиотечных пакетов. Все объекты маршрутизатора управляются RouterContainer из которого вы извлекаете экземпляр объекта Map . Каждый метод этого объекта Map может принимать интерфейс сообщения HTTP в качестве одного из своих аргументов.

  6. HTTPlug — пакет интерфейсов, позволяющий разработчикам библиотек и приложений создавать HTTP-клиенты, полностью совместимые с PSR-7. HttpClient отправляет запрос PSR-7 и возвращает ответ PSR-7. См. Освобождение от Guzzle5 с PHP-HTTP и HTTPlug для практического использования этого пакета.

Packagist имеет список реализаций PSR-7 с широким диапазоном популярности или признания. Тем не менее, разработчик по-прежнему имеет следующие варианты использования:

  1. Direct — Как указано в спецификации, в то время как Psr \ Http \ Message \ MessageInterface МОЖЕТ быть реализован напрямую, разработчики ДОЛЖНЫ реализовать Psr \ Http \ Message \ RequestInterface и Psr \ Http \ Message \ ResponseInterface. Самый простой способ — установить пакет с помощью Composer.

     composer require psr/http-message 
  2. Косвенный — интерфейсы используются косвенно через адаптер. Например, поддержка PSR-7 в Symfony осуществляется через HTTP Message Bridge PSR, библиотеку, предназначенную для преобразования объектов Request и Response Symfony в объекты, полностью совместимые с PSR-7, и обратно из PSR-7 обратно в объекты Symfony.

  3. Частичный — вас могут заинтересовать более общие интерфейсы, такие как StreamInterface, UriInterface и UploadedFileInterface. Ничто не мешает вам реализовать их в не-HTTP-контексте сообщений. Пакет доступен на Packagist, а Composer — ваш друг.

Вывод

Следует приветствовать сообщество PHP за то, что мы собрались вместе на фундаментальном принципе того, как мы взаимодействуем с HTTP-запросами и ответами и управляем ими. PSR-15 выходит за рамки этого, и интенсивность дебатов вокруг промежуточного программного обеспечения не исчезнет очень скоро, и при этом проект не должен быть принят быстро. Тем временем PSR-7 существует для всех.

Что вы думаете о PSR-7? Используете ли вы его и подписываетесь на него, или вы чувствуете, что это просто слой усложнения? Мы хотели бы услышать от вас!