diff --git a/.phpunit.result.cache b/.phpunit.result.cache new file mode 100644 index 0000000..36b74f0 --- /dev/null +++ b/.phpunit.result.cache @@ -0,0 +1 @@ +C:37:"PHPUnit\Runner\DefaultTestResultCache":5600:{a:2:{s:7:"defects";a:35:{s:70:"CoinbaseCommerce\Tests\ApiClientTest::testFailOnGetInstanceWithoutInit";i:4;s:56:"CoinbaseCommerce\Tests\ApiClientTest::testInitWithParams";i:4;s:55:"CoinbaseCommerce\Tests\ApiClientTest::testCorrectReinit";i:4;s:64:"CoinbaseCommerce\Tests\ApiCollectionListTest::testInitCollection";i:4;s:55:"CoinbaseCommerce\Tests\ApiResourceTest::testRefreshFrom";i:4;s:60:"CoinbaseCommerce\Tests\ApiResourceTest::testUpdateAttributes";i:4;s:59:"CoinbaseCommerce\Tests\ApiResourceTest::testDeleteAttribute";i:4;s:59:"CoinbaseCommerce\Tests\ApiResourceTest::testDirtyAttributes";i:4;s:59:"CoinbaseCommerce\Tests\ApiResourceTest::testClearAttributes";i:4;s:56:"CoinbaseCommerce\Tests\ExceptionsTest::testApiExceptions";i:4;s:61:"CoinbaseCommerce\Tests\Resources\ChargeTest::testInsertMethod";i:4;s:59:"CoinbaseCommerce\Tests\Resources\ChargeTest::testSaveMethod";i:4;s:65:"CoinbaseCommerce\Tests\Resources\ChargeTest::testSaveMethodWithId";i:4;s:61:"CoinbaseCommerce\Tests\Resources\ChargeTest::testCreateMethod";i:4;s:62:"CoinbaseCommerce\Tests\Resources\ChargeTest::testRefreshMethod";i:4;s:63:"CoinbaseCommerce\Tests\Resources\ChargeTest::testRetrieveMethod";i:4;s:59:"CoinbaseCommerce\Tests\Resources\ChargeTest::testListMethod";i:4;s:58:"CoinbaseCommerce\Tests\Resources\ChargeTest::testAllMethod";i:4;s:62:"CoinbaseCommerce\Tests\Resources\ChargeTest::testResolveMethod";i:4;s:61:"CoinbaseCommerce\Tests\Resources\ChargeTest::testCancelMethod";i:4;s:64:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testRefreshMethod";i:4;s:65:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testRetrieveMethod";i:4;s:61:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testListMethod";i:4;s:63:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testInsertMethod";i:4;s:67:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testInsertSaveMethod";i:4;s:63:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testCreateMethod";i:4;s:63:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testUpdateMethod";i:4;s:67:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testUpdateByIdMethod";i:4;s:67:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testUpdateSaveMethod";i:4;s:61:"CoinbaseCommerce\Tests\Resources\EventTest::testRefreshMethod";i:4;s:62:"CoinbaseCommerce\Tests\Resources\EventTest::testRetrieveMethod";i:4;s:58:"CoinbaseCommerce\Tests\Resources\EventTest::testListMethod";i:4;s:64:"CoinbaseCommerce\Tests\WebhookTest::testFailedOnInvalidSecretKey";i:4;s:74:"CoinbaseCommerce\Tests\WebhookTest::testThrowExceptionOnInvalidJsonPayload";i:4;s:70:"CoinbaseCommerce\Tests\WebhookTest::testThrowExceptionOnNoEventPayload";i:4;}s:5:"times";a:39:{s:70:"CoinbaseCommerce\Tests\ApiClientTest::testFailOnGetInstanceWithoutInit";d:0.008;s:56:"CoinbaseCommerce\Tests\ApiClientTest::testInitWithParams";d:0.001;s:55:"CoinbaseCommerce\Tests\ApiClientTest::testCorrectReinit";d:0.001;s:64:"CoinbaseCommerce\Tests\ApiCollectionListTest::testInitCollection";d:0.03;s:55:"CoinbaseCommerce\Tests\ApiResourceTest::testRefreshFrom";d:0;s:60:"CoinbaseCommerce\Tests\ApiResourceTest::testUpdateAttributes";d:0;s:59:"CoinbaseCommerce\Tests\ApiResourceTest::testDeleteAttribute";d:0.001;s:59:"CoinbaseCommerce\Tests\ApiResourceTest::testDirtyAttributes";d:0;s:59:"CoinbaseCommerce\Tests\ApiResourceTest::testClearAttributes";d:0;s:56:"CoinbaseCommerce\Tests\ExceptionsTest::testApiExceptions";d:0.008;s:61:"CoinbaseCommerce\Tests\Resources\ChargeTest::testInsertMethod";d:0.001;s:59:"CoinbaseCommerce\Tests\Resources\ChargeTest::testSaveMethod";d:0.001;s:65:"CoinbaseCommerce\Tests\Resources\ChargeTest::testSaveMethodWithId";d:0;s:61:"CoinbaseCommerce\Tests\Resources\ChargeTest::testCreateMethod";d:0.001;s:62:"CoinbaseCommerce\Tests\Resources\ChargeTest::testRefreshMethod";d:0.001;s:63:"CoinbaseCommerce\Tests\Resources\ChargeTest::testRetrieveMethod";d:0.001;s:59:"CoinbaseCommerce\Tests\Resources\ChargeTest::testListMethod";d:0.002;s:58:"CoinbaseCommerce\Tests\Resources\ChargeTest::testAllMethod";d:0.001;s:62:"CoinbaseCommerce\Tests\Resources\ChargeTest::testResolveMethod";d:0.001;s:61:"CoinbaseCommerce\Tests\Resources\ChargeTest::testCancelMethod";d:0.001;s:64:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testRefreshMethod";d:0.001;s:65:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testRetrieveMethod";d:0.001;s:61:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testListMethod";d:0.001;s:63:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testInsertMethod";d:0.001;s:67:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testInsertSaveMethod";d:0.001;s:63:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testCreateMethod";d:0.001;s:63:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testUpdateMethod";d:0.001;s:67:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testUpdateByIdMethod";d:0.001;s:67:"CoinbaseCommerce\Tests\Resources\CheckoutTest::testUpdateSaveMethod";d:0.001;s:61:"CoinbaseCommerce\Tests\Resources\EventTest::testRefreshMethod";d:0.001;s:62:"CoinbaseCommerce\Tests\Resources\EventTest::testRetrieveMethod";d:0.001;s:58:"CoinbaseCommerce\Tests\Resources\EventTest::testListMethod";d:0.001;s:53:"CoinbaseCommerce\Tests\UtilTest::testHashEqualSuccess";d:0;s:42:"CoinbaseCommerce\Tests\UtilTest::testEqual";d:0;s:52:"CoinbaseCommerce\Tests\UtilTest::testGetResourcePath";d:0;s:62:"CoinbaseCommerce\Tests\WebhookTest::testSuccessfullyVerifyBody";d:0.001;s:64:"CoinbaseCommerce\Tests\WebhookTest::testFailedOnInvalidSecretKey";d:0;s:74:"CoinbaseCommerce\Tests\WebhookTest::testThrowExceptionOnInvalidJsonPayload";d:0;s:70:"CoinbaseCommerce\Tests\WebhookTest::testThrowExceptionOnNoEventPayload";d:0;}}} \ No newline at end of file diff --git a/composer.json b/composer.json index 1bb9810..83a0ac6 100644 --- a/composer.json +++ b/composer.json @@ -1,45 +1,45 @@ { - "name": "coinbase/coinbase-commerce", - "description": "Coinbase Commerce API library", - "keywords": [ - "bitcoin", - "coinbase-commerce", - "coinbase", - "ethereum", - "litecoin" - ], - "homepage": "https://commerce.coinbase.com/", - "type": "library", - "license": "Apache-2.0", - "autoload": { - "psr-4": { - "CoinbaseCommerce\\": "src/" + "name": "coinbase/coinbase-commerce", + "description": "Coinbase Commerce API library", + "keywords": [ + "bitcoin", + "coinbase-commerce", + "coinbase", + "ethereum", + "litecoin" + ], + "homepage": "https://commerce.coinbase.com/", + "type": "library", + "license": "Apache-2.0", + "autoload": { + "psr-4": { + "CoinbaseCommerce\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "CoinbaseCommerce\\Tests\\": "tests/" + } + }, + "require": { + "php": ">=8.1", + "guzzlehttp/guzzle": "^7.0.1", + "psr/http-message": "^1.0" + }, + "require-dev": { + "squizlabs/php_codesniffer": "3.*", + "phpunit/phpunit": "9.5" + }, + "scripts": { + "lint": "./vendor/bin/phpcs", + "test": "./vendor/bin/phpunit --verbose", + "coverage": "./vendor/bin/phpunit --coverage-text" + }, + "archive": { + "exclude": [ + "/tests", + "/examples", + "/.circleci" + ] } - }, - "autoload-dev": { - "psr-4": { - "CoinbaseCommerce\\Tests\\": "tests/" - } - }, - "require": { - "php": ">=5.4.0", - "guzzlehttp/guzzle": "~5.0|~6.0", - "psr/http-message": "^1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.7", - "squizlabs/php_codesniffer": "3.*" - }, - "scripts": { - "lint": "./vendor/bin/phpcs", - "test": "./vendor/bin/phpunit --verbose", - "coverage": "./vendor/bin/phpunit --coverage-text" - }, - "archive": { - "exclude": [ - "/tests", - "/examples", - "/.circleci" - ] - } } diff --git a/phpunit.xml b/phpunit.xml index 6c84ef8..dca0e0f 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -8,7 +8,6 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" > @@ -16,12 +15,4 @@ ./tests/BaseTest.php - - - ./src - - ./vendor - - - - \ No newline at end of file + diff --git a/src/ApiClient.php b/src/ApiClient.php index 0396c05..6139ee5 100644 --- a/src/ApiClient.php +++ b/src/ApiClient.php @@ -21,10 +21,7 @@ class ApiClient self::TIMEOUT_PARAM => 3 ]; - /** - * @var ApiClient - */ - private static $instance; + private static ApiClient $instance; /** * @var @@ -41,10 +38,7 @@ class ApiClient */ private $httpClient; - /** - * @var boolean - */ - private $verifySSL = true ; + private bool $verifySSL = true ; /** * ApiClient constructor. @@ -58,15 +52,11 @@ private function __clone() } /** - * @param string $apiKey - * @param null|string $baseUrl - * @param null|string $apiVersion - * @param null|integer $timeout - * @return ApiClient + * @throws \Exception */ - public static function init($apiKey, $baseUrl = null, $apiVersion = null, $timeout = null) + public static function init(string $apiKey, ?string $baseUrl = null, ?string $apiVersion = null, ?int $timeout = null): ApiClient { - if (!self::$instance) { + if (!isset(self::$instance) || !self::$instance) { self::$instance = new self(); } @@ -79,46 +69,34 @@ public static function init($apiKey, $baseUrl = null, $apiVersion = null, $timeo } /** - * @return ApiClient * @throws \Exception */ - public static function getInstance() + public static function getInstance(): ApiClient { - if (!self::$instance) { + if (!isset(self::$instance) || !self::$instance) { throw new \Exception('Please init client first.'); } return self::$instance; } - /** - * @param string $key - * @return mixed - */ - private function getParam($key) + private function getParam(string $key) { if (array_key_exists($key, $this->params)) { return $this->params[$key]; } } - /** - * @param string $key - * @param mixed $value - * @return $this - */ - private function setParam($key, $value) + private function setParam(string $key, mixed $value): self { $this->params[$key] = $value; return $this; } /** - * @param string $value - * @return $this * @throws \Exception */ - public function setApiKey($value) + public function setApiKey(string $value): self { if (empty($value)) { throw new \Exception('Api Key is required'); @@ -128,21 +106,14 @@ public function setApiKey($value) return $this; } - /** - * @return mixed - */ - public function getApiKey() + public function getApiKey(): mixed { return $this->getParam(self::API_KEY_PARAM); } - /** - * @param string $value - * @return $this - */ - public function setBaseUrl($value) + public function setBaseUrl(?string $value): self { - if (!empty($value) && \is_string($value)) { + if (!empty($value)) { if (substr($value, -1) !== '/') { $value .= '/'; } @@ -153,65 +124,42 @@ public function setBaseUrl($value) return $this; } - /** - * @return mixed - */ - public function getBaseUrl() + public function getBaseUrl(): mixed { return $this->getParam(self::BASE_API_URL_PARAM); } - /** - * @param string $value - * @return $this - */ - public function setApiVersion($value) + public function setApiVersion(?string $value): self { - if (!empty($value) && \is_string($value)) { + if (!empty($value)) { $this->setParam(self::API_VERSION_PARAM, $value); } return $this; } - /** - * @return mixed - */ - public function getApiVersion() + public function getApiVersion(): mixed { return $this->getParam(self::API_VERSION_PARAM); } - /** - * @param integer $value - * @return $this - */ - public function setTimeout($value) + public function setTimeout(?int $value): self { - if (!empty($value) && \is_numeric($value)) { + if (!empty($value)) { $this->setParam(self::TIMEOUT_PARAM, $value); } return $this; } - /** - * @return mixed - */ public function getTimeout() { return $this->getParam(self::TIMEOUT_PARAM); } - /** - * @param array $query - * @param array $body - * @param array $headers - * @return array - */ - private function generateRequestOptions($query = [], $body = [], $headers = []) + private function generateRequestOptions(array $query = [], array $body = [], array $headers = []): array { - return $options = [ + return [ 'headers' => array_merge( [ 'Content-Type' => 'application/json', @@ -228,13 +176,7 @@ private function generateRequestOptions($query = [], $body = [], $headers = []) ]; } - /** - * @param string $method - * @param string $path - * @param array $options - * @return ApiResponse - */ - private function makeRequest($method, $path, $options) + private function makeRequest(string $method, string $path, array $options): ApiResponse { try { $path = Util::joinPath($this->getParam('baseApiUrl'), $path); @@ -256,7 +198,7 @@ private function makeRequest($method, $path, $options) } } - public function getHttpClient() + public function getHttpClient(): Client { if (!isset($this->httpClient)) { $this->httpClient = new Client(['verify' => $this->verifySSL]); @@ -265,80 +207,46 @@ public function getHttpClient() return $this->httpClient; } - /** - * @param $logger - */ - public function setLogger($logger) + public function setLogger($logger): void { $this->logger = $logger; } - /** - * @param $path - * @param array $queryParams - * @param array $headers - * @return ApiResponse - */ - public function get($path, $queryParams = [], $headers = []) + public function get(string $path, array $queryParams = [], array $headers = []): ApiResponse { $options = $this->generateRequestOptions($queryParams, $headers); return $this->makeRequest('GET', $path, $options); } - /** - * @param string $path - * @param array $body - * @param array $headers - * @return ApiResponse - */ - public function post($path, $body, $headers) + public function post(string $path, array $body, array $headers): ApiResponse { $options = $this->generateRequestOptions([], $body, $headers = []); return $this->makeRequest('POST', $path, $options); } - /** - * @param string $path - * @param array $headers - * @return ApiResponse - */ - public function put($path, $body, $headers) + public function put(string $path, array $body, array $headers): ApiResponse { $options = $this->generateRequestOptions([], $body, $headers = []); return $this->makeRequest('PUT', $path, $options); } - /** - * @param string $path - * @param array $headers - * @return ApiResponse - */ - public function delete($path, $headers = []) + public function delete(string $path, array $headers = []): ApiResponse { $options = $this->generateRequestOptions([], [], $headers); return $this->makeRequest('DELETE', $path, $options); } - /** - * @param ApiResponse $response - */ - public function setLastResponse($response) + public function setLastResponse(ApiResponse $response): void { $this->response = $response; } - /** - * @return ApiResponse - */ - public function getLastResponse() + public function getLastResponse(): ApiResponse { return $this->response; } - /** - * @param ApiResponse $response - */ - public function logWarnings($response) + public function logWarnings(ApiResponse $response): void { if (!$this->logger) { return; @@ -355,12 +263,12 @@ public function logWarnings($response) } } - public static function getClassName() + public static function getClassName(): string { return get_called_class(); } - public function verifySsl($verify) + public function verifySsl($verify): void { if(!is_bool($verify)) { return; diff --git a/src/ApiErrorFactory.php b/src/ApiErrorFactory.php index 19f36e0..85fa056 100644 --- a/src/ApiErrorFactory.php +++ b/src/ApiErrorFactory.php @@ -14,21 +14,10 @@ class ApiErrorFactory { - /** - * @var array - */ - private static $mapErrorMessageToClass = []; + private static array $mapErrorMessageToClass = []; + private static array $mapErrorCodeToClass = []; - /** - * @var array - */ - private static $mapErrorCodeToClass = []; - - /** - * @param $message - * @return mixed|null - */ - public static function getErrorClassByMessage($message) + public static function getErrorClassByMessage(mixed $message): mixed { if (empty(self::$mapErrorMessageToClass)) { self::$mapErrorMessageToClass = [ @@ -42,14 +31,10 @@ public static function getErrorClassByMessage($message) ]; } - return isset(self::$mapErrorMessageToClass[$message]) ? self::$mapErrorMessageToClass[$message]: null; + return self::$mapErrorMessageToClass[$message] ?? null; } - /** - * @param $code - * @return mixed|null - */ - public static function getErrorClassByCode($code) + public static function getErrorClassByCode(int $code): mixed { if (empty(self::$mapErrorCodeToClass)) { self::$mapErrorCodeToClass = [ @@ -62,20 +47,17 @@ public static function getErrorClassByCode($code) ]; } - return isset(self::$mapErrorCodeToClass[$code]) ? self::$mapErrorCodeToClass[$code]: null; + return self::$mapErrorCodeToClass[$code] ?? null; } - /** - * @param RequestException $exception - */ - public static function create($exception) + public static function create(RequestException $exception): mixed { $response = $exception->getResponse(); $request = $exception->getRequest(); $code = $exception->getCode(); $data = $response ? json_decode($response->getBody(), true) : null; - $errorMessage = isset($data['error']['message']) ? $data['error']['message'] : $exception->getMessage(); - $errorId = isset($data['error']['type']) ? $data['error']['type'] : null; + $errorMessage = $data['error']['message'] ?? $exception->getMessage(); + $errorId = $data['error']['type'] ?? null; $errorClass = self::getErrorClassByMessage($errorId) ?: self::getErrorClassByCode($code) ?: ApiException::getClassName(); diff --git a/src/ApiResourceList.php b/src/ApiResourceList.php index 3722fa7..df13333 100644 --- a/src/ApiResourceList.php +++ b/src/ApiResourceList.php @@ -7,40 +7,18 @@ class ApiResourceList extends \ArrayObject const PREV_CURSOR = 'ending_before'; const NEXT_CURSOR = 'starting_after'; - private static $apiClient; + private static ApiClient $apiClient; - /** - * @var array - */ - protected $items = []; - - /** - * @var array - */ - protected $pagination = []; - - /** - * @var string - */ - protected $resourceClass; - - /** - * @var array - */ - protected $headers = []; - - /** - * @var array - */ - protected $params = []; + protected array $items = []; + protected array $pagination = []; + protected mixed $resourceClass; + protected array $headers = []; + protected array $params = []; - /** - * ApiResourceList constructor. - * @param array $items - * @param array $pagination - */ - public function __construct($resourceClass, $items, $pagination, $params, $headers) + public function __construct(mixed $resourceClass, array $items, array $pagination, array $params, array $headers) { + parent::__construct(); + $this->resourceClass = $resourceClass; $this->items = $items; $this->pagination = $pagination; @@ -48,50 +26,32 @@ public function __construct($resourceClass, $items, $pagination, $params, $heade $this->headers = $headers; } - /** - * @param $items - */ - public function setItems($items) + public function setItems(array $items): void { $this->items = $items; } - /** - * @param $pagination - */ - public function setPagination($pagination) + public function setPagination(array $pagination): void { $this->pagination = $pagination; } - /** - * @return array - */ - public function getPagination() + public function getPagination(): array { return $this->pagination; } - /** - * @return array - */ - public function getParams() + public function getParams(): array { return $this->params; } - /** - * @return bool - */ - public function hasNext() + public function hasNext(): bool { return isset($this->pagination[self::CURSOR_PARAM][1]) && null !== $this->pagination[self::CURSOR_PARAM][1]; } - /** - * @return bool - */ - public function loadNext() + public function loadNext(): bool { if (!$this->hasNext()) { return false; @@ -105,18 +65,12 @@ public function loadNext() return true; } - /** - * @return bool - */ - public function hasPrev() + public function hasPrev(): bool { return isset($this->pagination[self::CURSOR_PARAM][0]) && null !== $this->pagination[self::CURSOR_PARAM][0]; } - /** - * @return bool - */ - public function loadPrev() + public function loadPrev(): bool { if (!$this->hasPrev()) { return false; @@ -130,7 +84,7 @@ public function loadPrev() return true; } - protected function loadPage($params) + protected function loadPage($params): void { $client = self::getClient(); $resourceClass = $this->resourceClass; @@ -139,7 +93,7 @@ protected function loadPage($params) $response = $client->get($path, $params, $this->headers); $responseData = $response->bodyArray; - $this->pagination = isset($responseData['pagination']) ? $responseData['pagination'] : []; + $this->pagination = $responseData['pagination'] ?? []; $this->items = []; if (isset($responseData['data'])) { @@ -152,49 +106,52 @@ function ($item) { } } - public function offsetGet($key) + public function offsetGet(mixed $key): mixed { return $this->items[$key]; } - public function offsetSet($key, $value) + public function offsetSet(mixed $key, mixed $value): void { null === $key ? array_push($this->items, $value) : $this->items[$key] = $value; } - public function count() + public function count(): int { return count($this->items); } - public function countAll() + public function countAll(): mixed { if (isset($this->pagination['total'])) { return $this->pagination['total']; } } - public function asort() + public function asort(int $flags = SORT_REGULAR): bool { - asort($this->items); + return asort($this->items, $flags); } - public function ksort() + public function ksort(int $flags = SORT_REGULAR): bool { - ksort($this->items); + return ksort($this->items, $flags); } - public function offsetUnset($key) + public function offsetUnset($key): void { unset($this->items[$key]); } - public function getIterator() + public function getIterator(): \ArrayIterator { return new \ArrayIterator($this->items); } - public static function getClient() + /** + * @throws \Exception + */ + public static function getClient(): ApiClient { if (self::$apiClient) { return self::$apiClient; @@ -202,12 +159,12 @@ public static function getClient() return ApiClient::getInstance(); } - public function setClient($client) + public static function setClient($client): void { self::$apiClient = $client; } - public static function getClassName() + public static function getClassName(): string { return get_called_class(); } diff --git a/src/ApiResponse.php b/src/ApiResponse.php index 792b939..264a83b 100644 --- a/src/ApiResponse.php +++ b/src/ApiResponse.php @@ -1,6 +1,8 @@ code = $response->getStatusCode(); - $this->headers = $response->getHeaders(); - $this->body = (string)$response->getBody(); - $lowerCaseKeys = array_change_key_case($this->headers); - $this->requestId = array_key_exists(strtolower(self::REQUEST_ID_HEADER), $lowerCaseKeys) - && !empty($lowerCaseKeys[strtolower(self::REQUEST_ID_HEADER)][0]) ? - $lowerCaseKeys[strtolower(self::REQUEST_ID_HEADER)][0] : null; + $this->code = $response->getStatusCode(); + $this->headers = $response->getHeaders(); + $this->body = (string)$response->getBody(); + $lowerCaseKeys = array_change_key_case($this->headers); + $this->requestId = array_key_exists(strtolower(self::REQUEST_ID_HEADER), $lowerCaseKeys) + && !empty($lowerCaseKeys[strtolower(self::REQUEST_ID_HEADER)][0]) ? + $lowerCaseKeys[strtolower(self::REQUEST_ID_HEADER)][0] : null; - $this->bodyArray = !empty($this->body)? \json_decode($this->body, true): null; - } + $this->bodyArray = !empty($this->body)? \json_decode($this->body, true): null; } } diff --git a/src/Exceptions/InvalidResponseException.php b/src/Exceptions/InvalidResponseException.php index 5e9db44..0bc95c9 100644 --- a/src/Exceptions/InvalidResponseException.php +++ b/src/Exceptions/InvalidResponseException.php @@ -3,6 +3,11 @@ class InvalidResponseException extends CoinbaseException { + /** + * @var mixed|string + */ + private mixed $body; + public function __construct($message = '', $body = '') { parent::__construct($message); diff --git a/src/Resources/ApiResource.php b/src/Resources/ApiResource.php index 4ddf20c..d271987 100644 --- a/src/Resources/ApiResource.php +++ b/src/Resources/ApiResource.php @@ -136,37 +136,37 @@ protected static function getClient() return ApiClient::getInstance(); } - public function offsetGet($key) + public function offsetGet($key): mixed { return $this->__get($key); } - public function offsetSet($key, $value) + public function offsetSet($key, $value): void { null === $key ? array_push($this->attributes, $value) : $this->attributes[$key] = $value; } - public function count() + public function count(): int { return count($this->attributes); } - public function asort() + public function asort(int $flags = SORT_REGULAR): bool { asort($this->attributes); } - public function ksort() + public function ksort(int $flags = SORT_REGULAR): bool { ksort($this->attributes); } - public function offsetUnset($key) + public function offsetUnset($key): void { unset($this->attributes[$key]); } - public function getIterator() + public function getIterator(): \Iterator { return new \ArrayIterator($this->attributes); } diff --git a/src/Util.php b/src/Util.php index 32070a1..7ecd498 100644 --- a/src/Util.php +++ b/src/Util.php @@ -7,15 +7,12 @@ class Util { - private static $mapResourceByName = []; + private static array $mapResourceByName = []; - /** - * @param mixed $response - */ - public static function convertToApiObject($response) + public static function convertToApiObject(mixed $response): mixed { if ($response instanceof ApiResponse) { - $response = isset($response->bodyArray['data']) ? $response->bodyArray['data'] : null; + $response = $response->bodyArray['data'] ?? null; } if (is_array($response)) { @@ -34,7 +31,7 @@ function (&$item) { return $response; } - public static function getResourceClassByName($name) + public static function getResourceClassByName($name): mixed { if (empty(self::$mapResourceByName)) { self::$mapResourceByName = [ @@ -44,13 +41,10 @@ public static function getResourceClassByName($name) ]; } - return isset(self::$mapResourceByName[$name]) ? self::$mapResourceByName[$name] : null; + return self::$mapResourceByName[$name] ?? null; } - /** - * @return string - */ - public static function joinPath() + public static function joinPath(): string { $arguments = func_get_args(); array_walk( @@ -63,12 +57,7 @@ function (&$item) { return implode('/', $arguments); } - /** - * @param mixed $prop1 - * @param mixed $prop2 - * @return bool - */ - public static function equal($prop1, $prop2) + public static function equal(mixed $prop1, mixed $prop2): bool { if (is_array($prop1)) { foreach ($prop1 as $key => $value) { @@ -89,12 +78,7 @@ public static function equal($prop1, $prop2) return $prop1 === $prop2; } - /** - * @param string $str1 - * @param string $str2 - * @return bool - */ - public static function hashEqual($str1, $str2) + public static function hashEqual(string $str1, string $str2): bool { if (function_exists('hash_equals')) { return \hash_equals($str1, $str2); diff --git a/src/Webhook.php b/src/Webhook.php index f1d3698..58eac46 100644 --- a/src/Webhook.php +++ b/src/Webhook.php @@ -7,7 +7,11 @@ class Webhook { - public static function buildEvent($payload, $sigHeader, $secret) + /** + * @throws SignatureVerificationException + * @throws InvalidResponseException + */ + public static function buildEvent(string $payload, string $sigHeader, string $secret): Event { $data = null; @@ -26,7 +30,10 @@ public static function buildEvent($payload, $sigHeader, $secret) return new Event($data['event']); } - public static function verifySignature($payload, $sigHeader, $secret) + /** + * @throws SignatureVerificationException + */ + public static function verifySignature(string $payload, string $sigHeader, string $secret): void { $computedSignature = \hash_hmac('sha256', $payload, $secret); diff --git a/tests/ApiClientTest.php b/tests/ApiClientTest.php index ba1f694..bb6d45b 100644 --- a/tests/ApiClientTest.php +++ b/tests/ApiClientTest.php @@ -6,17 +6,15 @@ class ApiClientTest extends TestCase { - public function setUp() + public function setUp(): void { parent::setUp(); } - /** - * @expectedException \Exception - * @expectedExceptionMessage Please init client first. - */ public function testFailOnGetInstanceWithoutInit() { + $this->expectException(\Exception::class); + $this->expectExceptionMessage("Please init client first."); ApiClient::getInstance(); } diff --git a/tests/ApiResourceTest.php b/tests/ApiResourceTest.php index 787c965..c47cc90 100644 --- a/tests/ApiResourceTest.php +++ b/tests/ApiResourceTest.php @@ -2,11 +2,14 @@ namespace CoinbaseCommerce\Tests; use CoinbaseCommerce\Resources\ApiResource; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class ApiResourceTest extends TestCase { - public function setUp() + private MockObject $apiResourceStub; + + public function setUp(): void { parent::setUp(); $this->apiResourceStub = $this->getMockForAbstractClass(ApiResource::getClassName()); @@ -62,12 +65,12 @@ public function testUpdateAttributes() ] ); - $this->assertEquals('Test Name', $this->apiResourceStub->name); - $this->assertEquals('value1', $this->apiResourceStub->meta['option1']); - $this->apiResourceStub->name = 'New Name'; - $this->apiResourceStub->meta['option1'] = 'value2'; - $this->assertEquals('New Name', $this->apiResourceStub->name); - $this->assertEquals('value2', $this->apiResourceStub->meta['option1']); + $this->assertEquals('Test Name', $this->apiResourceStub["name"]); + $this->assertEquals('value1', $this->apiResourceStub["meta"]['option1']); + $this->apiResourceStub["name"] = 'New Name'; + $this->apiResourceStub["meta"]['option1'] = 'value2'; + $this->assertEquals('New Name', $this->apiResourceStub["name"]); + $this->assertEquals('value2', $this->apiResourceStub["meta"]['option1']); } public function testDeleteAttribute() diff --git a/tests/BaseTest.php b/tests/BaseTest.php index d068a9b..62be0ad 100644 --- a/tests/BaseTest.php +++ b/tests/BaseTest.php @@ -16,7 +16,7 @@ class BaseTest extends TestCase protected $logger; - public function setUp() + public function setUp(): void { $this->initMockClient(); diff --git a/tests/Resources/ChargeTest.php b/tests/Resources/ChargeTest.php index 87101bd..6a4124d 100644 --- a/tests/Resources/ChargeTest.php +++ b/tests/Resources/ChargeTest.php @@ -7,7 +7,7 @@ class ChargeTest extends BaseTest { - public function setUp() + public function setUp(): void { parent::setUp(); Charge::setClient($this->apiClient); @@ -43,12 +43,10 @@ public function testSaveMethod() $this->assertEquals('7C7V5ECK', $chargeObj->code); } - /** - * @expectedException \Exception - * @expectedExceptionMessage Update is not allowed - */ public function testSaveMethodWithId() { + $this->expectException(\Exception::class); + $this->expectExceptionMessage("Update is not allowed"); $this->appendRequest(200, $this->parseJsonFile('charge.json')); $chargeObj = new Charge( [ diff --git a/tests/Resources/CheckoutTest.php b/tests/Resources/CheckoutTest.php index 3ca863e..29bc53f 100644 --- a/tests/Resources/CheckoutTest.php +++ b/tests/Resources/CheckoutTest.php @@ -10,7 +10,7 @@ class CheckoutTest extends BaseTest { - public function setUp() + public function setUp(): void { parent::setUp(); Checkout::setClient($this->apiClient); diff --git a/tests/Resources/EventTest.php b/tests/Resources/EventTest.php index f3a4cfa..99dda8c 100644 --- a/tests/Resources/EventTest.php +++ b/tests/Resources/EventTest.php @@ -8,7 +8,7 @@ class EventTest extends BaseTest { - public function setUp() + public function setUp(): void { parent::setUp(); Event::setClient($this->apiClient); diff --git a/tests/WebhookTest.php b/tests/WebhookTest.php index 69f3593..1d67c9b 100644 --- a/tests/WebhookTest.php +++ b/tests/WebhookTest.php @@ -1,13 +1,15 @@ assertEquals('charge:created', $event->type); } - /** - * @expectedException \CoinbaseCommerce\Exceptions\SignatureVerificationException - */ public function testFailedOnInvalidSecretKey() { + $this->expectException(SignatureVerificationException::class); $invalidSecret = '30291a20-0bd1-4267-9b0f-e6e7b123c0bg'; $payload = '{"id":1,"scheduled_for":"2017-01-31T20:50:02Z","attempt_number":1,"event":{"id":"24934862-d980-46cb-9402-43c81b0cdba6","type":"charge:created","api_version":"2018-03-22","created_at":"2017-01-31T20:49:02Z","data":{"code":"66BEOV2A","name":"The Sovereign Individual","description":"Mastering the Transition to the Information Age","hosted_url":"https://commerce.coinbase.com/charges/66BEOV2A","created_at":"2017-01-31T20:49:02Z","expires_at":"2017-01-31T21:04:02Z","timeline":[{"time":"2017-01-31T20:49:02Z","status":"NEW"}],"metadata":{},"pricing_type":"no_price","payments":[],"addresses":{"bitcoin":"0000000000000000000000000000000000","ethereum":"0x0000000000000000000000000000000000000000","litecoin":"3000000000000000000000000000000000","bitcoincash":"bitcoincash:000000000000000000000000000000000000000000"}}}}'; $headerSignature = '8be7742c7d372f08a6a3224edadf18a22b65fa9e28f3f2de97376cdaa092590d'; @@ -36,12 +36,10 @@ public function testFailedOnInvalidSecretKey() Webhook::buildEvent($payload, $headerSignature, $invalidSecret); } - /** - * @expectedException \CoinbaseCommerce\Exceptions\InvalidResponseException - * @expectedExceptionMessage Invalid payload provided. No JSON object could be decoded - */ public function testThrowExceptionOnInvalidJsonPayload() { + $this->expectException(InvalidResponseException::class); + $this->expectExceptionMessage("Invalid payload provided. No JSON object could be decoded"); $secret = '30291a20-0bd1-4267-9b0f-e6e7b123c0bf'; $invalidPayload = 'Not json'; $headerSignature = '8be7742c7d372f08a6a3224edadf18a22b65fa9e28f3f2de97376cdaa092590d'; @@ -49,12 +47,10 @@ public function testThrowExceptionOnInvalidJsonPayload() Webhook::buildEvent($invalidPayload, $headerSignature, $secret); } - /** - * @expectedException \CoinbaseCommerce\Exceptions\InvalidResponseException - * @expectedExceptionMessage Invalid payload provided. - */ public function testThrowExceptionOnNoEventPayload() { + $this->expectException(InvalidResponseException::class); + $this->expectExceptionMessage("Invalid payload provided."); $secret = '30291a20-0bd1-4267-9b0f-e6e7b123c0bf'; $invalidPayload = '{"id":1,"scheduled_for":"2017-01-31T20:50:02Z","attempt_number":1}'; $headerSignature = '8be7742c7d372f08a6a3224edadf18a22b65fa9e28f3f2de97376cdaa092590d';