- PVSM.RU - https://www.pvsm.ru -

Httplug — абстрагирование от клиента HTTP для PHP

HTTPlug

В прошлом году PHP-FIG приняла стандарт PSR-7 [1], описывающий работу с сообщениями HTTP. Хорошая статья об этом стандарте и его применении [2] была на Хабре. И хотя PSR-7 — большой шаг вперёд, ему не хватает логичного продолжения — общего интерфейса клиентов HTTP. Созданием недостающего компонента занялась группа PHP-HTTP [3].

Проблема

Используя интерфейсы PSR-7 вы можете абстрагироваться от конкретной реализации запросов и ответов HTTP. Но только до тех пор, пока вам не понадобится сделать запрос самостоятельно. Тут вам придётся по-прежнему жёстко привязываться к каким-либо реализациям. При написании приложения это естественно. При написании библиотеки — совсем нехорошо. Например, наш сайт взаимодействует с пятью сторонними службами при помощи официальных и неофициальных библиотек и SDK. И каждая из них использует собственного клиента HTTP. Кто-то Guzzle, кто-то cURL, кто-то просто file_get_contents. Пять разных клиентов HTTP в одном приложении! У каждого свои особенности, свои ограничения, свои возможности настройки. Было бы здорово заменить этот зоопарк одним единственным клиентом, который использовался бы всеми компонентами приложения и библиотеками?

Httplug

Основная разработка группы — набор интерфейсов Httplug [4], позволяющий библиотекам абстрагироваться от конкретного клиента HTTP, используемого в приложении. Уже есть несколько реализаций клиентов (сокеты, cURL) и адаптеров (Guzzle, React). Кроме того в рамках проекта разработано множество вспомогательных пакетов, включая пакет для Symfony.

И что же это всё даёт разработчикам?

Применение в библиотеках

Если вы пишете библиотеку, которая должна выполнять запросы HTTP, у вас больше нет необходимости привязываться к конкретному клиенту. Вместо этого в ''composer.json'' можно указать:

{
    "require": {
        "php-http/client-implementation": "^1.0"
    },
    "require-dev": {
        "php-http/curl-client": "^1.4"
    }
}

php-http/client-implementation указывает, что вашей библиотеке требуется клиент HTTP, php-http/curl-clientлюбая на выбор [5] реализация, которую можно будет использовать во время отладки и которая подтянет все необходимые для разработки интерфейсы и классы. Для отладки также может потребоваться реализация PSR-7, например guzzlehttp/psr7 [6].

Предположим, что ваша библиотека должна работать с неким API, и главный компонент — клиент этого API:

class ApiClient
{
    /**
     * Клиент HTTP.
     */
    private $httpClient;

    /**
     * Фабрика запросов HTTP.
     */
    private $requestFactory;

    public function __construct(HttpClient $httpClient, RequestFactory $requestFactory)
    {
        $this->httpClient = $httpClient;
        $this->requestFactory = $requestFactory;
    }

Здесь:

Теперь, когда вам надо сделать запрос, можно написать что-то такое:

/**
 * @param string $uri
 * @param string $payload тело запроса
 */
public function apiCall($uri, $payload)
{
    $request = $this->requestFactory->createRequest('POST', $uri, ['content-type' => 'foo/bar'], $payload);
    $response = $this->httpClient->sendRequest($request);
    // ...
}

Вот, по большому счёту, и всё, что требуется. Теперь ваша библиотека не зависит от конкретного клиента, и её пользователь сможет выбрать тот, который ему больше подходит. Посмотрим, как это делается.

Использование в приложении

Для использования описанной выше библиотеки разработчику приложения понадобится выбрать одну из реализаций [5] клиента, реализацию PSR-7 и подключить их вместе с вашей библиотекой:

$ composer require php-http/guzzle6-adapter
$ composer require ваша/библиотека

guzzle6-adapter автоматически подтянет guzzlehttp/psr7, поэтому отдельно его указывать необязательно.

Ещё потребуется php-http/message [11] в качестве моста к guzzlehttp/psr7:

$ composer require php-http/message

Далее нужно создать клиента, адаптер, фабрику запросов и передать два последних в конструктор ApiClient:

use GuzzleHttpClient as GuzzleClient;
use HttpAdapterGuzzle6Client as GuzzleAdapter;
use HttpMessageMessageFactoryGuzzleMessageFactory;

$config = [
    // ...
];
$guzzle = new GuzzleClient($config);
$adapter = new GuzzleAdapter($guzzle);
$apiClient = new ApiClient($adapter, new GuzzleMessageFactory);

ApiClient готов к работе. Этот же объект ($adapter) можно передать во все компоненты, которым нужен клиент HTTP.

Если в будущем возникнет надобность заменить Guzzle на что-то другое, то переписать потребуется только вот эту часть кода. Весь остальной код, работающий с HTTP, трогать не придётся.

Что ещё есть интересного?

Полностью с имеющимися пакетами и их возможностями можно ознакомиться в официальной документации [3], я же отмечу только некоторые интересные на мой взгляд.

Асинхронные запросы

Специальный интерфейс HttpAsyncClient [12] позволяет выполнять запросы асинхронно, используя механизм обещаний [13].

Автообнаружение реализаций (Discovery)

Система автообнаружения [14] на основе Puli [15] позволяет получать объекты клиента и фабрик без привязки к конкретным реализациям:

$httpClient = HttpClientDiscovery::find();

Система расширений (плагинов)

Позволяет добавлять сквозную функциональность ко всем или только некоторым клиентам HTTP. Вот некоторые примеры:

HttplugBundle

Пакет для Symfony [21], включающий поддержку нескольких клиентов, расширений и отладочной панели Symfony.

В заключение

Проект находится в начале своего пути, но уже достаточно стабилен для использования в боевых условиях. Ребята ставят перед собой амбициозную цель — добиться принятия их разработок в качестве рекомендации PSR.

Автор: Mekras

Источник [22]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/php-2/146410

Ссылки в тексте:

[1] PSR-7: http://www.php-fig.org/psr/psr-7/

[2] статья об этом стандарте и его применении: http://habrahabr.ru/post/250343/

[3] PHP-HTTP: http://docs.php-http.org/en/latest/index.html

[4] Httplug: https://github.com/php-http/httplug

[5] любая на выбор: http://docs.php-http.org/en/latest/clients.html

[6] guzzlehttp/psr7: https://packagist.org/packages/guzzlehttp/psr7

[7] HttpClient: https://github.com/php-http/httplug/blob/v1.0.0/src/HttpClient.php

[8] php-http/httplug: https://packagist.org/packages/php-http/httplug

[9] RequestFactory: https://github.com/php-http/message-factory/blob/v1.0.1/src/RequestFactory.php

[10] php-http/message-factory: https://packagist.org/packages/php-http/message-factory

[11] php-http/message: https://packagist.org/packages/php-http/message

[12] HttpAsyncClient: https://github.com/php-http/httplug/blob/v1.0.0/src/HttpAsyncClient.php

[13] обещаний: http://docs.php-http.org/en/latest/components/promise.html

[14] Система автообнаружения: http://docs.php-http.org/en/latest/discovery.html

[15] Puli: http://docs.puli.io/en/latest/index.html

[16] кэширование: http://docs.php-http.org/en/latest/plugins/cache.html

[17] куки: http://docs.php-http.org/en/latest/plugins/cookie.html

[18] преобразование статусов 4xx и 5xx в исключения: http://docs.php-http.org/en/latest/plugins/error.html

[19] управление заголовками: http://docs.php-http.org/en/latest/plugins/headers.html

[20] журналирование: http://docs.php-http.org/en/latest/plugins/logger.html

[21] Пакет для Symfony: http://docs.php-http.org/en/latest/integrations/symfony-bundle.html

[22] Источник: https://habrahabr.ru/post/271687/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best