Support Guzzle 8 while keeping Guzzle 7 compatibility - #880
Conversation
0f6da5c to
74c6695
Compare
bd6ecb4 to
a9ae9ca
Compare
Update the api.pebble template for two Guzzle 8 breaking changes: - Replace GuzzleHttp\Utils::jsonEncode(), removed in Guzzle 8, with native json_encode() using JSON_THROW_ON_ERROR. - Stop calling RequestException::getResponse(), which was moved to ResponseException in Guzzle 8. Catch BadResponseException for error responses instead, which carries the response in both Guzzle 7 and 8, and TransferException for failures without a response such as connection errors (both in sync and async paths). Also widen the guzzlehttp/guzzle constraint to ^7.3 || ^8.0. orchestra/testbench (via laravel/framework, which still requires guzzle ^7.8.2) cannot be installed together with guzzle ^8.0 only, which made composer install unresolvable and broke CI.
Generated by python3 generate-code.py from the updated api.pebble template. No manual edits.
The generate-code workflow regenerates against the latest line-openapi (git submodule update --remote), so this PR fails that check while the pinned submodule is behind. Pull in line/line-openapi@de8bd9e (Fix access_token description in revokeChannelToken, line/line-openapi#130), which replaces the closed auto-generated update PR #879. The resulting diff is doc comments only.
a9ae9ca to
55acb61
Compare
| $options = $this->createHttpClientOption(); | ||
| try { | ||
| $response = $this->client->send($request, $options); | ||
| } catch (RequestException $e) { |
There was a problem hiding this comment.
Why this catch structure changed: Guzzle 8 removed RequestException::getResponse(), which the previous code relied on. The response-carrying exceptions were moved to a new ResponseException subclass:
Guzzle 7 Guzzle 8
──────────────────────────────── ────────────────────────────────
TransferException TransferException
├── RequestException ├── RequestException
│ │ getResponse(): ?Response │ │ (getResponse() removed!)
│ └── BadResponseException │ └── ResponseException (new)
│ 4xx/5xx, response │ │ getResponse(): Response
│ guaranteed │ └── BadResponseException
└── ConnectException └── NetworkException (new)
└── ConnectException
There was a problem hiding this comment.
If you want the equivalent exception, BadResponseException is not the right one. You should still catch RequestException and check if it's an instance of ResponseException to see if you can get the response headers or body.
| if (!$exception instanceof BadResponseException) { | ||
| throw new ApiException( | ||
| "[{$exception->getCode()}] {$exception->getMessage()}", | ||
| (int) $exception->getCode(), | ||
| null, | ||
| null | ||
| ); | ||
| } |
There was a problem hiding this comment.
The original callback called $exception->getResponse() unconditionally, but rejections without a response (e.g. ConnectException, which has no getResponse()) made this crash with a fatal Error even on Guzzle 7 as a bug.
This branch converts such failures to a response-less ApiException, exactly as the sync path's catch (ConnectException) has always done.
| $options = $this->createHttpClientOption(); | ||
| try { | ||
| $response = $this->client->send($request, $options); | ||
| } catch (RequestException $e) { |
There was a problem hiding this comment.
If you want the equivalent exception, BadResponseException is not the right one. You should still catch RequestException and check if it's an instance of ResponseException to see if you can get the response headers or body.
Supersedes #876 (
guzzlehttp/guzzletov8).Its CI fails because
laravel/framework(via dev dependencyorchestra/testbench) still requires Guzzle^7.8.2, so pinning the root requirement to^8.0makescomposer installunresolvable.Changes
guzzlehttp/guzzleto^7.3 || ^8.0so consumers can use either major version, while dev dependencies (Laravel, still Guzzle 7 only) stay installable.api.pebble+ regenerated clientsUtils::jsonEncode()was removedjson_encode(..., JSON_THROW_ON_ERROR).RequestException::getResponse()was removed