Как стать автором
Обновить

PSR-7 сегодня или немного о грустном

Наконец, нашлось время разобраться, что собой представляет PSR-7. Как известно, на сегодняшний день существует публичный интерфейс https://github.com/php-fig/http-message и две его реализации:


Итак, что можно сказать о реализациях?

Uri


Метод Psr\Http\Message\UriInterface::setScheme() предусматривает, что реализация должна поддерживать схемы «http» и «https» и может также поддерживать и другие схемы. То есть, если мне нужно быстро создать ссылку на ftp — ресусрс, mailto или file я мог бы воспользоваться уже существующим классом Uri.

К сожалению, ни одна из реализаций подобных возможностей не предоставляет. Массив в теле класса с двумя методами «http» и «https» тоскливо смотрится, учитывая какие титаны это писали.

Кроме того, ни в интерфейсе, ни в реализациях ничего не сказано о том, что метод UriInterface::withUserInfo для протокола http, по сути, является @depricated, о чем говорится в здесь.

Streams


Классы, реализующие Psr\Http\Message\StreamInterface являются, по сути, обертками для стандартных потоков PHP. Вот как аргументирует использование потоков автор Guzzle в своей статье «Несколько слов о более высоком уровне (абстракции) потоков PHP в PSR-7»:

Самые горячие споры до сих пор были вокруг решения о том, как будет представлено тело HTTP-сообщения…

… Есть несколько других вариантов, которые могли бы быть использованы для представления HTTP-сообщения:

— строка
— итераторы
— потоки PHP

Использование строки потребовало бы, чтобы все содержимое сообщения загружалось бы в память. Это не годится, если вы взаимодействуете с веб-сервисами вроде Amazon S3, где является обыденной загрузка гигабайт данных в качестве объектов хранения.

Могло бы сработать использование итераторов, но это может привести к значительному снижению производительности из-за того, что каждый вызов next() будет возвращать только один байт, что приведет к огромному количеству вызовов метода при загрузке больших файлов. Кроме того, это предоставляло бы представление тела сообщения в режиме «только для чтения».

Тем не менее, из нереализованных на сегодняшний день в Guzzle интерфейсов остается Psr\Http\Message\UploadedFileInterface.

То есть, непонятно, в чем же здесь преимущество потоков, если загрузка файлов, о которой так много говорили, осталась нереализованной. В реализации Matthew O'Phinney метод ::moveTo() реализовывается с помощью старой доброй нативной PHP фунции move_uploaded_file().

Immutable


Все уже, наверное, знают, что особенностью HTTP прослойки от PSR-7 является неизменность объектов. Вот только в двух существующих реализациях это выполняется различным образом.

Guzzle внутри методов с приставкой with проверяет, изменилось ли состояние объекта. Если оно изменилось — возвращает новый объект. Если не изменилось — возвращает экземпляр текущего. Реализация Matthew O'Phinney всегда возвращает новый объект, не проверяя при этом состояние.

Нужно сказать, что здесь во многом виноваты сами интерфейсы Psr, поскольку в описании, например UriInterface::withFragment:
Return an instance with the specified URI fragment

А вот в return того же метода:
A new instance with the specified fragment

Конечно, можно сказать, что я придираюсь, но нужно же как-то обьяснить разницу в реализации.

RequestInterface


Честно говоря, реализации этого интерфейса озадачили больше всего. Есть некоторые огрехи в самих интерфейсах, но это ерунда по сравнению с реализациями.

Так, например, метод RequestInterface::withUri описывает использование параметра $preserveHost, приведу в оригинале:
When `$preserveHost` is set to `true`, this method interacts with the Host header in the following ways:
— If the Host header is missing or empty, and the new URI contains a host component, this method MUST update the Host header in the returned request.
— If the Host header is missing or empty, and the new URI does not contain a host component, this method MUST NOT update the Host header in the returned request.
— If a Host header is present and non-empty, this method MUST NOT update the Host header in the returned request.

Может быть я чего-то не понял, но Matthew O'Phinney в реализации этого метода дает следующие комментарии:
When `$preserveHost` is set to `true`, the returned request will not update the Host header of the returned message — even if the message contains no Host header.

То есть логика изменена на полностью противоположную. И самое интересное, Guzzle делает то же самое, но без всяких комментариев.

В общем, если кратко по итогам — то на сегодняшний день весь стандарт, что интерфейсы, что реализация оставляет некоторое ощущение «сырости», хотя, возможно, это просто мое субьективное мнение.

Чтобы быть совсем уж точным, сейчас проект от Matthew O'Phinney https://github.com/phly/http приостановлен и начат ребрендинг. Хотя, по сути — это не ребрендинг, а очень серьезный рефакторинг. И, нужно сказать, что явные ошибки залатываются качественно и оперативно. Проект развивается под крылом Zend Framework, посмотреть можно здесь. Хотя, это не перекрывает некоторые архитектурные недоработки самого стандарта. Поэтому я думаю, что в самом стандарте еще предстоят изменения.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.