Skip to content

Instantly share code, notes, and snippets.

@gnumoksha
Last active June 10, 2020 21:35
Show Gist options
  • Save gnumoksha/b35d97613dd729315d3dc1ba0449544a to your computer and use it in GitHub Desktop.
Save gnumoksha/b35d97613dd729315d3dc1ba0449544a to your computer and use it in GitHub Desktop.
Wrap some of Guzzle's behaviours using only PSR-7, PSR-17 and PSR-18 interfaces.
<?php
declare(strict_types=1);
namespace Libs\Tools\Http;
use Http\Client\Common\Plugin\CookiePlugin;
use Http\Client\Common\Plugin\RedirectPlugin;
use Http\Client\Common\PluginClient;
use Http\Message\CookieJar;
use InvalidArgumentException;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
use Libs\Tools\Json\Json;
use function is_string;
/**
* Adapts some of Guzzle's behaviours using only PSR interfaces.
* Its give us the possibility to use any implementations for PSR interfaces while still using
* a simple Guzzle-like API.
*
* Note: 4xx and 5xx http codes will not throw an exception.
*
* @see https://github.com/guzzle/guzzle/issues/2186 Guzzle does not implements PSR 17 and PSR 18.
* @see https://github.com/guzzle/guzzle/pull/2122
* @see https://github.com/guzzle/guzzle/pull/2525
*/
class Client implements ClientInterface
{
/** @var \Psr\Http\Client\ClientInterface */
protected $wrappedClient;
/** @var \Http\Client\Common\PluginClient */
protected $pluginClient;
/** @var \Psr\Http\Message\RequestFactoryInterface */
protected $requestFactory;
/** @var \Psr\Http\Message\ResponseFactoryInterface */
protected $responseFactory;
/** @var \Psr\Http\Message\UriFactoryInterface */
protected $uriFactory;
/** @var \Psr\Http\Message\StreamFactoryInterface */
protected $streamFactory;
/** @var null|string */
protected $baseUri;
/** @var \Http\Client\Common\Plugin[] */
protected $plugins = [];
public function __construct(
ClientInterface $wrappedClient,
RequestFactoryInterface $requestFactory,
ResponseFactoryInterface $responseFactory,
UriFactoryInterface $uriFactory,
StreamFactoryInterface $streamFactory
) {
$this->wrappedClient = $wrappedClient;
$this->requestFactory = $requestFactory;
$this->responseFactory = $responseFactory;
$this->uriFactory = $uriFactory;
$this->streamFactory = $streamFactory;
$this->plugins[] = new RedirectPlugin();
$this->pluginClient = new PluginClient($wrappedClient, $this->plugins);
}
/**
* @param mixed[] $config
*
* @return static
*/
public function withConfig(array $config) : self
{
$obj = clone $this;
if (isset($config['base_uri'])) {
$obj->baseUri = $config['base_uri'];
}
if (isset($config['cookies'])) {
$value = $config['cookies'];
if ($value === true) {
$cookieJar = new CookieJar();
} else {
$cookieJar = $value;
}
$obj->plugins[] = new CookiePlugin($cookieJar);
}
$obj->pluginClient = new PluginClient($obj->wrappedClient, $obj->plugins);
return $obj;
}
/**
* {@inheritDoc}
*/
public function sendRequest(RequestInterface $request) : ResponseInterface
{
return $this->pluginClient->sendRequest($request);
}
/**
* Create and send an HTTP request.
*
* Use an absolute path to override the base path of the client, or a
* relative path to append to the base path of the client. The URL can
* contain the query string as well.
*
* @param string $method HTTP method.
* @param string|UriInterface $uri URI object or string.
* @param mixed[] $options Request options to apply.
*
* @see \GuzzleHttp\ClientInterface guzzle client interface
*/
public function request(string $method, $uri = '', array $options = []) : ResponseInterface
{
$request = $this->buildRequest($method, $uri, $options);
return $this->sendRequest($request);
}
/**
* @param string|UriInterface $uri
* @param mixed[] $options may contains the keys: headers, form_params, json, query.
*
* @return \Psr\Http\Message\ResponseInterface
* @throws \Psr\Http\Client\ClientExceptionInterface
*/
public function get($uri, array $options = []) : ResponseInterface
{
$request = $this->buildRequest('GET', $uri, $options);
return $this->sendRequest($request);
}
/**
* @param string|UriInterface $uri
* @param mixed[] $options may contains the keys: headers, form_params, json, query.
*
* @return \Psr\Http\Message\ResponseInterface
* @throws \Psr\Http\Client\ClientExceptionInterface
*/
public function post($uri, array $options = []) : ResponseInterface
{
$request = $this->buildRequest('POST', $uri, $options);
return $this->sendRequest($request);
}
/**
* @param string|UriInterface $uri
* @param mixed[] $options may contains the keys: headers, form_params, json, query.
*
* @see \GuzzleHttp\Client::applyOptions()
* @see \GuzzleHttp\Client::requestAsync()
*/
protected function buildRequest(string $method, $uri, array $options = []) : RequestInterface
{
$request = $this->requestFactory->createRequest($method, $this->buildUri($uri, $options));
if (isset($options['headers'])) {
foreach ($options['headers'] as $key => $value) {
$request = $request->withHeader($key, $value);
}
}
if (isset($options['form_params'])) {
if (isset($options['multipart'])) {
throw new InvalidArgumentException('You cannot use form_params and multipart at the same time.');
}
$request = $request->withBody(
$this->streamFactory->createStream(
http_build_query($options['form_params'], '', '&')
)
)->withHeader('Content-Type', 'application/x-www-form-urlencoded');
}
if (isset($options['json'])) {
$request = $request->withBody($this->streamFactory->createStream(Json::encode($options['json'])))
->withHeader('Content-Type', 'application/json');
}
return $request;
}
/**
* @param string|UriInterface $uri
* @param mixed[] $options may contains the keys: query.
*
* @see \GuzzleHttp\Client::applyOptions()
* @see \GuzzleHttp\Client::buildUri()
*/
protected function buildUri($uri, array $options) : UriInterface
{
if ($this->baseUri !== null) {
$uri = $this->baseUri . $uri;
}
if (is_string($uri)) {
$uri = $this->uriFactory->createUri($uri);
}
if (isset($options['query'])) {
$uri = $uri->withQuery(
http_build_query($options['query'], '', '&', PHP_QUERY_RFC3986)
);
}
return $uri;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment