Httplug — абстрагування від клієнта HTTP для PHP

HTTPlug
У минулому році PHP-FIG прийняла стандарт PSR-7, що описує роботу з повідомленнями HTTP. Хороша стаття про цей стандарт та його застосування була на Хабре. І хоча PSR-7 — великий крок вперед, йому не вистачає логічного продовження — загального інтерфейсу клієнтів HTTP. Створенням відсутнього компонента зайнялася група PHP-HTTP.

Проблема

Використовуючи інтерфейси PSR-7 ви можете абстрагуватися від конкретної реалізації запитів і відповідей HTTP. Але тільки до тих пір, поки вам не знадобиться зробити запит самостійно. Тут вам доведеться як і раніше жорстко прив'язуватися до якихось реалізацій. При написанні програми це природно. При написанні бібліотеки — зовсім недобре. Наприклад, наш сайт взаємодіє з п'ятьма іншими службами за допомогою офіційних та неофіційних бібліотек і SDK. І кожна з них використовує власного клієнта HTTP. Хтось Guzzle, хтось cURL, хтось просто file_get_contents. П'ять різних клієнтів HTTP в одному додатку! У кожного свої особливості, свої обмеження, свої можливості налаштування. Було б здорово замінити цей зоопарк одним єдиним клієнтом, який використовувався б усіма компонентами програми та бібліотеками?

Httplug

Основна розробка групи — набір інтерфейсів Httplug, що дозволяє бібліотекам абстрагуватися від конкретного клієнта 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
будь-яка на вибір реалізація, яку можна буде використовувати під час налагодження і яка підтягне всі необхідні для розробки інтерфейси і класи. Для налагодження також може знадобитися реалізація PSR-7, наприклад guzzlehttp/psr7.
Припустимо, що ваша бібліотека повинна працювати з деяким 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);
// ...
}

Ось, за великим рахунком, і все, що потрібно. Тепер ваша бібліотека не залежить від конкретного клієнта, і її користувач зможе вибрати той, який йому більше підходить. Подивимося, як це робиться.
Використання у додатку
Для використання описаної вище бібліотеки розробнику програми знадобиться вибрати одну з реалізацій клієнта, реалізацію PSR-7 і підключити їх разом з вашою бібліотекою:
$ composer require php-http/guzzle6-adapter
$ composer require ваша/бібліотека

guzzle6-adapter
автоматично підтягне
guzzlehttp/psr7
, тому його окремо вказувати необов'язково.
Ще потрібно php-http/message в якості моста до
guzzlehttp/psr7
:
$ composer require php-http/message

Далі потрібно створити клієнта, адаптер, фабрику запитів і передати два останніх в конструктор ApiClient:
use GuzzleHttp\Client as GuzzleClient;
use Http\Adapter\Guzzle6\Client as GuzzleAdapter;
use Http\Message\MessageFactory\GuzzleMessageFactory;

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

ApiClient готовий до роботи. Цей же об'єкт ($adapter) можна передати в усі компоненти, яким потрібен клієнт HTTP.
Якщо у майбутньому виникне потреба замінити Guzzle на щось інше, то потрібно переписати тільки ось цю частину коду. Весь інший код, який працює з HTTP, чіпати не доведеться.

Що ще є цікавого?

Повністю з наявними пакетами та їх можливостями можна ознайомитися в офіційній документації, я ж зазначу лише деякі цікаві на мій погляд.
Асинхронні запити
Спеціальний інтерфейс HttpAsyncClient дозволяє виконувати запити асинхронно, використовуючи механізм обіцянок.
Автовизначення реалізацій (Discovery)
Система автовизначення на основі Puli дозволяє отримувати об'єкти клієнта і фабрик без прив'язки до конкретних реалізацій:
$httpClient = HttpClientDiscovery::find();

Система розширень (плагінів)
Дозволяє додавати наскрізну функціональність до всіх або тільки деяким клієнтам HTTP. Ось деякі приклади:
HttplugBundle
Пакет для Symfony, що включає підтримку декількох клієнтів, розширення і налагоджувальної панелі Symfony.
висновок
Проект знаходиться на початку свого шляху, але вже досить стабільний для використання в бойових умовах. Хлопці ставлять перед собою амбітну мету — домогтися прийняття їх розробок в якості рекомендації PSR.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.