├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── phpstan.neon.dist ├── psalm.baseline.xml ├── psalm.xml └── src ├── Client.php ├── Exception └── UnexpectedValueException.php └── Promise.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 7 | 8 | ## [1.1.0] - 2024-11-26 9 | 10 | ### Changed 11 | 12 | - Add support for PHP 8.4, drop support for PHP version 7.2 13 | 14 | ### Fixed 15 | 16 | - Add missing `composer test` command and use it in CI 17 | - Fix deprecated usage of prophesize in phpunit tests 18 | 19 | ## [1.0.0] - 2021-03-09 20 | 21 | - Stable release - no changes since 0.1.1 22 | 23 | ## [0.1.1] - 2020-10-21 24 | 25 | * Allow installation with PHP 8 26 | 27 | ## [0.1.0] - 2020-08-16 28 | 29 | First release 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 PHP HTTP Team 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Guzzle 7 HTTP Adapter 2 | 3 | [![Latest Version](https://img.shields.io/github/release/php-http/guzzle7-adapter.svg?style=flat-square)](https://github.com/php-http/guzzle7-adapter/releases) 4 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) 5 | [![Total Downloads](https://img.shields.io/packagist/dt/php-http/guzzle7-adapter.svg?style=flat-square)](https://packagist.org/packages/php-http/guzzle7-adapter) 6 | 7 | **Guzzle 7 HTTP Adapter.** 8 | 9 | ## Install 10 | 11 | Via Composer 12 | 13 | ``` bash 14 | $ composer require php-http/guzzle7-adapter 15 | ``` 16 | 17 | ## Documentation 18 | 19 | Please see the [official documentation](http://docs.php-http.org/en/latest/clients/guzzle7-adapter.html). 20 | 21 | ## Testing 22 | 23 | First launch the http server: 24 | 25 | ```bash 26 | $ ./vendor/bin/http_test_server > /dev/null 2>&1 & 27 | ``` 28 | 29 | Then the test suite: 30 | 31 | ``` bash 32 | $ composer test 33 | ``` 34 | 35 | ## Contributing 36 | 37 | Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html). 38 | 39 | ## Security 40 | 41 | If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org). 42 | 43 | ## License 44 | 45 | The MIT License (MIT). Please see [License File](LICENSE) for more information. 46 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-http/guzzle7-adapter", 3 | "description": "Guzzle 7 HTTP Adapter", 4 | "license": "MIT", 5 | "keywords": ["guzzle", "http"], 6 | "homepage": "http://httplug.io", 7 | "authors": [ 8 | { 9 | "name": "Tobias Nyholm", 10 | "email": "tobias.nyholm@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": "^7.3 | ^8.0", 15 | "php-http/httplug": "^2.0", 16 | "psr/http-client": "^1.0", 17 | "guzzlehttp/guzzle": "^7.0" 18 | }, 19 | "require-dev": { 20 | "phpunit/phpunit": "^8.0|^9.3", 21 | "php-http/client-integration-tests": "^3.0", 22 | "phpspec/prophecy-phpunit": "^2.0", 23 | "php-http/message-factory": "^1.1" 24 | }, 25 | "provide": { 26 | "php-http/client-implementation": "1.0", 27 | "php-http/async-client-implementation": "1.0", 28 | "psr/http-client-implementation": "1.0" 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "Http\\Adapter\\Guzzle7\\": "src/" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "Http\\Adapter\\Guzzle7\\Tests\\": "tests/" 38 | } 39 | }, 40 | "scripts": { 41 | "test": "@php vendor/bin/phpunit" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 5 3 | reportUnmatchedIgnoredErrors: false 4 | paths: 5 | - src 6 | -------------------------------------------------------------------------------- /psalm.baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $exception->getResponse() 6 | 7 | 8 | 9 | 10 | createWithConfig 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | final class Client implements HttpClient, HttpAsyncClient 23 | { 24 | /** 25 | * @var ClientInterface 26 | */ 27 | private $guzzle; 28 | 29 | public function __construct(?ClientInterface $guzzle = null) 30 | { 31 | if (!$guzzle) { 32 | $guzzle = self::buildClient(); 33 | } 34 | 35 | $this->guzzle = $guzzle; 36 | } 37 | 38 | /** 39 | * Factory method to create the Guzzle 7 adapter with custom Guzzle configuration. 40 | */ 41 | public static function createWithConfig(array $config): Client 42 | { 43 | return new self(self::buildClient($config)); 44 | } 45 | 46 | public function sendRequest(RequestInterface $request): ResponseInterface 47 | { 48 | return $this->sendAsyncRequest($request)->wait(); 49 | } 50 | 51 | public function sendAsyncRequest(RequestInterface $request) 52 | { 53 | $promise = $this->guzzle->sendAsync($request); 54 | 55 | return new Promise($promise, $request); 56 | } 57 | 58 | /** 59 | * Build the Guzzle client instance. 60 | */ 61 | private static function buildClient(array $config = []): GuzzleClient 62 | { 63 | $handlerStack = new HandlerStack(Utils::chooseHandler()); 64 | $handlerStack->push(Middleware::prepareBody(), 'prepare_body'); 65 | $config = array_merge(['handler' => $handlerStack], $config); 66 | 67 | return new GuzzleClient($config); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Exception/UnexpectedValueException.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | final class Promise implements HttpPromise 21 | { 22 | /** 23 | * @var PromiseInterface 24 | */ 25 | private $promise; 26 | 27 | /** 28 | * @var string State of the promise 29 | */ 30 | private $state; 31 | 32 | /** 33 | * @var ResponseInterface 34 | */ 35 | private $response; 36 | 37 | /** 38 | * @var HttplugException 39 | */ 40 | private $exception; 41 | 42 | /** 43 | * @var RequestInterface 44 | */ 45 | private $request; 46 | 47 | public function __construct(PromiseInterface $promise, RequestInterface $request) 48 | { 49 | $this->request = $request; 50 | $this->state = self::PENDING; 51 | $this->promise = $promise->then(function ($response) { 52 | $this->response = $response; 53 | $this->state = self::FULFILLED; 54 | 55 | return $response; 56 | }, function ($reason) { 57 | $this->state = self::REJECTED; 58 | 59 | if ($reason instanceof HttplugException) { 60 | $this->exception = $reason; 61 | } elseif ($reason instanceof GuzzleExceptions\GuzzleException) { 62 | $this->exception = $this->handleException($reason); 63 | } elseif ($reason instanceof \Throwable) { 64 | $this->exception = new HttplugException\TransferException('Invalid exception returned from Guzzle7', 0, $reason); 65 | } else { 66 | $this->exception = new UnexpectedValueException('Reason returned from Guzzle7 must be an Exception'); 67 | } 68 | 69 | throw $this->exception; 70 | }); 71 | } 72 | 73 | public function then(?callable $onFulfilled = null, ?callable $onRejected = null) 74 | { 75 | return new static($this->promise->then($onFulfilled, $onRejected), $this->request); 76 | } 77 | 78 | public function getState() 79 | { 80 | return $this->state; 81 | } 82 | 83 | public function wait($unwrap = true) 84 | { 85 | $this->promise->wait(false); 86 | 87 | if ($unwrap) { 88 | if (self::REJECTED === $this->getState()) { 89 | throw $this->exception; 90 | } 91 | 92 | return $this->response; 93 | } 94 | } 95 | 96 | /** 97 | * Converts a Guzzle exception into an Httplug exception. 98 | * 99 | * @return HttplugException 100 | */ 101 | private function handleException(GuzzleExceptions\GuzzleException $exception) 102 | { 103 | if ($exception instanceof GuzzleExceptions\ConnectException) { 104 | return new HttplugException\NetworkException($exception->getMessage(), $exception->getRequest(), $exception); 105 | } 106 | 107 | if ($exception instanceof GuzzleExceptions\RequestException) { 108 | // Make sure we have a response for the HttpException 109 | if ($exception->hasResponse()) { 110 | return new HttplugException\HttpException( 111 | $exception->getMessage(), 112 | $exception->getRequest(), 113 | $exception->getResponse(), 114 | $exception 115 | ); 116 | } 117 | 118 | return new HttplugException\RequestException($exception->getMessage(), $exception->getRequest(), $exception); 119 | } 120 | 121 | return new HttplugException\TransferException($exception->getMessage(), 0, $exception); 122 | } 123 | } 124 | --------------------------------------------------------------------------------