Account
11 months ago
Cache
11 months ago
Contracts
11 months ago
Core
11 months ago
Http
11 months ago
Notices
11 months ago
Analyst.php
11 months ago
ApiRequestor.php
11 months ago
ApiResponse.php
11 months ago
Collector.php
11 months ago
Mutator.php
11 months ago
helpers.php
11 months ago
ApiRequestor.php
258 lines
| 1 | <?php |
| 2 | |
| 3 | namespace Analyst; |
| 4 | |
| 5 | use Exception; |
| 6 | use Analyst\Contracts\HttpClientContract; |
| 7 | use Analyst\Contracts\RequestorContract; |
| 8 | |
| 9 | class ApiRequestor implements RequestorContract |
| 10 | { |
| 11 | /** |
| 12 | * Supported http client |
| 13 | * |
| 14 | * @var HttpClientContract |
| 15 | */ |
| 16 | protected $httpClient; |
| 17 | |
| 18 | /** |
| 19 | * @var string |
| 20 | */ |
| 21 | protected $clientId; |
| 22 | |
| 23 | /** |
| 24 | * @var string |
| 25 | */ |
| 26 | protected $clientSecret; |
| 27 | |
| 28 | /** |
| 29 | * @var string |
| 30 | */ |
| 31 | protected $apiBase; |
| 32 | |
| 33 | /** |
| 34 | * Default headers to be sent |
| 35 | * |
| 36 | * @var array |
| 37 | */ |
| 38 | protected $defaultHeaders = [ |
| 39 | 'accept' => 'application/json', |
| 40 | 'content-type' => 'application/json' |
| 41 | ]; |
| 42 | |
| 43 | /** |
| 44 | * Prioritized http clients |
| 45 | * |
| 46 | * @var array |
| 47 | */ |
| 48 | protected $availableClients = [ |
| 49 | 'Analyst\Http\WordPressHttpClient', |
| 50 | 'Analyst\Http\CurlHttpClient', |
| 51 | 'Analyst\Http\DummyHttpClient', |
| 52 | ]; |
| 53 | |
| 54 | /** |
| 55 | * ApiRequestor constructor. |
| 56 | * @param $id |
| 57 | * @param $secret |
| 58 | * @param $apiBase |
| 59 | * @throws \Exception |
| 60 | */ |
| 61 | public function __construct($id, $secret, $apiBase) |
| 62 | { |
| 63 | $this->clientId = $id; |
| 64 | $this->clientSecret = $secret; |
| 65 | |
| 66 | $this->setApiBase($apiBase); |
| 67 | |
| 68 | $this->httpClient = $this->resolveHttpClient(); |
| 69 | } |
| 70 | |
| 71 | /** |
| 72 | * Set api base url |
| 73 | * |
| 74 | * @param $url |
| 75 | */ |
| 76 | public function setApiBase($url) |
| 77 | { |
| 78 | $this->apiBase = $url; |
| 79 | } |
| 80 | |
| 81 | /** |
| 82 | * Get request |
| 83 | * |
| 84 | * @param $url |
| 85 | * @param array $headers |
| 86 | * @return mixed |
| 87 | */ |
| 88 | public function get($url, $headers = []) |
| 89 | { |
| 90 | return $this->request('GET', $url, null, $headers); |
| 91 | } |
| 92 | |
| 93 | /** |
| 94 | * Post request |
| 95 | * |
| 96 | * @param $url |
| 97 | * @param $body |
| 98 | * @param array $headers |
| 99 | * @return mixed |
| 100 | */ |
| 101 | public function post($url, $body = [], $headers = []) |
| 102 | { |
| 103 | return $this->request('POST', $url, $body, $headers); |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | * Put request |
| 108 | * |
| 109 | * @param $url |
| 110 | * @param $body |
| 111 | * @param array $headers |
| 112 | * @return mixed |
| 113 | */ |
| 114 | public function put($url, $body = [], $headers = []) |
| 115 | { |
| 116 | return $this->request('PUT', $url, $body, $headers); |
| 117 | } |
| 118 | |
| 119 | /** |
| 120 | * Delete request |
| 121 | * |
| 122 | * @param $url |
| 123 | * @param array $headers |
| 124 | * @return mixed |
| 125 | */ |
| 126 | public function delete($url, $headers = []) |
| 127 | { |
| 128 | return $this->request('DELETE', $url, null, $headers); |
| 129 | } |
| 130 | |
| 131 | /** |
| 132 | * Make request to api |
| 133 | * |
| 134 | * @param $method |
| 135 | * @param $url |
| 136 | * @param array $body |
| 137 | * @param array $headers |
| 138 | * @return mixed |
| 139 | */ |
| 140 | protected function request($method, $url, $body = [], $headers = []) |
| 141 | { |
| 142 | $fullUrl = $this->resolveFullUrl($url); |
| 143 | |
| 144 | $date = date('r', time()); |
| 145 | |
| 146 | $headers['date'] = $date; |
| 147 | $headers['signature'] = $this->resolveSignature($this->clientSecret, $method, $fullUrl, $body, $date); |
| 148 | |
| 149 | // Lowercase header names |
| 150 | $headers = $this->prepareHeaders( |
| 151 | array_merge($headers, $this->defaultHeaders) |
| 152 | ); |
| 153 | |
| 154 | $response = $this->httpClient->request($method, $fullUrl, $body, $headers); |
| 155 | |
| 156 | // TODO: Check response code and take actions |
| 157 | |
| 158 | return $response; |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Set one default header |
| 163 | * |
| 164 | * @param $header |
| 165 | * @param $value |
| 166 | */ |
| 167 | public function setDefaultHeader($header, $value) |
| 168 | { |
| 169 | $this->defaultHeaders[ |
| 170 | $this->resolveValidHeaderName($header) |
| 171 | ] = $value; |
| 172 | } |
| 173 | |
| 174 | /** |
| 175 | * Resolves supported http client |
| 176 | * |
| 177 | * @return HttpClientContract |
| 178 | * @throws Exception |
| 179 | */ |
| 180 | protected function resolveHttpClient() |
| 181 | { |
| 182 | $clients = array_filter($this->availableClients, $this->guessClientSupportEnvironment()); |
| 183 | |
| 184 | if (!isset($clients[0])) { |
| 185 | throw new Exception('There is no http client which this application can support'); |
| 186 | } |
| 187 | |
| 188 | // Instantiate first supported http client |
| 189 | return new $clients[0]; |
| 190 | } |
| 191 | |
| 192 | /** |
| 193 | * This will filter out clients which is not supported |
| 194 | * by the current environment |
| 195 | * |
| 196 | * @return \Closure |
| 197 | */ |
| 198 | protected function guessClientSupportEnvironment() |
| 199 | { |
| 200 | return function ($client) { |
| 201 | return forward_static_call([$client, 'hasSupport']); |
| 202 | }; |
| 203 | } |
| 204 | |
| 205 | /** |
| 206 | * Resolves valid header name |
| 207 | * |
| 208 | * @param $headerName |
| 209 | * @return string |
| 210 | */ |
| 211 | private function resolveValidHeaderName($headerName) |
| 212 | { |
| 213 | return strtolower($headerName); |
| 214 | } |
| 215 | |
| 216 | /** |
| 217 | * Lowercase header names |
| 218 | * |
| 219 | * @param $headers |
| 220 | * @return array |
| 221 | */ |
| 222 | private function prepareHeaders($headers) |
| 223 | { |
| 224 | return array_change_key_case($headers, CASE_LOWER); |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * Sign request |
| 229 | * |
| 230 | * @param $key |
| 231 | * @param $method |
| 232 | * @param $url |
| 233 | * @param $body |
| 234 | * @param $date |
| 235 | * |
| 236 | * @return false|string |
| 237 | */ |
| 238 | private function resolveSignature($key, $method, $url, $body, $date) |
| 239 | { |
| 240 | $string = implode('\n', [$method, $url, md5(json_encode($body)), $date]); |
| 241 | |
| 242 | $contentSecret = hash_hmac('sha256', $string, $key); |
| 243 | |
| 244 | return sprintf('%s:%s', $this->clientId, $contentSecret); |
| 245 | } |
| 246 | |
| 247 | /** |
| 248 | * Compose full url |
| 249 | * |
| 250 | * @param $url |
| 251 | * @return string |
| 252 | */ |
| 253 | private function resolveFullUrl($url) |
| 254 | { |
| 255 | return sprintf('%s/%s', $this->apiBase, trim($url, '/')); |
| 256 | } |
| 257 | } |
| 258 |