From b9e5487d50351ecab2452ba30775cce8917ef2b8 Mon Sep 17 00:00:00 2001 From: Luke Stokes Date: Mon, 20 Mar 2017 16:34:37 -0500 Subject: [PATCH 01/11] Changes needed for Guzzle 6. --- composer.json | 5 ++--- src/Foxy/FoxyClient/FoxyClient.php | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 154b492..dd044da 100644 --- a/composer.json +++ b/composer.json @@ -14,9 +14,8 @@ } ], "require": { - "guzzlehttp/guzzle": "~5.2", - "guzzlehttp/cache-subscriber": "0.1.*@dev", - "php": ">=5.3.3" + "guzzlehttp/guzzle": "~6.2", + "php": ">=5.5" }, "autoload": { "psr-0": {"Foxy\\FoxyClient": "src/"} diff --git a/src/Foxy/FoxyClient/FoxyClient.php b/src/Foxy/FoxyClient/FoxyClient.php index 678d3c3..69c2ad3 100644 --- a/src/Foxy/FoxyClient/FoxyClient.php +++ b/src/Foxy/FoxyClient/FoxyClient.php @@ -236,7 +236,7 @@ private function go($method, $uri, $post, $is_retry = false) if ($method === "GET" && $post !== null) { $guzzle_args['query'] = $post; } elseif ($post !== null) { - $guzzle_args['body'] = $post; + $guzzle_args['form_params'] = $post; } if (!$this->handle_exceptions) { @@ -262,9 +262,8 @@ private function processRequest($method, $uri, $post, $guzzle_args, $is_retry = $guzzle_args['headers']['X-HTTP-Method-Override'] = 'PATCH'; } - $api_request = $this->guzzle->createRequest($method, $uri, $guzzle_args); - $this->last_response = $this->guzzle->send($api_request); - $data = $this->last_response->json(); + $this->last_response = $this->guzzle->request($method, $uri, $guzzle_args); + $data = json_decode($this->last_response->getBody()->getContents(),true); $this->saveLinks($data); if ($this->hasExpiredAccessTokenError($data) && !$this->shouldRefreshToken()) { if (!$is_retry) { From 47ad8059a0b67633d4567b332e3a7505385e7dad Mon Sep 17 00:00:00 2001 From: Luke Stokes Date: Mon, 20 Mar 2017 16:40:04 -0500 Subject: [PATCH 02/11] Adding cache middleware. --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index dd044da..8dd67c3 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ ], "require": { "guzzlehttp/guzzle": "~6.2", - "php": ">=5.5" + "php": ">=5.5", + "kevinrob/guzzle-cache-middleware": "^2.0" }, "autoload": { "psr-0": {"Foxy\\FoxyClient": "src/"} From 7d4d2e4513cfe9bf9124f8bba8c5fc4f7f8e8846 Mon Sep 17 00:00:00 2001 From: Luke Stokes Date: Fri, 7 Apr 2017 16:20:21 -0500 Subject: [PATCH 03/11] Adding support for PUT. --- src/Foxy/FoxyClient/FoxyClient.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Foxy/FoxyClient/FoxyClient.php b/src/Foxy/FoxyClient/FoxyClient.php index 69c2ad3..faac4ab 100644 --- a/src/Foxy/FoxyClient/FoxyClient.php +++ b/src/Foxy/FoxyClient/FoxyClient.php @@ -190,6 +190,11 @@ public function get($uri = "", $post = null) return $this->go('GET', $uri, $post); } + public function put($uri, $post = null) + { + return $this->go('PUT', $uri, $post); + } + public function post($uri, $post = null) { return $this->go('POST', $uri, $post); @@ -257,7 +262,7 @@ private function go($method, $uri, $post, $is_retry = false) private function processRequest($method, $uri, $post, $guzzle_args, $is_retry = false) { // special case for PATCHing a Downloadable File - if ($post !== null && array_key_exists('file', $post) && $method == 'PATCH') { + if ($post !== null && is_array($post) && array_key_exists('file', $post) && $method == 'PATCH') { $method = 'POST'; $guzzle_args['headers']['X-HTTP-Method-Override'] = 'PATCH'; } From 3cc21b6a31e2fe814f12d122eea2bf4f207aebf1 Mon Sep 17 00:00:00 2001 From: Jan Peca Date: Mon, 6 Jun 2022 17:44:45 +0200 Subject: [PATCH 04/11] Upgrade guzzle --- composer.json | 7 +- composer.lock | 694 ++++++++++++++++++++++++++ src/Foxy/FoxyClient/FoxyClient.php | 53 +- src/Foxy/FoxyClient/JsonException.php | 5 + 4 files changed, 731 insertions(+), 28 deletions(-) create mode 100644 composer.lock create mode 100644 src/Foxy/FoxyClient/JsonException.php diff --git a/composer.json b/composer.json index 8dd67c3..0054be2 100644 --- a/composer.json +++ b/composer.json @@ -14,9 +14,10 @@ } ], "require": { - "guzzlehttp/guzzle": "~6.2", - "php": ">=5.5", - "kevinrob/guzzle-cache-middleware": "^2.0" + "guzzlehttp/guzzle": "~7.4", + "php": ">=7.4", + "kevinrob/guzzle-cache-middleware": "^2.0", + "ext-json": "*" }, "autoload": { "psr-0": {"Foxy\\FoxyClient": "src/"} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..64a0cf9 --- /dev/null +++ b/composer.lock @@ -0,0 +1,694 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "62265a783d7cb41128a3abb153161e80", + "packages": [ + { + "name": "guzzlehttp/guzzle", + "version": "7.4.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "74a8602c6faec9ef74b7a9391ac82c5e65b1cdab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/74a8602c6faec9ef74b7a9391ac82c5e65b1cdab", + "reference": "74a8602c6faec9ef74b7a9391ac82c5e65b1cdab", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.8.3 || ^2.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-curl": "*", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.5 || ^9.3.5", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.4-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.4.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2022-05-25T13:24:33+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4 || ^5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:56:57+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c94a94f120803a18554c1805ef2e539f8285f9a2", + "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.2.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2022-03-20T21:55:58+00:00" + }, + { + "name": "kevinrob/guzzle-cache-middleware", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/Kevinrob/guzzle-cache-middleware.git", + "reference": "6952064f7747756b0be7b4c234c0fd7535ea4c8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Kevinrob/guzzle-cache-middleware/zipball/6952064f7747756b0be7b4c234c0fd7535ea4c8c", + "reference": "6952064f7747756b0be7b4c234c0fd7535ea4c8c", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "cache/array-adapter": "^0.4", + "doctrine/cache": "^1.0", + "guzzlehttp/guzzle": "^6.0", + "illuminate/cache": "^5.0", + "league/flysystem": "^1.0", + "phpunit/phpunit": "^4.0 || ^5.0", + "psr/cache": "^1.0" + }, + "suggest": { + "doctrine/cache": "This library have a lot of ready-to-use cache storage (to be use with Kevinrob\\GuzzleCache\\Storage\\DoctrineCacheStorage)", + "guzzlehttp/guzzle": "For using this library. It was created for Guzzle6. (but you can use it with any PSR-7 HTTP Client)", + "laravel/framework": "To be use with Kevinrob\\GuzzleCache\\Storage\\LaravelCacheStorage", + "league/flysystem": "To be use with Kevinrob\\GuzzleCache\\Storage\\FlysystemStorage", + "psr/cache": "To be use with Kevinrob\\GuzzleCache\\Storage\\Psr6CacheStorage" + }, + "type": "library", + "autoload": { + "psr-4": { + "Kevinrob\\GuzzleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kevin Robatel", + "email": "kevinrob2@gmail.com", + "homepage": "https://github.com/Kevinrob" + } + ], + "description": "A HTTP/1.1 Cache for Guzzle 6. It's a simple Middleware to be added in the HandlerStack. (RFC 7234)", + "homepage": "https://github.com/Kevinrob/guzzle-cache-middleware", + "keywords": [ + "Etag", + "Flysystem", + "Guzzle", + "cache", + "cache-control", + "doctrine", + "expiration", + "guzzle6", + "handler", + "http", + "http 1.1", + "middleware", + "performance", + "php", + "promise", + "psr6", + "psr7", + "rfc7234", + "validation" + ], + "support": { + "issues": "https://github.com/Kevinrob/guzzle-cache-middleware/issues", + "source": "https://github.com/Kevinrob/guzzle-cache-middleware/tree/master" + }, + "time": "2017-08-17T12:23:43+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "time": "2019-04-30T12:38:16+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-02-25T11:15:52+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.4" + }, + "platform-dev": [], + "plugin-api-version": "2.2.0" +} diff --git a/src/Foxy/FoxyClient/FoxyClient.php b/src/Foxy/FoxyClient/FoxyClient.php index faac4ab..364dd66 100644 --- a/src/Foxy/FoxyClient/FoxyClient.php +++ b/src/Foxy/FoxyClient/FoxyClient.php @@ -67,8 +67,8 @@ class FoxyClient private $guzzle; private $last_response = ''; - private $registered_link_relations = array('self', 'first', 'prev', 'next', 'last'); - private $links = array(); + private $registered_link_relations = ['self', 'first', 'prev', 'next', 'last']; + private $links = []; private $use_sandbox = false; private $obtaining_updated_access_token = false; private $include_auth_header = true; @@ -77,7 +77,7 @@ class FoxyClient private $api_home = ''; private $authorization_endpoint = ''; - public function __construct(\GuzzleHttp\Client $guzzle, array $config = array()) + public function __construct(\GuzzleHttp\Client $guzzle, array $config = []) { $this->guzzle = $guzzle; $this->api_home = static::PRODUCTION_API_HOME; @@ -87,7 +87,7 @@ public function __construct(\GuzzleHttp\Client $guzzle, array $config = array()) public function updateFromConfig($config) { - $valid_config_options = array( + $valid_config_options = [ 'api_home', 'authorization_endpoint', 'use_sandbox', @@ -97,7 +97,7 @@ public function updateFromConfig($config) 'client_id', 'client_secret', 'handle_exceptions' - ); + ]; foreach($valid_config_options as $valid_config_option) { if (array_key_exists($valid_config_option, $config)) { $this->{$valid_config_option} = $config[$valid_config_option]; @@ -111,13 +111,13 @@ public function updateFromConfig($config) public function clearCredentials() { - $config = array( + $config = [ 'access_token' => '', 'access_token_expires' => '', 'refresh_token' => '', 'client_id' => '', 'client_secret' => '' - ); + ]; $this->updateFromConfig($config); } @@ -232,10 +232,10 @@ private function go($method, $uri, $post, $is_retry = false) } //Setup Guzzle Details - $guzzle_args = array( + $guzzle_args = [ 'headers' => $this->getHeaders(), 'connect_timeout' => 30, - ); + ]; //Set Query or Body if ($method === "GET" && $post !== null) { @@ -251,10 +251,10 @@ private function go($method, $uri, $post, $is_retry = false) return $this->processRequest($method, $uri, $post, $guzzle_args, $is_retry); //Catch Errors - http error } catch (\GuzzleHttp\Exception\RequestException $e) { - return array("error_description" => $e->getMessage()); + return ["error_description" => $e->getMessage()]; //Catch Errors - not JSON - } catch (\GuzzleHttp\Exception\ParseException $e) { - return array("error_description" => $e->getMessage()); + } catch (JsonException $e) { + return ["error_description" => $e->getMessage()]; } } } @@ -269,6 +269,9 @@ private function processRequest($method, $uri, $post, $guzzle_args, $is_retry = $this->last_response = $this->guzzle->request($method, $uri, $guzzle_args); $data = json_decode($this->last_response->getBody()->getContents(),true); + if ($data === null) { + throw new JsonException('Can\'t decode response body to JSON'); + } $this->saveLinks($data); if ($this->hasExpiredAccessTokenError($data) && !$this->shouldRefreshToken()) { if (!$is_retry) { @@ -277,7 +280,7 @@ private function processRequest($method, $uri, $post, $guzzle_args, $is_retry = $this->access_token_expires = 0; return $this->go($method, $uri, $post, true); // try one more time } else { - return array("error_description" => 'An error occurred attempting to update your access token. Please verify your refresh token and OAuth client credentials.'); + return ["error_description" => 'An error occurred attempting to update your access token. Please verify your refresh token and OAuth client credentials.']; } } return $data; @@ -286,7 +289,7 @@ private function processRequest($method, $uri, $post, $guzzle_args, $is_retry = //Clear any saved links public function clearLinks() { - $this->links = array(); + $this->links = []; } //Save Links to the Object For Easy Retrieval Later @@ -336,10 +339,10 @@ public function getLink($link_rel_string) // Get stored links (excluding link rel base uri) public function getLinks() { - $links = array(); + $links = []; foreach($this->links as $rel => $href) { $simple_rel = $rel; - $base_uris = array("fx:", static::LINK_RELATIONSHIPS_BASE_URI); + $base_uris = ["fx:", static::LINK_RELATIONSHIPS_BASE_URI]; foreach($base_uris as $base_uri) { $pos = strpos($simple_rel, $base_uri); if ($pos !== FALSE && ($simple_rel.'/' != $base_uri)) { @@ -354,7 +357,7 @@ public function getLinks() //Return any errors that exist in the response data. public function getErrors($data) { - $errors = array(); + $errors = []; if ($this->getLastStatusCode() >= 400) { if (isset($data['error_description'])) { $errors[] = $data['error_description']; @@ -396,9 +399,9 @@ public function getAcceptContentType() //Get headers for this call public function getHeaders() { - $headers = array( + $headers = [ 'FOXY-API-VERSION' => 1 - ); + ]; if ($this->access_token && $this->include_auth_header) { $headers['Authorization'] = "Bearer " . $this->access_token; } @@ -454,12 +457,12 @@ public function obtainingTokenDone() public function refreshTokenAsNeeded() { if ($this->shouldRefreshToken()) { - $refresh_token_data = array( + $refresh_token_data = [ 'grant_type' => 'refresh_token', 'refresh_token' => $this->refresh_token, 'client_id' => $this->client_id, 'client_secret' => $this->client_secret - ); + ]; $this->obtainingToken(); $data = $this->post($this->getOAuthTokenEndpoint(),$refresh_token_data); $this->obtainingTokenDone(); @@ -472,12 +475,12 @@ public function refreshTokenAsNeeded() public function getAccessTokenFromClientCredentials() { - $client_credentials_request_data = array( + $client_credentials_request_data = [ 'grant_type' => 'client_credentials', 'scope' => 'client_full_access', 'client_id' => $this->client_id, 'client_secret' => $this->client_secret - ); + ]; $this->obtainingToken(); $data = $this->post($this->getOAuthTokenEndpoint(),$client_credentials_request_data); $this->obtainingTokenDone(); @@ -490,12 +493,12 @@ public function getAccessTokenFromClientCredentials() public function getAccessTokenFromAuthorizationCode($code) { - $authorize_code_request_data = array( + $authorize_code_request_data = [ 'grant_type' => 'authorization_code', 'code' => $code, 'client_id' => $this->client_id, 'client_secret' => $this->client_secret - ); + ]; $this->obtainingToken(); $data = $this->post($this->getOAuthTokenEndpoint(),$authorize_code_request_data); $this->obtainingTokenDone(); diff --git a/src/Foxy/FoxyClient/JsonException.php b/src/Foxy/FoxyClient/JsonException.php new file mode 100644 index 0000000..d9b6d34 --- /dev/null +++ b/src/Foxy/FoxyClient/JsonException.php @@ -0,0 +1,5 @@ + Date: Mon, 6 Jun 2022 17:46:26 +0200 Subject: [PATCH 05/11] Remove guzzly cache --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 0054be2..93d84a7 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,6 @@ "require": { "guzzlehttp/guzzle": "~7.4", "php": ">=7.4", - "kevinrob/guzzle-cache-middleware": "^2.0", "ext-json": "*" }, "autoload": { From f8f1ce42950cc037d5c213e400925e6178dbd7bf Mon Sep 17 00:00:00 2001 From: Jan Peca Date: Tue, 7 Jun 2022 16:14:29 +0200 Subject: [PATCH 06/11] Use PSR 7, 17 and 18 --- composer.json | 8 +- composer.lock | 510 +++++++------- .../JsonException.php | 2 +- src/{Foxy/FoxyClient => }/FoxyClient.php | 630 ++++++++++-------- 4 files changed, 587 insertions(+), 563 deletions(-) rename src/{Foxy/FoxyClient => Exceptions}/JsonException.php (56%) rename src/{Foxy/FoxyClient => }/FoxyClient.php (50%) diff --git a/composer.json b/composer.json index 93d84a7..0007305 100644 --- a/composer.json +++ b/composer.json @@ -14,11 +14,13 @@ } ], "require": { - "guzzlehttp/guzzle": "~7.4", "php": ">=7.4", - "ext-json": "*" + "ext-json": "*", + "psr/http-message": "^1.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" }, "autoload": { - "psr-0": {"Foxy\\FoxyClient": "src/"} + "psr-4": {"Foxy\\FoxyClient\\": "src/"} } } diff --git a/composer.lock b/composer.lock index 64a0cf9..44fa485 100644 --- a/composer.lock +++ b/composer.lock @@ -4,57 +4,48 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "62265a783d7cb41128a3abb153161e80", + "content-hash": "8838325f3931ecb2ca6e6ca584a28708", "packages": [ { - "name": "guzzlehttp/guzzle", - "version": "7.4.3", + "name": "kriswallsmith/buzz", + "version": "1.2.1", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "74a8602c6faec9ef74b7a9391ac82c5e65b1cdab" + "url": "https://github.com/kriswallsmith/Buzz.git", + "reference": "2db23c3627ae7a86240ef2e68c6f8bb2c622e90d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/74a8602c6faec9ef74b7a9391ac82c5e65b1cdab", - "reference": "74a8602c6faec9ef74b7a9391ac82c5e65b1cdab", + "url": "https://api.github.com/repos/kriswallsmith/Buzz/zipball/2db23c3627ae7a86240ef2e68c6f8bb2c622e90d", + "reference": "2db23c3627ae7a86240ef2e68c6f8bb2c622e90d", "shasum": "" }, "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5", - "guzzlehttp/psr7": "^1.8.3 || ^2.1", - "php": "^7.2.5 || ^8.0", + "php": "^7.1 || ^8.0", + "php-http/httplug": "^1.1 || ^2.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "symfony/options-resolver": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, "provide": { + "php-http/client-implementation": "1.0", "psr/http-client-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "ext-curl": "*", + "nyholm/psr7": "^1.0", "php-http/client-integration-tests": "^3.0", - "phpunit/phpunit": "^8.5.5 || ^9.3.5", - "psr/log": "^1.1 || ^2.0 || ^3.0" + "phpunit/phpunit": "^7.5 || ^9.4", + "psr/log": "^1.0" }, "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" + "ext-curl": "To use our cUrl clients", + "nyholm/psr7": "For PSR-7 and PSR-17 implementation" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.4-dev" - } - }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { - "GuzzleHttp\\": "src/" + "Buzz\\": "lib" } }, "notification-url": "https://packagist.org/downloads/", @@ -63,105 +54,67 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "https://kriswallsmith.net/" }, { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" + "homepage": "https://tnyholm.se/" } ], - "description": "Guzzle is a PHP HTTP client library", + "description": "Lightweight HTTP client", + "homepage": "https://github.com/kriswallsmith/Buzz", "keywords": [ - "client", "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" + "http client" ], "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.4.3" + "issues": "https://github.com/kriswallsmith/Buzz/issues", + "source": "https://github.com/kriswallsmith/Buzz/tree/1.2.1" }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2022-05-25T13:24:33+00:00" + "time": "2022-03-25T13:55:55+00:00" }, { - "name": "guzzlehttp/promises", - "version": "1.5.1", + "name": "nyholm/psr7", + "version": "1.5.0", "source": { "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" + "url": "https://github.com/Nyholm/psr7.git", + "reference": "1461e07a0f2a975a52082ca3b769ca912b816226" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", - "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/1461e07a0f2a975a52082ca3b769ca912b816226", + "reference": "1461e07a0f2a975a52082ca3b769ca912b816226", "shasum": "" }, "require": { - "php": ">=5.5" + "php": ">=7.1", + "php-http/message-factory": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" + "http-interop/http-factory-tests": "^0.9", + "php-http/psr7-integration-tests": "^1.0", + "phpunit/phpunit": "^7.5 || 8.5 || 9.4", + "symfony/error-handler": "^4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.5-dev" + "dev-master": "1.4-dev" } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { - "GuzzleHttp\\Promise\\": "src/" + "Nyholm\\Psr7\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -169,92 +122,70 @@ "MIT" ], "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, { "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" + "email": "tobias.nyholm@gmail.com" }, { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" } ], - "description": "Guzzle promises library", + "description": "A fast PHP7 implementation of PSR-7", + "homepage": "https://tnyholm.se", "keywords": [ - "promise" + "psr-17", + "psr-7" ], "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.1" + "issues": "https://github.com/Nyholm/psr7/issues", + "source": "https://github.com/Nyholm/psr7/tree/1.5.0" }, "funding": [ { - "url": "https://github.com/GrahamCampbell", + "url": "https://github.com/Zegnat", "type": "github" }, { - "url": "https://github.com/Nyholm", + "url": "https://github.com/nyholm", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" } ], - "time": "2021-10-22T20:56:57+00:00" + "time": "2022-02-02T18:37:57+00:00" }, { - "name": "guzzlehttp/psr7", - "version": "2.2.1", + "name": "php-http/httplug", + "version": "2.3.0", "source": { "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2" + "url": "https://github.com/php-http/httplug.git", + "reference": "f640739f80dfa1152533976e3c112477f69274eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c94a94f120803a18554c1805ef2e539f8285f9a2", - "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2", + "url": "https://api.github.com/repos/php-http/httplug/zipball/f640739f80dfa1152533976e3c112477f69274eb", + "reference": "f640739f80dfa1152533976e3c112477f69274eb", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" + "php": "^7.1 || ^8.0", + "php-http/promise": "^1.1", + "psr/http-client": "^1.0", + "psr/http-message": "^1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.8 || ^9.3.10" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + "friends-of-phpspec/phpspec-code-coverage": "^4.1", + "phpspec/phpspec": "^5.1 || ^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.x-dev" } }, "autoload": { "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" + "Http\\Client\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -263,109 +194,111 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "HTTPlug, the HTTP client abstraction for PHP", + "homepage": "http://httplug.io", + "keywords": [ + "client", + "http" + ], + "support": { + "issues": "https://github.com/php-http/httplug/issues", + "source": "https://github.com/php-http/httplug/tree/2.3.0" + }, + "time": "2022-02-21T09:52:22+00:00" + }, + { + "name": "php-http/message-factory", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-http/message-factory.git", + "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", + "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" + "email": "mark.sagikazar@gmail.com" } ], - "description": "PSR-7 message implementation that also provides common utility methods", + "description": "Factory interfaces for PSR-7 HTTP Message", + "homepage": "http://php-http.org", "keywords": [ + "factory", "http", "message", - "psr-7", - "request", - "response", "stream", - "uri", - "url" + "uri" ], "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.2.1" + "issues": "https://github.com/php-http/message-factory/issues", + "source": "https://github.com/php-http/message-factory/tree/master" }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2022-03-20T21:55:58+00:00" + "time": "2015-12-19T14:08:53+00:00" }, { - "name": "kevinrob/guzzle-cache-middleware", - "version": "v2.1.1", + "name": "php-http/promise", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/Kevinrob/guzzle-cache-middleware.git", - "reference": "6952064f7747756b0be7b4c234c0fd7535ea4c8c" + "url": "https://github.com/php-http/promise.git", + "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Kevinrob/guzzle-cache-middleware/zipball/6952064f7747756b0be7b4c234c0fd7535ea4c8c", - "reference": "6952064f7747756b0be7b4c234c0fd7535ea4c8c", + "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", + "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", "shasum": "" }, "require": { - "php": ">=5.5.0" + "php": "^7.1 || ^8.0" }, "require-dev": { - "cache/array-adapter": "^0.4", - "doctrine/cache": "^1.0", - "guzzlehttp/guzzle": "^6.0", - "illuminate/cache": "^5.0", - "league/flysystem": "^1.0", - "phpunit/phpunit": "^4.0 || ^5.0", - "psr/cache": "^1.0" - }, - "suggest": { - "doctrine/cache": "This library have a lot of ready-to-use cache storage (to be use with Kevinrob\\GuzzleCache\\Storage\\DoctrineCacheStorage)", - "guzzlehttp/guzzle": "For using this library. It was created for Guzzle6. (but you can use it with any PSR-7 HTTP Client)", - "laravel/framework": "To be use with Kevinrob\\GuzzleCache\\Storage\\LaravelCacheStorage", - "league/flysystem": "To be use with Kevinrob\\GuzzleCache\\Storage\\FlysystemStorage", - "psr/cache": "To be use with Kevinrob\\GuzzleCache\\Storage\\Psr6CacheStorage" + "friends-of-phpspec/phpspec-code-coverage": "^4.3.2", + "phpspec/phpspec": "^5.1.2 || ^6.2" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, "autoload": { "psr-4": { - "Kevinrob\\GuzzleCache\\": "src/" + "Http\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -374,39 +307,24 @@ ], "authors": [ { - "name": "Kevin Robatel", - "email": "kevinrob2@gmail.com", - "homepage": "https://github.com/Kevinrob" + "name": "Joel Wurtz", + "email": "joel.wurtz@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" } ], - "description": "A HTTP/1.1 Cache for Guzzle 6. It's a simple Middleware to be added in the HandlerStack. (RFC 7234)", - "homepage": "https://github.com/Kevinrob/guzzle-cache-middleware", + "description": "Promise used for asynchronous HTTP requests", + "homepage": "http://httplug.io", "keywords": [ - "Etag", - "Flysystem", - "Guzzle", - "cache", - "cache-control", - "doctrine", - "expiration", - "guzzle6", - "handler", - "http", - "http 1.1", - "middleware", - "performance", - "php", - "promise", - "psr6", - "psr7", - "rfc7234", - "validation" + "promise" ], "support": { - "issues": "https://github.com/Kevinrob/guzzle-cache-middleware/issues", - "source": "https://github.com/Kevinrob/guzzle-cache-middleware/tree/master" + "issues": "https://github.com/php-http/promise/issues", + "source": "https://github.com/php-http/promise/tree/1.1.0" }, - "time": "2017-08-17T12:23:43+00:00" + "time": "2020-07-07T09:29:14+00:00" }, { "name": "psr/http-client", @@ -569,30 +487,35 @@ "time": "2016-08-06T14:39:51+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "3.0.3", + "name": "symfony/deprecation-contracts", + "version": "v3.1.0", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", "shasum": "" }, "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" + "php": ">=8.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, "autoload": { "files": [ - "src/getallheaders.php" + "function.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -601,47 +524,60 @@ ], "authors": [ { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A polyfill for getallheaders.", + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.0" }, - "time": "2019-03-08T08:55:37+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-02-25T11:15:52+00:00" }, { - "name": "symfony/deprecation-contracts", - "version": "v3.1.0", + "name": "symfony/options-resolver", + "version": "v6.1.0", "source": { "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918" + "url": "https://github.com/symfony/options-resolver.git", + "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", - "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a3016f5442e28386ded73c43a32a5b68586dd1c4", + "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.1-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, "autoload": { - "files": [ - "function.php" + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -650,18 +586,23 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "A generic function and convention to trigger deprecation notices", + "description": "Provides an improved replacement for the array_replace PHP function", "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.0" + "source": "https://github.com/symfony/options-resolver/tree/v6.1.0" }, "funding": [ { @@ -687,7 +628,8 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.4" + "php": ">=7.4", + "ext-json": "*" }, "platform-dev": [], "plugin-api-version": "2.2.0" diff --git a/src/Foxy/FoxyClient/JsonException.php b/src/Exceptions/JsonException.php similarity index 56% rename from src/Foxy/FoxyClient/JsonException.php rename to src/Exceptions/JsonException.php index d9b6d34..c8f4ce1 100644 --- a/src/Foxy/FoxyClient/JsonException.php +++ b/src/Exceptions/JsonException.php @@ -1,5 +1,5 @@ guzzle = $guzzle; + * Used by OAuth for getting new Access Token + */ + private string $client_secret = ''; + private ClientInterface $client; + private ServerRequestFactoryInterface $requestFactory; + private ?ResponseInterface $last_response = null; + private array $registered_link_relations = ['self', 'first', 'prev', 'next', 'last']; + private array $links = []; + private bool $use_sandbox = false; + private bool $obtaining_updated_access_token = false; + private bool $include_auth_header = true; + private bool $handle_exceptions = true; + private string $accept_content_type = ''; + private string $api_home = ''; + private string $authorization_endpoint = ''; + + public function __construct( + ServerRequestFactoryInterface $requestFactory, + ClientInterface $client, + array $config = [] + ) { + $this->client = $client; + $this->requestFactory = $requestFactory; $this->api_home = static::PRODUCTION_API_HOME; $this->authorization_endpoint = static::PRODUCTION_AUTHORIZATION_ENDPOINT; $this->updateFromConfig($config); } - public function updateFromConfig($config) + public function updateFromConfig(array $config): void { - $valid_config_options = [ - 'api_home', - 'authorization_endpoint', - 'use_sandbox', - 'access_token', - 'access_token_expires', - 'refresh_token', - 'client_id', - 'client_secret', - 'handle_exceptions' - ]; - foreach($valid_config_options as $valid_config_option) { + foreach (self::$valid_config_options as $valid_config_option) { if (array_key_exists($valid_config_option, $config)) { $this->{$valid_config_option} = $config[$valid_config_option]; } } - if ($this->use_sandbox && (!array_key_exists('api_home', $config) || !array_key_exists('authorization_endpoint', $config))) { + if ($this->use_sandbox && (!array_key_exists('api_home', $config) || !array_key_exists( + 'authorization_endpoint', + $config + ))) { $this->api_home = static::SANDBOX_API_HOME; $this->authorization_endpoint = static::SANDBOX_AUTHORIZATION_ENDPOINT; } } - public function clearCredentials() + public function clearCredentials(): void { - $config = [ + $this->updateFromConfig([ 'access_token' => '', 'access_token_expires' => '', 'refresh_token' => '', 'client_id' => '', 'client_secret' => '' - ]; - $this->updateFromConfig($config); + ]); } - public function setAccessToken($access_token) - { - $this->access_token = $access_token; - } - public function getAccessToken() + public function getAccessToken(): string { return $this->access_token; } - public function setAccessTokenExpires($access_token_expires) + public function setAccessToken(string $access_token): void { - $this->access_token_expires = $access_token_expires; + $this->access_token = $access_token; } - public function getAccessTokenExpires() + + public function getAccessTokenExpires(): int { return $this->access_token_expires; } - public function setRefreshToken($refresh_token) + public function setAccessTokenExpires(int $access_token_expires): void { - $this->refresh_token = $refresh_token; + $this->access_token_expires = $access_token_expires; } - public function getRefreshToken() + + public function getRefreshToken(): string { return $this->refresh_token; } - public function setClientId($client_id) + public function setRefreshToken(string $refresh_token): void { - $this->client_id = $client_id; + $this->refresh_token = $refresh_token; } - public function getClientId() + + public function getClientId(): string { return $this->client_id; } - public function setClientSecret($client_secret) + public function setClientId(string $client_id): void { - $this->client_secret = $client_secret; + $this->client_id = $client_id; } - public function getClientSecret() + + public function getClientSecret(): string { return $this->client_secret; } - public function setUseSandbox($use_sandbox) + public function setClientSecret(string $client_secret): void { - $this->use_sandbox = $use_sandbox; + $this->client_secret = $client_secret; } - public function getUseSandbox() + + public function getUseSandbox(): bool { return $this->use_sandbox; } - public function getApiHome() + public function setUseSandbox(bool $use_sandbox): void { - return $this->api_home; + $this->use_sandbox = $use_sandbox; } - public function getAuthorizationEndpoint() + public function getAuthorizationEndpoint(): string { return $this->authorization_endpoint; } - public function get($uri = "", $post = null) + public function get(string $uri = "", array $post = null): array { return $this->go('GET', $uri, $post); } - public function put($uri, $post = null) + private function go(string $method, string $uri, ?array $post, bool $is_retry = false): array { - return $this->go('PUT', $uri, $post); + if (!$this->obtaining_updated_access_token) { + $this->refreshTokenAsNeeded(); + } + + //Nothing passed in, set uri to homepage + if (!$uri) { + $uri = $this->getApiHome(); + } + + //Setup Guzzle Details + $guzzle_args = [ + 'headers' => $this->getHeaders(), + ]; + + //Set Query or Body + if ($method === "GET" && $post !== null) { + $guzzle_args['query'] = $post; + } elseif ($post !== null) { + $guzzle_args['form_params'] = $post; + } + + if (!$this->handle_exceptions) { + return $this->processRequest($method, $uri, $post, $guzzle_args, $is_retry); + } + + try { + return $this->processRequest($method, $uri, $post, $guzzle_args, $is_retry); + } catch (ClientExceptionInterface $e) { + return ["error_description" => $e->getMessage()]; + } catch (Exception $e) { + return ["error_description" => $e->getMessage()]; + } } - public function post($uri, $post = null) + public function refreshTokenAsNeeded(): void { - return $this->go('POST', $uri, $post); + if (!$this->shouldRefreshToken()) { + return; + } + + $refresh_token_data = [ + 'grant_type' => 'refresh_token', + 'refresh_token' => $this->refresh_token, + 'client_id' => $this->client_id, + 'client_secret' => $this->client_secret + ]; + + $this->obtainingToken(); + $data = $this->post($this->getOAuthTokenEndpoint(), $refresh_token_data); + $this->obtainingTokenDone(); + + if ($this->getLastStatusCode() === 200) { + $this->access_token_expires = time() + $data['expires_in']; + $this->access_token = $data['access_token']; + } } - public function patch($uri, $post = null) + public function shouldRefreshToken(): bool { - return $this->go('PATCH', $uri, $post); + return ($this->hasOAuthCredentialsForTokenRefresh() && $this->accessTokenNeedsRefreshing()); } - public function delete($uri, $post = null) + public function hasOAuthCredentialsForTokenRefresh(): bool { - return $this->go('DELETE', $uri, $post); + return ($this->client_id && $this->client_secret && $this->refresh_token); } - public function options($uri, $post = null) + public function accessTokenNeedsRefreshing(): bool { - return $this->go('OPTIONS', $uri, $post); + return (!$this->access_token_expires || (($this->access_token_expires - 30) < time())); } - public function head($uri, $post = null) + public function obtainingToken(): void { - return $this->go('HEAD', $uri, $post); + $this->obtaining_updated_access_token = true; + $this->include_auth_header = false; } - private function go($method, $uri, $post, $is_retry = false) + public function post(string $uri, array $post = null): array { - if (!$this->obtaining_updated_access_token) { - $this->refreshTokenAsNeeded(); + return $this->go('POST', $uri, $post); + } + + public function getOAuthTokenEndpoint(): string + { + if ($this->oauth_token_endpoint !== null) { + return $this->oauth_token_endpoint; } - //Nothing passed in, set uri to homepage - if (!$uri) { - $uri = $this->getApiHome(); + $this->obtainingToken(); + $this->get(); + + if ($this->getLastStatusCode() === 200) { + $this->oauth_token_endpoint = $this->getLink("token"); + return $this->oauth_token_endpoint; } - //Setup Guzzle Details - $guzzle_args = [ - 'headers' => $this->getHeaders(), - 'connect_timeout' => 30, - ]; + trigger_error('ERROR IN getOAuthTokenEndpoint: ' . $this->getLastStatusCode()); + $this->oauth_token_endpoint = $this->api_home . '/token'; + return $this->oauth_token_endpoint; + } - //Set Query or Body - if ($method === "GET" && $post !== null) { - $guzzle_args['query'] = $post; - } elseif ($post !== null) { - $guzzle_args['form_params'] = $post; + public function getLastStatusCode(): ?int + { + if ($this->last_response === null) { + return null; } - if (!$this->handle_exceptions) { - return $this->processRequest($method, $uri, $post, $guzzle_args, $is_retry); - } else { - try { - return $this->processRequest($method, $uri, $post, $guzzle_args, $is_retry); - //Catch Errors - http error - } catch (\GuzzleHttp\Exception\RequestException $e) { - return ["error_description" => $e->getMessage()]; - //Catch Errors - not JSON - } catch (JsonException $e) { - return ["error_description" => $e->getMessage()]; + return $this->last_response->getStatusCode(); + } + + //Clear any saved links + + public function getLink(string $link_rel_string): string + { + $search_string = $link_rel_string; + + if (!in_array($link_rel_string, $this->registered_link_relations) + && strpos($link_rel_string, "fx:") === false + && strpos($link_rel_string, static::LINK_RELATIONSHIPS_BASE_URI) === false) { + if ($this->getAcceptContentType() == static::DEFAULT_ACCEPT_CONTENT_TYPE) { + $search_string = 'fx:' . $search_string; + } else { + $search_string = static::LINK_RELATIONSHIPS_BASE_URI . $search_string; } } + + return $this->links[$search_string] ?? ""; + } + + //Save Links to the Object For Easy Retrieval Later + + public function getAcceptContentType(): string + { + if ($this->accept_content_type == '') { + return static::DEFAULT_ACCEPT_CONTENT_TYPE; + } + + return $this->accept_content_type; } - private function processRequest($method, $uri, $post, $guzzle_args, $is_retry = false) + // Get a link out of the internally stored links + // The link relationship base uri can be excluded ("fx:" for HAL, full URI for Siren) + + public function setAcceptContentType(string $accept_content_type): void { + $this->accept_content_type = $accept_content_type; + } + + // Get stored links (excluding link rel base uri) + + public function obtainingTokenDone(): void + { + $this->obtaining_updated_access_token = false; + $this->include_auth_header = true; + } + + //Return any errors that exist in the response data. + + public function getApiHome(): string + { + return $this->api_home; + } + + public function getHeaders(): array + { + $headers = [ + 'FOXY-API-VERSION' => 1 + ]; + + if ($this->access_token && $this->include_auth_header) { + $headers['Authorization'] = "Bearer " . $this->access_token; + } + + $headers['Accept'] = $this->getAcceptContentType(); + + return $headers; + } + + // Set a custom supported content type (application/hal+json, application/vnd.siren+json, etc) + + /** + * @throws ClientExceptionInterface + * @throws JsonException + */ + private function processRequest( + string $method, + string $uri, + ?array $post, + array $options, + bool $is_retry = false + ): array { // special case for PATCHing a Downloadable File - if ($post !== null && is_array($post) && array_key_exists('file', $post) && $method == 'PATCH') { + if (is_array($post) && array_key_exists('file', $post) && $method == 'PATCH') { $method = 'POST'; - $guzzle_args['headers']['X-HTTP-Method-Override'] = 'PATCH'; + $options['headers']['X-HTTP-Method-Override'] = 'PATCH'; + } + + $request = $this->requestFactory->createServerRequest($method, $uri); + + if (!empty($options['query'])) { + $request = $request->withQueryParams($options['query']); + } + + if (isset($options['form_params'])) { + $body = $request->getBody(); + $body->write(http_build_query($options['form_params'], '', '&')); + $request = $request->withBody($body); } - $this->last_response = $this->guzzle->request($method, $uri, $guzzle_args); - $data = json_decode($this->last_response->getBody()->getContents(),true); + if (is_array($options['headers']) && !empty($options['headers'])) { + foreach ($options['headers'] as $headerName => $headerValue) { + $request = $request->withAddedHeader($headerName, $headerValue); + } + } + + $response = $this->client->sendRequest($request); + + $this->last_response = $response; + $data = json_decode($response->getBody()->getContents(), true); + if ($data === null) { throw new JsonException('Can\'t decode response body to JSON'); } + $this->saveLinks($data); + if ($this->hasExpiredAccessTokenError($data) && !$this->shouldRefreshToken()) { if (!$is_retry) { // we should have gotten a refresh token... looks like our access_token_expires was incorrect // so we'll clear it out to force a refresh $this->access_token_expires = 0; return $this->go($method, $uri, $post, true); // try one more time - } else { - return ["error_description" => 'An error occurred attempting to update your access token. Please verify your refresh token and OAuth client credentials.']; } + + return ["error_description" => 'An error occurred attempting to update your access token. Please verify your refresh token and OAuth client credentials.']; } - return $data; - } - //Clear any saved links - public function clearLinks() - { - $this->links = []; + return $data; } - //Save Links to the Object For Easy Retrieval Later - public function saveLinks($data) + public function saveLinks(array $data): void { if (isset($data['_links'])) { foreach ($data['_links'] as $rel => $link) { if (!in_array($rel, $this->registered_link_relations) && $rel != 'curies' - && $rel.'/' != static::LINK_RELATIONSHIPS_BASE_URI) { + && $rel . '/' != static::LINK_RELATIONSHIPS_BASE_URI) { $this->links[$rel] = $link['href']; } } - } else if (isset($data['links'])) { + + return; + } + + if (isset($data['links'])) { foreach ($data['links'] as $link) { foreach ($link['rel'] as $rel) { if (!in_array($rel, $this->registered_link_relations) - && $rel.'/' != static::LINK_RELATIONSHIPS_BASE_URI) { + && $rel . '/' != static::LINK_RELATIONSHIPS_BASE_URI) { $this->links[$rel] = $link['href']; } } @@ -315,49 +473,23 @@ public function saveLinks($data) } } - // Get a link out of the internally stored links - // The link relationship base uri can be excluded ("fx:" for HAL, full URI for Siren) - public function getLink($link_rel_string) - { - $search_string = $link_rel_string; - if (!in_array($link_rel_string, $this->registered_link_relations) - && strpos($link_rel_string, "fx:") === FALSE - && strpos($link_rel_string, static::LINK_RELATIONSHIPS_BASE_URI) === FALSE) { - if ($this->getAcceptContentType() == static::DEFAULT_ACCEPT_CONTENT_TYPE) { - $search_string = 'fx:' . $search_string; - } else { - $search_string = static::LINK_RELATIONSHIPS_BASE_URI . $search_string; - } - } - if (isset($this->links[$search_string])) { - return $this->links[$search_string]; - } else { - return ""; - } - } + //Get headers for this call - // Get stored links (excluding link rel base uri) - public function getLinks() + public function hasExpiredAccessTokenError(array $data): bool { - $links = []; - foreach($this->links as $rel => $href) { - $simple_rel = $rel; - $base_uris = ["fx:", static::LINK_RELATIONSHIPS_BASE_URI]; - foreach($base_uris as $base_uri) { - $pos = strpos($simple_rel, $base_uri); - if ($pos !== FALSE && ($simple_rel.'/' != $base_uri)) { - $simple_rel = substr($simple_rel, strlen($base_uri)); - } - } - $links[$simple_rel] = $href; + $errors = $this->getErrors($data); + + if (in_array('The access token provided has expired', $errors)) { + return true; } - return $links; + + return false; } - //Return any errors that exist in the response data. - public function getErrors($data) + public function getErrors(array $data): array { $errors = []; + if ($this->getLastStatusCode() >= 400) { if (isset($data['error_description'])) { $errors[] = $data['error_description']; @@ -369,162 +501,110 @@ public function getErrors($data) $errors[] = 'No data returned.'; } } + return $errors; } - public function hasExpiredAccessTokenError($data) + public function put(string $uri, array $post = null): array { - $errors = $this->getErrors($data); - if (in_array('The access token provided has expired', $errors)) { - return true; - } - return false; + return $this->go('PUT', $uri, $post); } - // Set a custom supported content type (application/hal+json, application/vnd.siren+json, etc) - public function setAcceptContentType($accept_content_type) + public function patch(string $uri, array $post = null): array { - $this->accept_content_type = $accept_content_type; + return $this->go('PATCH', $uri, $post); } - public function getAcceptContentType() + public function delete(string $uri, array $post = null): array { - if ($this->accept_content_type == '') { - return static::DEFAULT_ACCEPT_CONTENT_TYPE; - } - - return $this->accept_content_type; + return $this->go('DELETE', $uri, $post); } - //Get headers for this call - public function getHeaders() + public function options(string $uri, array $post = null): array { - $headers = [ - 'FOXY-API-VERSION' => 1 - ]; - if ($this->access_token && $this->include_auth_header) { - $headers['Authorization'] = "Bearer " . $this->access_token; - } - $headers['Accept'] = $this->getAcceptContentType(); - - return $headers; + return $this->go('OPTIONS', $uri, $post); } - //Get Last Status Code - public function getLastStatusCode() + public function head(string $uri, array $post = null): array { - if ($this->last_response == '') { - return ''; - } - return $this->last_response->getStatusCode(); - } - //Get Last Response Header - public function getLastResponseHeader($header) - { - if ($this->last_response == '') { - return ''; - } - return $this->last_response->getHeader($header); + return $this->go('HEAD', $uri, $post); } - public function hasOAuthCredentialsForTokenRefresh() + public function clearLinks(): void { - return ($this->client_id && $this->client_secret && $this->refresh_token); + $this->links = []; } - public function accessTokenNeedsRefreshing() + public function getLinks(): array { - return (!$this->access_token_expires || (($this->access_token_expires - 30) < time())); - } + $links = []; + foreach ($this->links as $rel => $href) { + $simple_rel = $rel; + $base_uris = ["fx:", static::LINK_RELATIONSHIPS_BASE_URI]; - public function shouldRefreshToken() - { - return ($this->hasOAuthCredentialsForTokenRefresh() && $this->accessTokenNeedsRefreshing()); - } + foreach ($base_uris as $base_uri) { + $pos = strpos($simple_rel, $base_uri); + if ($pos !== false && ($simple_rel . '/' != $base_uri)) { + $simple_rel = substr($simple_rel, strlen($base_uri)); + } + } - public function obtainingToken() - { - $this->obtaining_updated_access_token = true; - $this->include_auth_header = false; - } + $links[$simple_rel] = $href; + } - public function obtainingTokenDone() - { - $this->obtaining_updated_access_token = false; - $this->include_auth_header = true; + return $links; } - public function refreshTokenAsNeeded() - { - if ($this->shouldRefreshToken()) { - $refresh_token_data = [ - 'grant_type' => 'refresh_token', - 'refresh_token' => $this->refresh_token, - 'client_id' => $this->client_id, - 'client_secret' => $this->client_secret - ]; - $this->obtainingToken(); - $data = $this->post($this->getOAuthTokenEndpoint(),$refresh_token_data); - $this->obtainingTokenDone(); - if ($this->getLastStatusCode() == '200') { - $this->access_token_expires = time() + $data['expires_in']; - $this->access_token = $data['access_token']; - } + public function getLastResponseHeader(string $header): ?array + { + if ($this->last_response === null) { + return null; } + + return $this->last_response->getHeader($header); } - public function getAccessTokenFromClientCredentials() + public function getAccessTokenFromClientCredentials(): array { $client_credentials_request_data = [ - 'grant_type' => 'client_credentials', - 'scope' => 'client_full_access', - 'client_id' => $this->client_id, - 'client_secret' => $this->client_secret + 'grant_type' => 'client_credentials', + 'scope' => 'client_full_access', + 'client_id' => $this->client_id, + 'client_secret' => $this->client_secret ]; + $this->obtainingToken(); - $data = $this->post($this->getOAuthTokenEndpoint(),$client_credentials_request_data); + $data = $this->post($this->getOAuthTokenEndpoint(), $client_credentials_request_data); $this->obtainingTokenDone(); - if ($this->getLastStatusCode() == '200') { + + if ($this->getLastStatusCode() === 200) { $this->access_token_expires = time() + $data['expires_in']; $this->access_token = $data['access_token']; } + return $data; } - public function getAccessTokenFromAuthorizationCode($code) + public function getAccessTokenFromAuthorizationCode(string $code): array { $authorize_code_request_data = [ - 'grant_type' => 'authorization_code', - 'code' => $code, - 'client_id' => $this->client_id, - 'client_secret' => $this->client_secret + 'grant_type' => 'authorization_code', + 'code' => $code, + 'client_id' => $this->client_id, + 'client_secret' => $this->client_secret ]; + $this->obtainingToken(); - $data = $this->post($this->getOAuthTokenEndpoint(),$authorize_code_request_data); + $data = $this->post($this->getOAuthTokenEndpoint(), $authorize_code_request_data); $this->obtainingTokenDone(); - if ($this->getLastStatusCode() == '200') { + + if ($this->getLastStatusCode() === 200) { $this->access_token_expires = time() + $data['expires_in']; $this->access_token = $data['access_token']; $this->refresh_token = $data['refresh_token']; } - return $data; - } - public function getOAuthTokenEndpoint() - { - if ($this->oauth_token_endpoint == '') { - $this->obtainingToken(); - $data = $this->get(); - if ($this->getLastStatusCode() == '200') { - $this->oauth_token_endpoint = $this->getLink("token"); - } else { - trigger_error('ERROR IN getOAuthTokenEndpoint: ' . $this->getLastStatusCode()); - //trigger_error(serialize($this->last_response->json())); - //die or hard code the endpoint? - $this->oauth_token_endpoint = $this->api_home . '/token'; - } - } - return $this->oauth_token_endpoint; + return $data; } } From 03291eeaad6b91fbe8ed34df275c16ecd516ea93 Mon Sep 17 00:00:00 2001 From: Jan Peca Date: Tue, 7 Jun 2022 16:17:51 +0200 Subject: [PATCH 07/11] Update init docs --- src/FoxyClient.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/FoxyClient.php b/src/FoxyClient.php index 3d84309..950b299 100644 --- a/src/FoxyClient.php +++ b/src/FoxyClient.php @@ -4,26 +4,28 @@ /* -The FoxyClient wraps Guzzle with some useful helpers for working with link relationships. config: use_sandbox (boolean): set to true to connect to the API sandbox for testing. access_token (string): pass in your properly scoped OAuth access_token to be used as a Authentication Bearer HTTP header. Examples: +$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory(); +$psr18Client = new \Buzz\Client\Curl($psr17Factory); + //Get Homepage -$fc = new FoxyClient($guzzle); +$fc = new FoxyClient($psr17Factory, $psr18Client); $result = $fc->get(); die("
" . print_r($result, 1) . "
"); //Get Authenticated Store -$fc = new FoxyClient($guzzle, array( +$fc = new FoxyClient($psr17Factory, $psr18Client, [ 'client_id' => $client_id, 'client_secret' => $client_secret, 'refresh_token' => $refresh_token, 'access_token' => $access_token, // optional 'access_token_expires' => $access_token_expires // optional - ) + ] ); $fc->get(); $result = $fc->get($fc->getLink("fx:store")); From c87063ce8ff6819524738c8237098c45d4826040 Mon Sep 17 00:00:00 2001 From: Jan Peca Date: Wed, 8 Jun 2022 10:30:34 +0200 Subject: [PATCH 08/11] Better error handling --- src/Exceptions/JsonException.php | 2 +- src/Exceptions/RequestException.php | 43 ++++++++++++++++++++++++++++ src/Exceptions/ResponseException.php | 29 +++++++++++++++++++ src/FoxyClient.php | 24 ++++++++++++++-- 4 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 src/Exceptions/RequestException.php create mode 100644 src/Exceptions/ResponseException.php diff --git a/src/Exceptions/JsonException.php b/src/Exceptions/JsonException.php index c8f4ce1..2ac0f43 100644 --- a/src/Exceptions/JsonException.php +++ b/src/Exceptions/JsonException.php @@ -2,4 +2,4 @@ namespace Foxy\FoxyClient\Exceptions; -class JsonException extends \Exception {} \ No newline at end of file +class JsonException extends ResponseException {} \ No newline at end of file diff --git a/src/Exceptions/RequestException.php b/src/Exceptions/RequestException.php new file mode 100644 index 0000000..d6b8858 --- /dev/null +++ b/src/Exceptions/RequestException.php @@ -0,0 +1,43 @@ +getStatusCode() : 0; + parent::__construct($message, $code, $previous); + $this->request = $request; + $this->response = $response; + } + + public function getRequest(): RequestInterface + { + return $this->request; + } + + public function getResponse(): ?ResponseInterface + { + return $this->response; + } + + public function hasResponse(): bool + { + return $this->response !== null; + } +} \ No newline at end of file diff --git a/src/Exceptions/ResponseException.php b/src/Exceptions/ResponseException.php new file mode 100644 index 0000000..053f8c3 --- /dev/null +++ b/src/Exceptions/ResponseException.php @@ -0,0 +1,29 @@ +processRequest($method, $uri, $post, $guzzle_args, $is_retry); + } catch (JsonException $e) { + return [ + "error_description" => $e->getMessage(), + "error_code" => 400, + "error_contents" => $e->getResponse()->getBody()->getContents(), + ]; + } catch (ResponseException $e) { + $response = $e->getResponse(); + $responseContent = $response->getBody()->getContents(); + $parsedResponseContent = json_decode($responseContent, true); + + return [ + "error_description" => $e->getMessage(), + "error_code" => $response->getStatusCode(), + "error_contents" => $responseContent, + "error_contents_parsed" => $parsedResponseContent, + ]; } catch (ClientExceptionInterface $e) { return ["error_description" => $e->getMessage()]; } catch (Exception $e) { @@ -425,12 +443,14 @@ private function processRequest( } $response = $this->client->sendRequest($request); - + if ($response->getStatusCode() >= 400) { + throw new ResponseException('Error completing request', $request, $response); + } $this->last_response = $response; $data = json_decode($response->getBody()->getContents(), true); if ($data === null) { - throw new JsonException('Can\'t decode response body to JSON'); + throw new JsonException('Error decoding response body', $request, $response); } $this->saveLinks($data); From b5b9ffac2e5112a8fec50641174e5ac7396e2016 Mon Sep 17 00:00:00 2001 From: Jan Peca Date: Thu, 9 Jun 2022 14:59:18 +0200 Subject: [PATCH 09/11] Fix refreshing access token with invalid expiration date --- src/FoxyClient.php | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/FoxyClient.php b/src/FoxyClient.php index ce78a04..74a85e8 100644 --- a/src/FoxyClient.php +++ b/src/FoxyClient.php @@ -247,6 +247,19 @@ private function go(string $method, string $uri, ?array $post, bool $is_retry = $responseContent = $response->getBody()->getContents(); $parsedResponseContent = json_decode($responseContent, true); + if (!empty($parsedResponseContent)) { + if ($this->hasExpiredAccessTokenError($parsedResponseContent) && !$this->shouldRefreshToken()) { + if (!$is_retry) { + // we should have gotten a refresh token... looks like our access_token_expires was incorrect + // so we'll clear it out to force a refresh + $this->access_token_expires = 0; + return $this->go($method, $uri, $post, true); // try one more time + } + + return ["error_description" => 'An error occurred attempting to update your access token. Please verify your refresh token and OAuth client credentials.']; + } + } + return [ "error_description" => $e->getMessage(), "error_code" => $response->getStatusCode(), @@ -415,8 +428,7 @@ private function processRequest( string $method, string $uri, ?array $post, - array $options, - bool $is_retry = false + array $options ): array { // special case for PATCHing a Downloadable File if (is_array($post) && array_key_exists('file', $post) && $method == 'PATCH') { @@ -443,10 +455,12 @@ private function processRequest( } $response = $this->client->sendRequest($request); + $this->last_response = $response; + if ($response->getStatusCode() >= 400) { throw new ResponseException('Error completing request', $request, $response); } - $this->last_response = $response; + $data = json_decode($response->getBody()->getContents(), true); if ($data === null) { @@ -455,17 +469,6 @@ private function processRequest( $this->saveLinks($data); - if ($this->hasExpiredAccessTokenError($data) && !$this->shouldRefreshToken()) { - if (!$is_retry) { - // we should have gotten a refresh token... looks like our access_token_expires was incorrect - // so we'll clear it out to force a refresh - $this->access_token_expires = 0; - return $this->go($method, $uri, $post, true); // try one more time - } - - return ["error_description" => 'An error occurred attempting to update your access token. Please verify your refresh token and OAuth client credentials.']; - } - return $data; } From fc63353ae57c2dac3c00c50b420224565ac815cf Mon Sep 17 00:00:00 2001 From: Jan Peca Date: Tue, 14 Jun 2022 16:28:28 +0200 Subject: [PATCH 10/11] Fix setting GET query params --- src/FoxyClient.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/FoxyClient.php b/src/FoxyClient.php index 74a85e8..b02d1df 100644 --- a/src/FoxyClient.php +++ b/src/FoxyClient.php @@ -39,8 +39,8 @@ use Foxy\FoxyClient\Exceptions\ResponseException; use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestFactoryInterface; class FoxyClient { @@ -86,7 +86,7 @@ class FoxyClient */ private string $client_secret = ''; private ClientInterface $client; - private ServerRequestFactoryInterface $requestFactory; + private RequestFactoryInterface $requestFactory; private ?ResponseInterface $last_response = null; private array $registered_link_relations = ['self', 'first', 'prev', 'next', 'last']; private array $links = []; @@ -99,7 +99,7 @@ class FoxyClient private string $authorization_endpoint = ''; public function __construct( - ServerRequestFactoryInterface $requestFactory, + RequestFactoryInterface $requestFactory, ClientInterface $client, array $config = [] ) { @@ -436,10 +436,12 @@ private function processRequest( $options['headers']['X-HTTP-Method-Override'] = 'PATCH'; } - $request = $this->requestFactory->createServerRequest($method, $uri); + $request = $this->requestFactory->createRequest($method, $uri); if (!empty($options['query'])) { - $request = $request->withQueryParams($options['query']); + $uri = $request->getUri(); + $uri = $uri->withQuery(http_build_query($options['query'])); + $request = $request->withUri($uri); } if (isset($options['form_params'])) { From b8516a6d303cc8da71bd22c436a55688f86f5fff Mon Sep 17 00:00:00 2001 From: Jan Peca Date: Wed, 29 Jun 2022 20:28:31 +0200 Subject: [PATCH 11/11] Use JSON_THROW_ON_ERROR --- src/FoxyClient.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/FoxyClient.php b/src/FoxyClient.php index b02d1df..562c2f5 100644 --- a/src/FoxyClient.php +++ b/src/FoxyClient.php @@ -463,15 +463,14 @@ private function processRequest( throw new ResponseException('Error completing request', $request, $response); } - $data = json_decode($response->getBody()->getContents(), true); + try { + $data = json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); + $this->saveLinks($data); - if ($data === null) { - throw new JsonException('Error decoding response body', $request, $response); + return $data; + } catch (\JsonException $e) { + throw new JsonException($e->getMessage(), $request, $response); } - - $this->saveLinks($data); - - return $data; } public function saveLinks(array $data): void