Introduction¶
Install¶
The plugin client and the core plugins are available in the php-http/client-common package:
$ composer require php-http/client-common
New in version 1.1: The plugins were moved to the clients-common package in version 1.1.
If you work with version 1.0, you need to require the separate php-http/plugins package
and the namespace is Http\Client\Plugin
instead of Http\Client\Common
How it works¶
In the plugin package, you can find the following content:
the
PluginClient
itself which acts as a wrapper around any kind of HTTP client (sync/async);the
Plugin
interface;a set of core plugins (see the full list in the left side navigation).
The PluginClient
accepts an HTTP client implementation and an array of plugins.
Let’s see an example:
use Http\Discovery\HttpClientDiscovery;
use Http\Client\Common\PluginClient;
use Http\Client\Common\Plugin\RetryPlugin;
use Http\Client\Common\Plugin\RedirectPlugin;
$retryPlugin = new RetryPlugin();
$redirectPlugin = new RedirectPlugin();
$pluginClient = new PluginClient(
HttpClientDiscovery::find(),
[
$retryPlugin,
$redirectPlugin,
]
);
The PluginClient
accepts and implements both Http\Client\HttpClient
and
Http\Client\HttpAsyncClient
, so you can use both ways to send a request. In
case the passed client implements only one of these interfaces, the PluginClient
“emulates” the other behavior as a fallback.
It is important to note that the order of plugins matters. During the request, plugins are executed in the order they have been specified in the constructor, from first to last. Once a response has been received, the plugins are called again in reversed order, from last to first.
For our previous example, the execution chain will look like this:
Request ---> PluginClient ---> RetryPlugin ---> RedirectPlugin ---> HttpClient ----
| (processing call)
Response <--- PluginClient <--- RetryPlugin <--- RedirectPlugin <--- HttpClient <---
In order to achieve the intended behavior in the global process, you need to pay attention to what each plugin does and define the correct order accordingly.
For example, the RetryPlugin
should probably be at the end of the chain to
keep the retry process as short as possible. However, if one of the other
plugins is doing a fragile operation that might need a retry, place the retry
plugin before that.
The recommended way to order plugins is the following:
Plugins that modify the request should be at the beginning (like Authentication or Cookie Plugin);
Plugins which intervene in the workflow should be in the “middle” (like Retry or Redirect Plugin);
Plugins which log information should be last (like Logger or History Plugin).
Note
There can be exceptions to these rules. For example, for security reasons you might not want
to log the authentication information (like Authorization
header) and choose to put the
Authentication Plugin after the Logger Plugin.
Configuration Options¶
The PluginClient
accepts an array of configuration options to tweak its behavior.
max_restarts
: int (default 10)¶
To prevent issues with faulty plugins or endless redirects, the PluginClient
injects a security
check to the start of the plugin chain. If the same request is restarted more than specified by
that value, execution is aborted and an error is raised.
debug_plugins
: array of Plugin¶
The debug plugins are injected between each normal plugin. This can be used to log the changes each plugin does on the request and response objects.
Libraries that Require Plugins¶
When writing a library based on HTTPlug, you might require
specific plugins to be active. The recommended way for doing this is to provide a factory method
for the PluginClient
that library users should use. This allows them to inject their own
plugins or configure a different client. For example:
$myApiClient = new My\Api\Client('https://api.example.org', My\Api\HttpClientFactory::create('john', 's3cr3t'));
use Http\Client\HttpClient;
use Http\Client\Common\Plugin;
use Http\Client\Common\Plugin\AuthenticationPlugin;
use Http\Client\Common\Plugin\ErrorPlugin;
use Http\Discovery\HttpClientDiscovery;
class HttpClientFactory
{
/**
* Build the HTTP client to talk with the API.
*
* @param string $user Username for the application on the API
* @param string $pass Password for the application on the API
* @param Plugin[] $plugins List of additional plugins to use
* @param HttpClient $client Base HTTP client
*
* @return HttpClient
*/
public static function create($user, $pass, array $plugins = [], HttpClient $client = null)
{
if (!$client) {
$client = HttpClientDiscovery::find();
}
$plugins[] = new ErrorPlugin();
$plugins[] = new AuthenticationPlugin(
// This API has it own authentication algorithm
new ApiAuthentication(Client::AUTH_OAUTH_TOKEN, $user, $pass)
);
return new PluginClient($client, $plugins);
}
}