plus-8.x-4.x-dev/src/Http/Client.php
src/Http/Client.php
<?php
namespace Drupal\plus\Http;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Url;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Response;
/**
* Class Client.
*/
class Client extends GuzzleClient implements ClientInterface {
/**
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* @var \Drupal\plus\Http\UserAgent
*/
protected $userAgent;
/**
* {@inheritdoc}
*/
public function __construct(array $config = []) {
if (isset($config['cache_backend'])) {
if (is_string($config['cache_backend'])) {
$config['cache_backend'] = \Drupal::service($config['cache_backend']);
}
if (!($config['cache_backend'] instanceof CacheBackendInterface)) {
throw new \InvalidArgumentException('The provided configuration for "cache_backend" must be an instance of \Drupal\Core\Cache\CacheBackendInterface or the name of a service that implements this interface.');
}
$cacheBackend = $config['cache_backend'];
}
else {
$cacheBackend = \Drupal::service('cache.http_client');
}
$this->cache = $cacheBackend;
$this->userAgent = new UserAgent(isset($config['headers']['User-Agent']) ? $config['headers']['User-Agent'] : NULL);
$config['headers']['User-Agent'] = $this->userAgent;
// Ensure there is a TTL.
if (!isset($config['ttl']) || !is_numeric($config['ttl'])) {
$config['ttl'] = 0;
}
parent::__construct($config);
}
/**
* {@inheritdoc}
*/
public function addUserAgent($user_agent, $version = NULL, $url = NULL) {
$this->userAgent->add($user_agent, $version, $url);
return $this;
}
/**
* {@inheritdoc}
*/
public function getUserAgent() {
return $this->userAgent;
}
/**
* {@inheritdoc}
*/
public function cacheableRequest($method, $uri = '', array $options = []) {
$options = $this->prepareDefaults($options);
// Allow Url objects to be passed.
if ($uri instanceof Url) {
$uri = $uri->toString();
}
$cid = $options['expires'] ? implode(':', [strtolower($method), $uri, $options['hash']]) : FALSE;
// Use a cached response, if one exists.
if ($cid && ($cache = $this->cache->get($cid)) && isset($cache->data) && $cache->data instanceof CacheableResponse) {
return $cache->data;
}
try {
$response = $this->request($method, $uri, $options);
}
catch (RequestException $e) {
$response = $e->getResponse() ?: new Response(500, [], $e->getMessage());
}
catch (GuzzleException $e) {
if ($e instanceof RequestException) {
$response = $e->getResponse();
}
if (!isset($response)) {
$response = new Response(500, [], $e->getMessage());
}
}
// Transform the response into a CacheableResponse and then cache it.
$body = $response->getBody();
$cacheable_response = new CacheableResponse($body ? $body->getContents() : '', $response->getStatusCode(), $response->getHeaders());
// Override any expiration sent.
$expires = new \DateTime();
$expires->setTimestamp($options['expires']);
$cacheable_response->setExpires($expires);
// Cache the response.
if ($cid) {
$this->cache->set($cid, $cacheable_response, $options['expires']);
}
return $cacheable_response;
}
/**
* {@inheritdoc}
*
* Note: unfortunately Symfony restricts access to this method using private.
*/
protected function prepareDefaults($options) {
$defaults = $this->getConfig();
if (!empty($defaults['headers'])) {
// Default headers are only added if they are not present.
$defaults['_conditional'] = $defaults['headers'];
unset($defaults['headers']);
}
// Special handling for headers is required as they are added as
// conditional headers and as headers passed to a request ctor.
if (array_key_exists('headers', $options)) {
// Allows default headers to be unset.
if ($options['headers'] === NULL) {
$defaults['_conditional'] = NULL;
unset($options['headers']);
}
elseif (!is_array($options['headers'])) {
throw new \InvalidArgumentException('headers must be an array');
}
}
// Shallow merge defaults underneath options.
$result = $options + $defaults;
// Remove null values.
foreach ($result as $k => $v) {
if ($v === NULL) {
unset($result[$k]);
}
}
// Retrieve or create a new expiration timestamp based on the TTL, but
// don't add it to the array just yet because it would affect the hash that
// is about to be created based on the array.
if (!empty($result['expires'])) {
$expires = (int) $result['expires'] ?: 0;
unset($result['expires']);
}
else {
$expires = !empty($result['ttl']) ? $result['ttl'] + \Drupal::time()->getRequestTime() : 0;
}
// Create a hash of the array.
$result['hash'] = $expires ? Crypt::hashBase64(serialize(array_diff_key($result, ['handler' => TRUE]))) : '';
// Now set the expiration timestamp.
$result['expires'] = $expires;
return $result;
}
}
