/** * @param OptionsInterface $options * @param DataInterface $dataObject * @return DataAbstract * @throws Exception\AccessDenied * @throws Exception\AuthenticationFailure * @throws Exception\DecodingError * @throws Exception\NotFound * @throws Exception\TransferError */ public function request(OptionsInterface $options, DataInterface $dataObject) { $apiKey = $options instanceof AccessInterface ? $this->accessApiKey : $this->manageApiKey; $apiSecret = $options instanceof AccessInterface ? $this->accessApiSecret : $this->manageApiSecret; if (!$options->hasAccessKey()) { $options->setAccessKey($apiKey); } $requestType = $options->getRequestType(); $endPoint = $options->getEndPoint(); $url = $options->getApiBaseURL() . $endPoint; $this->debug('URL', [$url]); $populatedValues = $options->getPopulated(); ksort($populatedValues); $this->debug('Parameters', [$populatedValues]); $payload = ''; if ($requestType == OptionsAbstract::REQUEST_TYPE_GET && !empty($populatedValues)) { $httpQuery = http_build_query($populatedValues); $url .= "?{$httpQuery}"; $this->debug('Get query', [$httpQuery]); $payload = $httpQuery; } else { if (empty($populatedValues)) { $this->debug('Empty payload'); } else { $json = json_encode($populatedValues); $this->debug('Json payload', [$json]); $payload = $json; } } $this->request->setRequestTypeAndPayload($requestType, $payload); $this->debug('Set Full URL', [$url]); $this->request->setUrl($url); $timestamp = gmdate('D, d M Y H:i:s T'); $tokenValues = [strtoupper($requestType), $timestamp, strtolower($endPoint)]; if ($requestType == OptionsAbstract::REQUEST_TYPE_GET) { $tokenValues[] = implode('&', array_map(function ($key, $value) { return sprintf('%s=%s', strtolower($key), strtolower($value)); }, array_keys($populatedValues), $populatedValues)); // note: this is different because it can't be escaped - unlike the http_build_query which does - and need to be lower cased } else { $tokenValues[] = ''; // for non-get we don't send any parameters, but we need this for the new line... } $this->debug('Token values', $tokenValues); $token = base64_encode(hash_hmac('sha256', implode("\n", $tokenValues), $apiSecret, true)); $this->debug('Token created', [$token]); $this->request->setAuthentication("{$apiKey}:{$token}")->setTimestamp($timestamp); $this->info("About to send to {$url} via {$requestType} with options of " . get_class($options)); $this->debug('Beginning request'); $result = $this->request->execute(); $this->debug('Request completed.', ['INFO' => $this->request->getResponseInfo(), 'BODY' => $result]); $this->handleRequestError($result); // really shouldn't happen unless something changes or I missed something if (($httpCode = $this->request->getResponseHTTPCode()) !== 200) { $message = "HTTP Error Code of {$httpCode} was generated and not caught: " . $result; $this->error($message); throw new Exception\TransferError($message); } $this->debug('All error checking passed.'); $this->info("The request was successful."); if (!$dataObject instanceof None) { $jsonArray = json_decode($result, true); if (is_null($jsonArray)) { throw new Exception\DecodingError(json_last_error_msg(), json_last_error()); } $dataObject->populate($jsonArray); } return $dataObject; }