Выпуск №3 — Guzzle

Специальный выпуск, посвящённый Guzzle 6.

Всем привет!

Вы слушаете «Пятиминутку PHP», выпуск номер 3 — еженедельный подкаст о новостях из мира PHP, интересных постах в блогах и современных подходах к разработке.

Это специальный выпуск, посвящённый выходу Guzzle 6.

Итак, 26 мая вышел мажорный релиз библиотеки Guzzle — версия 6.0 (release notes)

Guzzle — это продвинутый HTTP клиент для PHP. Поддерживает cookie, умеет отправлять и загружать большие файлы и делать асинхронные запросы.

guzzle

Где может пригодиться Guzzle?
Например, вы хотите распарсить какой-то сайт и для этого вам нужно получить его HTML контент. Можно было бы использовать простейшую PHP функцию file_get_contents(). Но что, если вам нужно сначала получить cookie с сайта, чтобы добраться до нужной страницы, или сделать сложные манипуляции с формами, отправив post запрос и, например, приложив при этом клиентский ssl сертификат? Или вы хотите скачать контент сразу с 10 сайтов параллельно? Guzzle решает все эти вопросы в форме приятного и удобного программного интерфейса.

И это реально очень популярная библиотека. Если смотреть статистику на Packagist, то Guzzle установили более 9 млн. раз и она имеет 5 тыс. звёзд на GitHub.

В только что вышедшей версии 6 произошли значительные изменения, о которых хочу рассказать в подробностях, т.к. они касаются не только Guzzle, но и общих трендов современной PHP разработки.

Во-первых, это поддержка стандарта PSR-7 — HTTP Message Interface, о котором я подробно рассказывал во втором выпуске подкаста.

Во-вторых, ориентация на PSR-7 повлекла за собой большой внутренний рефакторинг. Поскольку Request/Response объекты стали неизменяемыми, старая система с событиями и подписчиками была заменена на систему middleware. Небольшой пример: раньше у нас был emitter с методом on, и мы могли подписаться на событие before, указав свою функцию-обработчик. А внутри этой функции можно было поменять объект запроса Request, например, добавить какие-то http заголовки. Теперь объект Request неизменяемый, как завещал нам стандарт PSR-7. И вся система с событиями тоже канула в лету. Вместо этого в версии 6 мы добавляем наш before обработчик (которых, кстати, может быть несколько) в специальный стек хэндлеров. Функции из этого стека будут вызваны перед отправкой сообщения, при этом каждая функция принимает в качестве параметра объект Request и должна вернуть объект Request (возможно, как-то модифицированный, если того требует наша бизнес-логика). Возвращённый объект Request пойдёт на вход следующей функции и так по цепочке. Это и есть система middleware.

Казалось бы, всё очень похоже на то, что было раньше с обработчиками событий и event-based системой. Но тут вся фишка в философии middleware подхода: Guzzle 6 делает наши функции обработчики чистыми, функциональными, проще тестируемыми и более прозрачными.

Если же вам по душе старая event-based система, её можно построить самостоятельно поверх middleware. Возможно, в скором времени появятся готовые решения.

В целом, поднимая холивар между еvent-based и middlware-based подходами, первый мне больше напоминает спагетти-программирование, а второй функциональное, чистое программирование. Так что Guzzle 6 — это хороший пример роста качества архитектуры.

Ещё одним следствием перехода на PSR-7 стал отказ от RingPHP. RingPHP — это отдельная библиотека, которая разрабатывалась в рамках проекта Guzzle и использовалась в Guzzle 5 для абстракции над HTTP. Не буду углубляться в детали, но в своё время это была крутая штука, вдохновлённая аналогичной библиотекой Ring из языка Clojure (а Clojure — это такой современный LISP для JVM). По сути, RingPHP — это некая абстракция над HTTP, которая стала не нужна, т.к. теперь у нас есть новая абстракция под названием PSR-7.

Реализация промисов была заменена на свою собственную. Раньше использовались промисы из проекта ReactPHP и они обладали некоторыми недостатками. Например, они были рекурсивными и, как следствие, порой съедали много стека (замечу, что в самом проекте ReactPHP также наблюдались телодвижения по отказу от рекурсии в промисах). Помимо этого старые промисы приходилось оборачивать в футуры из RingPHP. В итоге отказавшись и от RingPHP, и от ReactPHP промисов стек технологий стал проще, а код надёжнее и быстрее.

В предыдущей версии Guzzle были отдельные классы Query и QueryParser. Теперь они ликвидированы, а строку запроса нужно указывать именно как строку, а не как объект некоего класса Query — капитан очевидность одобряет! Либо можно передать ассоциативный массив и Guzzle под капотом применит к нему нативную PHP функцию http_build_query. Если же вам нужен какой-то особый метод формирования строки запроса, то для вас существуют пара функций хелперов в неймспейсе GuzzleHttp\Psr7: parse_query и build_query, которые позволяют применить различные RFC к кодированию строки запроса. По сути, эти функции вобрали в себя часть того функционала, который раньше был в отдельных классах Query и QueryParser.

Класс Mimetypes был заменён на две функции в том же неймспейсе GuzzleHttp\Psr7: mimetype_from_extension и mimetype_from_filename.

Статические функции из класса Utils были перемещены в обычные функции в неймспейсе GuzzleHttp.

Я перечислил эти небольшие изменения, чтобы показать общий тренд: код становится ближе к нативному PHP, классы заменяются на обычные функции, и это мне нравится. Может показаться, что мы возвращаемся во времена процедурного программирования аля WordPress. На самом деле всё развивается по спирали. Только теперь мы стали ближе не к процедурному, а к функциональному подходу и неизменяемым данным.

Подводя итог, хочу сказать, что Guzzle — это отличный пример современного PHP кода.

На этом завершаю сегодняшний выпуск подкаста и, чтобы вы не скучали до следующего, рекомендую послушать 20-й эпизод подкаста PHP Town Hall посвященный Sculpin, Guzzle и PSR-7. В гостях Майкл Доулинг — автор Guzzle собственной персоной. Кстати, Майкл работает в команде Amazon Web Services над AWS SDK для PHP.

Попробуйте библиотеку Guzzle в своём следующем проекте и вы не пожалеете!