XylotrechusZ
<?php
namespace Analyst;
use Exception;
use Analyst\Contracts\HttpClientContract;
use Analyst\Contracts\RequestorContract;
class ApiRequestor implements RequestorContract
{
/**
* Supported http client
*
* @var HttpClientContract
*/
protected $httpClient;
/**
* @var string
*/
protected $clientId;
/**
* @var string
*/
protected $clientSecret;
/**
* @var string
*/
protected $apiBase;
/**
* Default headers to be sent
*
* @var array
*/
protected $defaultHeaders = [
'accept' => 'application/json',
'content-type' => 'application/json'
];
/**
* Prioritized http clients
*
* @var array
*/
protected $availableClients = [
'Analyst\Http\WordPressHttpClient',
'Analyst\Http\CurlHttpClient',
'Analyst\Http\DummyHttpClient',
];
/**
* ApiRequestor constructor.
* @param $id
* @param $secret
* @param $apiBase
* @throws \Exception
*/
public function __construct($id, $secret, $apiBase)
{
$this->clientId = $id;
$this->clientSecret = $secret;
$this->setApiBase($apiBase);
$this->httpClient = $this->resolveHttpClient();
}
/**
* Set api base url
*
* @param $url
*/
public function setApiBase($url)
{
$this->apiBase = $url;
}
/**
* Get request
*
* @param $url
* @param array $headers
* @return mixed
*/
public function get($url, $headers = [])
{
return $this->request('GET', $url, null, $headers);
}
/**
* Post request
*
* @param $url
* @param $body
* @param array $headers
* @return mixed
*/
public function post($url, $body = [], $headers = [])
{
return $this->request('POST', $url, $body, $headers);
}
/**
* Put request
*
* @param $url
* @param $body
* @param array $headers
* @return mixed
*/
public function put($url, $body = [], $headers = [])
{
return $this->request('PUT', $url, $body, $headers);
}
/**
* Delete request
*
* @param $url
* @param array $headers
* @return mixed
*/
public function delete($url, $headers = [])
{
return $this->request('DELETE', $url, null, $headers);
}
/**
* Make request to api
*
* @param $method
* @param $url
* @param array $body
* @param array $headers
* @return mixed
*/
protected function request($method, $url, $body = [], $headers = [])
{
$fullUrl = $this->resolveFullUrl($url);
$date = date('r', time());
$headers['date'] = $date;
$headers['signature'] = $this->resolveSignature($this->clientSecret, $method, $fullUrl, $body, $date);
// Lowercase header names
$headers = $this->prepareHeaders(
array_merge($headers, $this->defaultHeaders)
);
$response = $this->httpClient->request($method, $fullUrl, $body, $headers);
// TODO: Check response code and take actions
return $response;
}
/**
* Set one default header
*
* @param $header
* @param $value
*/
public function setDefaultHeader($header, $value)
{
$this->defaultHeaders[
$this->resolveValidHeaderName($header)
] = $value;
}
/**
* Resolves supported http client
*
* @return HttpClientContract
* @throws Exception
*/
protected function resolveHttpClient()
{
$clients = array_filter($this->availableClients, $this->guessClientSupportEnvironment());
if (!isset($clients[0])) {
throw new Exception('There is no http client which this application can support');
}
// Instantiate first supported http client
return new $clients[0];
}
/**
* This will filter out clients which is not supported
* by the current environment
*
* @return \Closure
*/
protected function guessClientSupportEnvironment()
{
return function ($client) {
return forward_static_call([$client, 'hasSupport']);
};
}
/**
* Resolves valid header name
*
* @param $headerName
* @return string
*/
private function resolveValidHeaderName($headerName)
{
return strtolower($headerName);
}
/**
* Lowercase header names
*
* @param $headers
* @return array
*/
private function prepareHeaders($headers)
{
return array_change_key_case($headers, CASE_LOWER);
}
/**
* Sign request
*
* @param $key
* @param $method
* @param $url
* @param $body
* @param $date
*
* @return false|string
*/
private function resolveSignature($key, $method, $url, $body, $date)
{
$string = implode('\n', [$method, $url, md5(json_encode($body)), $date]);
$contentSecret = hash_hmac('sha256', $string, $key);
return sprintf('%s:%s', $this->clientId, $contentSecret);
}
/**
* Compose full url
*
* @param $url
* @return string
*/
private function resolveFullUrl($url)
{
return sprintf('%s/%s', $this->apiBase, trim($url, '/'));
}
}