Выпуск №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 в своём следующем проекте и вы не пожалеете!

  • Безымянный

    Громче нада.

    • Спасибо за обратную связь, сделаем!

  • Like_Winter

    Спасибо за отличный подкаст!

    • Спасибо за поддержку! Если интересуют какие-то конкретные темы — предлагайте!

      • Like_Winter

        Лично мне было бы интересно послушать про грядущий релиз Laravel 5.1 и PHP7

        • По Laravel 5.1 уже запланировано, следите за обновлениями! Новости по PHP7 стараюсь в каждом выпуске по чуть-чуть.

      • Gemorroj

        а мне интересно про Symfony 2.7 и грядущий Symfony 3

        • Принято, поставил в план!

  • Mihail

    Замечательный подкаст, но Вам стоит поработать над подачей материала, такое ощущение, что Вам дали лист и Вы просто с него читаете, монотонно, в одном ритме.

    • Согласен, сам это слышу. Буду работать!

  • lytvynov

    У вопрос такого плана, как получить через $guzzle нормальные данные в $body при образении к русскоязычному сайту. Получаются кракозябры; подскажите пожалуйста.

    $body = $response->getBody(true);

    • Andrey Reshetchenko

      В более новых версиях (например 6.2)
      $body = (string) $response->getBody();

  • Andrey Reshetchenko

    А чем функции в неймспейме лучше статических методов утильного класса?