📜 ⬆️ ⬇️

Httplug - abstraction from HTTP client for PHP

HTTPlug


Last year, PHP-FIG adopted the PSR-7 standard, which describes how to work with HTTP messages. Good article about this standard and its application was on Habré. And although PSR-7 is a big step forward, it lacks a logical continuation — a common HTTP client interface. The creation of the missing component took the group PHP-HTTP .


Problem


Using the PSR-7 interfaces, you can abstract away from the specific implementation of HTTP requests and responses. But only as long as you do not need to make a request yourself. Here you will still have to be tightly tied to any implementations. When writing an application it is natural. When writing a library - absolutely not good. For example, our site interacts with five third-party services using official and unofficial libraries and the SDK. And each one uses its own HTTP client. Someone Guzzle, someone cURL, someone just file_get_contents. Five different HTTP clients in one application! Each has its own characteristics, its own limitations, its own customization capabilities. Would it be great to replace this zoo with a single client that would be used by all application components and libraries?


Httplug


The main development of the group is a set of Httplug interfaces, allowing libraries to abstract away from the specific HTTP client used in the application. There are already several client implementations (sockets, cURL) and adapters (Guzzle, React). In addition, the project has developed many auxiliary packages, including a package for symfony.


And what does all this give developers?


Application in libraries


If you are writing a library that needs to fulfill HTTP requests, you no longer need to bind to a specific client. Instead, in “composer.json” you can specify:


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

php-http/client-implementation indicates that your library requires an HTTP client, php-http/curl-client — any implementation that you can use during debugging and which will pull up all the interfaces and classes required for development. Debugging may also require the implementation of a PSR-7, such as guzzlehttp / psr7 .


Suppose your library should work with some API, and the main component is the client of this API:


 class ApiClient { /** *  HTTP. */ private $httpClient; /** *   HTTP. */ private $requestFactory; public function __construct(HttpClient $httpClient, RequestFactory $requestFactory) { $this->httpClient = $httpClient; $this->requestFactory = $requestFactory; } 

Here:



Now that you need to make a request, you can write something like this:


 /** * @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); // ... } 

Here, by and large, and all that is required. Now your library does not depend on a specific client, and its user will be able to choose the one that suits him best. Let's see how this is done.


Application use


To use the library described above, the application developer will need to select one of the client implementations, the PSR-7 implementation, and connect them with your library:


 $ composer require php-http/guzzle6-adapter $ composer require / 

guzzle6-adapter will automatically guzzlehttp/psr7 up guzzlehttp/psr7 , so it's not necessary to specify it separately.


You also need php-http / message as a bridge to guzzlehttp/psr7 :


 $ composer require php-http/message 

Next, you need to create a client, adapter, query factory and transfer the last two to the ApiClient constructor:


 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 is ready to go. The same object ($ adapter) can be passed to all components that need an HTTP client.


If in the future there is a need to replace Guzzle with something else, then only this part of the code will need to be rewritten. The rest of the code that works with HTTP will not have to be touched.


What else is interesting?


Fully available packages and their capabilities can be found in the official documentation , I will only note some interesting ones in my opinion.


Asynchronous requests

The special HttpAsyncClient interface allows you to execute requests asynchronously using the promise mechanism.


Autodiscovery implementations (Discovery)

The Puli- based Autodiscover system allows you to retrieve client objects and factories without reference to specific implementations:


 $httpClient = HttpClientDiscovery::find(); 

System extensions (plug-ins)

Allows you to add end-to-end functionality to all or just some HTTP clients. Here are some examples:



HttplugBundle

A symfony package that includes support for multiple clients, extensions, and the symfony debug panel.


Finally


The project is at the beginning of its path, but is already quite stable for use in combat conditions. The guys set an ambitious goal - to achieve the adoption of their development as a recommendation PSR.


')

Source: https://habr.com/ru/post/271687/


All Articles