diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml deleted file mode 100644 index 85e49bf..0000000 --- a/.github/workflows/checks.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Checks - -on: - push: - branches: - - master - pull_request: - - roave-bc-check: - name: Roave BC Check - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Roave BC Check - uses: docker://nyholm/roave-bc-check-ga diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4cef58..ad3f5c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,8 @@ name: CI on: push: + branches: + - '*.x' pull_request: jobs: @@ -10,11 +12,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: ['7.1', '7.2', '7.3', '7.4', '8.0'] + php: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -23,15 +25,8 @@ jobs: tools: composer:v2 coverage: none - - name: Install PHP 7 dependencies + - name: Install dependencies run: composer update --prefer-dist --no-interaction --no-progress - if: "matrix.php != '8.0'" - - - name: Install PHP 8 dependencies - run: | - composer require "phpdocumentor/reflection-docblock:^5.2@dev" --no-interaction --no-update - composer update --prefer-dist --prefer-stable --no-interaction --no-progress --ignore-platform-req=php - if: "matrix.php == '8.0'" - name: Execute tests run: composer test @@ -41,11 +36,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: ['7.1', '7.2', '7.3', '7.4'] + php: ['7.1', '7.4', '8.0', '8.1', '8.2'] steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -68,7 +63,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 8257a56..143e55e 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -3,17 +3,20 @@ name: Static analysis on: push: branches: - - master + - '*.x' pull_request: jobs: phpstan: name: PHPStan runs-on: ubuntu-latest - + steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 + + - name: Remove phpspec + run: composer remove --dev friends-of-phpspec/phpspec-code-coverage phpspec/phpspec - name: PHPStan uses: docker://oskarstark/phpstan-ga @@ -26,9 +29,9 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: PHP-CS-Fixer uses: docker://oskarstark/php-cs-fixer-ga with: - args: --dry-run --diff-format udiff + args: --dry-run diff --git a/.gitignore b/.gitignore index 1029e28..8bca981 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -.php_cs -.php_cs.cache +.php-cs-fixer.php +.php-cs-fixer.cache /build/ /composer.lock /phpspec.yml diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..83809c2 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,16 @@ +in(__DIR__.'/src') + ->name('*.php') +; + +$config = (new PhpCsFixer\Config()) + ->setRiskyAllowed(true) + ->setRules([ + '@Symfony' => true, + ]) + ->setFinder($finder) +; + +return $config; diff --git a/.php_cs.dist b/.php_cs.dist deleted file mode 100644 index 770eee4..0000000 --- a/.php_cs.dist +++ /dev/null @@ -1,16 +0,0 @@ -setRiskyAllowed(true) - ->setRules([ - '@Symfony' => true, - 'array_syntax' => ['syntax' => 'short'], - ]) - ->setFinder( - PhpCsFixer\Finder::create() - ->in(__DIR__.'/src') - ->name('*.php') - ) -; - -return $config; diff --git a/.styleci.yml b/.styleci.yml deleted file mode 100644 index 5328b61..0000000 --- a/.styleci.yml +++ /dev/null @@ -1,14 +0,0 @@ -preset: symfony - -finder: - exclude: - - "spec" - path: - - "src" - - "tests" - -enabled: - - short_array_syntax - -disabled: - - phpdoc_annotation_without_dot # This is still buggy: https://github.com/symfony/symfony/pull/19198 diff --git a/CHANGELOG.md b/CHANGELOG.md index 29b03e0..4c3d863 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [2.4.1] - 2024-09-23 -## [Unreleased] +- Updated code to not raise warnings for nullable parameters in PHP 8.4. + +## [2.4.0] - 2023-04-14 + +### Changed + +- Allow `psr/http-message` v2 in addition to v1 +- Deprecate `Http\Client\HttpClient`, use [PSR-18](https://www.php-fig.org/psr/psr-18/) instead + +## [2.3.0] - 2022-02-21 + +### Changed + +- Enabled the `$onRejected` callback of `HttpRejectedPromise` to return a promise for implementing a retry + mechanism [#168](https://github.com/php-http/httplug/pull/168) ## [2.2.0] - 2020-07-13 diff --git a/README.md b/README.md index aabcf25..a9b476a 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,11 @@ [![Latest Version](https://img.shields.io/github/release/php-http/httplug.svg?style=flat-square)](https://github.com/php-http/httplug/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) -[![Build Status](https://img.shields.io/travis/php-http/httplug/master.svg?style=flat-square)](https://travis-ci.org/php-http/httplug) +[![Build Status](https://github.com/php-http/httplug/actions/workflows/ci.yml/badge.svg)](https://github.com/php-http/httplug/actions/workflows/ci.yml) [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/httplug.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/httplug) [![Quality Score](https://img.shields.io/scrutinizer/g/php-http/httplug.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/httplug) [![Total Downloads](https://img.shields.io/packagist/dt/php-http/httplug.svg?style=flat-square)](https://packagist.org/packages/php-http/httplug) -[![Slack Status](http://slack.httplug.io/badge.svg)](http://slack.httplug.io) [![Email](https://img.shields.io/badge/email-team@httplug.io-blue.svg?style=flat-square)](mailto:team@httplug.io) **HTTPlug, the HTTP client abstraction for PHP.** @@ -16,19 +15,11 @@ ## Intro HTTP client standard built on [PSR-7](http://www.php-fig.org/psr/psr-7/) HTTP -messages. The HTTPlug client interface is compatible with the official standard -for the HTTP client interface, [PSR-18](http://www.php-fig.org/psr/psr-18/). -HTTPlug adds an interface for asynchronous HTTP requests, which PSR-18 does not -cover. - -Since HTTPlug has already been widely adopted and a whole ecosystem has been -built around it, we will keep maintaining this package for the time being. -HTTPlug 2.0 and newer extend the PSR-18 interface to allow for a convenient -migration path. - -New client implementations and consumers should use the PSR-18 interfaces -directly. In the long term, we expect PSR-18 to completely replace the need -for HTTPlug. +messages. The HttpAsyncClient defines an asynchronous HTTP client for PHP. + +This package also provides a synchronous HttpClient interface with the same +method signature as the [PSR-18](http://www.php-fig.org/psr/psr-18/) client. +For synchronous requests, we recommend using PSR-18 directly. ## History diff --git a/composer.json b/composer.json index 268b27e..7391770 100644 --- a/composer.json +++ b/composer.json @@ -22,16 +22,11 @@ "php": "^7.1 || ^8.0", "php-http/promise": "^1.1", "psr/http-client": "^1.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.1", - "phpspec/phpspec": "^5.1 || ^6.0" - }, - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } + "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0", + "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0" }, "autoload": { "psr-4": { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d45d0a4..c1629a6 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,7 +1,7 @@ parameters: ignoreErrors: - - message: "#^Method Http\\\\Client\\\\Exception\\\\HttpException\\:\\:create\\(\\) has no return typehint specified\\.$#" + message: "#^Method Http\\\\Client\\\\Exception\\\\HttpException\\:\\:create\\(\\) has no return type specified\\.$#" count: 1 path: src/Exception/HttpException.php @@ -11,12 +11,11 @@ parameters: path: src/Exception/HttpException.php - - message: "#^Method Http\\\\Client\\\\Exception\\\\NetworkException\\:\\:setRequest\\(\\) has no return typehint specified\\.$#" + message: "#^Method Http\\\\Client\\\\Exception\\\\NetworkException\\:\\:setRequest\\(\\) has no return type specified\\.$#" count: 1 path: src/Exception/NetworkException.php - - message: "#^Method Http\\\\Client\\\\Exception\\\\RequestException\\:\\:setRequest\\(\\) has no return typehint specified\\.$#" + message: "#^Method Http\\\\Client\\\\Exception\\\\RequestException\\:\\:setRequest\\(\\) has no return type specified\\.$#" count: 1 path: src/Exception/RequestException.php - diff --git a/spec/Exception/HttpExceptionSpec.php b/spec/Exception/HttpExceptionSpec.php index 3658f56..20eb58e 100644 --- a/spec/Exception/HttpExceptionSpec.php +++ b/spec/Exception/HttpExceptionSpec.php @@ -10,6 +10,8 @@ class HttpExceptionSpec extends ObjectBehavior { function let(RequestInterface $request, ResponseInterface $response) { + $response->getStatusCode()->willReturn(500); + $this->beConstructedWith('message', $request, $response); } diff --git a/spec/Promise/HttpRejectedPromiseSpec.php b/spec/Promise/HttpRejectedPromiseSpec.php index f46dcc5..5bc3f82 100644 --- a/spec/Promise/HttpRejectedPromiseSpec.php +++ b/spec/Promise/HttpRejectedPromiseSpec.php @@ -4,6 +4,7 @@ use Http\Client\Exception; use Http\Client\Exception\TransferException; +use Http\Client\Promise\HttpFulfilledPromise; use Http\Promise\Promise; use PhpSpec\ObjectBehavior; use Prophecy\Argument; @@ -87,4 +88,19 @@ function it_throws_not_http_exception() throw new \Exception(); }); } + + function it_returns_a_promise_when_rejected(ResponseInterface $response) + { + $exception = new TransferException(); + $this->beConstructedWith($exception); + + $promise = $this->then(null, function (Exception $exceptionReceived) use($exception, $response) { + return new HttpFulfilledPromise($response->getWrappedObject()); + }); + + $promise->shouldHaveType('Http\Promise\Promise'); + $promise->shouldHaveType('Http\Client\Promise\HttpFulfilledPromise'); + $promise->getState()->shouldReturn(Promise::FULFILLED); + $promise->wait()->shouldReturnAnInstanceOf('Psr\Http\Message\ResponseInterface'); + } } diff --git a/src/Exception/HttpException.php b/src/Exception/HttpException.php index 6c2a007..8af32f1 100644 --- a/src/Exception/HttpException.php +++ b/src/Exception/HttpException.php @@ -26,7 +26,7 @@ public function __construct( $message, RequestInterface $request, ResponseInterface $response, - \Exception $previous = null + ?\Exception $previous = null ) { parent::__construct($message, $request, $previous); @@ -50,7 +50,7 @@ public function getResponse() public static function create( RequestInterface $request, ResponseInterface $response, - \Exception $previous = null + ?\Exception $previous = null ) { $message = sprintf( '[url] %s [http method] %s [status code] %s [reason phrase] %s', diff --git a/src/Exception/NetworkException.php b/src/Exception/NetworkException.php index 9b4f1e8..ce5f4d7 100644 --- a/src/Exception/NetworkException.php +++ b/src/Exception/NetworkException.php @@ -19,7 +19,7 @@ class NetworkException extends TransferException implements PsrNetworkException /** * @param string $message */ - public function __construct($message, RequestInterface $request, \Exception $previous = null) + public function __construct($message, RequestInterface $request, ?\Exception $previous = null) { $this->setRequest($request); diff --git a/src/Exception/RequestAwareTrait.php b/src/Exception/RequestAwareTrait.php index 71b4bb8..f507982 100644 --- a/src/Exception/RequestAwareTrait.php +++ b/src/Exception/RequestAwareTrait.php @@ -16,9 +16,6 @@ private function setRequest(RequestInterface $request) $this->request = $request; } - /** - * {@inheritdoc} - */ public function getRequest(): RequestInterface { return $this->request; diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index f6c60ce..dbed296 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -20,7 +20,7 @@ class RequestException extends TransferException implements PsrRequestException /** * @param string $message */ - public function __construct($message, RequestInterface $request, \Exception $previous = null) + public function __construct($message, RequestInterface $request, ?\Exception $previous = null) { $this->setRequest($request); diff --git a/src/HttpClient.php b/src/HttpClient.php index 4442bd0..22b94aa 100644 --- a/src/HttpClient.php +++ b/src/HttpClient.php @@ -9,6 +9,8 @@ * * Provide the Httplug HttpClient interface for BC. * You should typehint Psr\Http\Client\ClientInterface in new code + * + * @deprecated since version 2.4, use Psr\Http\Client\ClientInterface instead; see https://www.php-fig.org/psr/psr-18/ */ interface HttpClient extends ClientInterface { diff --git a/src/Promise/HttpFulfilledPromise.php b/src/Promise/HttpFulfilledPromise.php index 1ad32cd..ccdf48e 100644 --- a/src/Promise/HttpFulfilledPromise.php +++ b/src/Promise/HttpFulfilledPromise.php @@ -18,10 +18,7 @@ public function __construct(ResponseInterface $response) $this->response = $response; } - /** - * {@inheritdoc} - */ - public function then(callable $onFulfilled = null, callable $onRejected = null) + public function then(?callable $onFulfilled = null, ?callable $onRejected = null) { if (null === $onFulfilled) { return $this; @@ -34,17 +31,11 @@ public function then(callable $onFulfilled = null, callable $onRejected = null) } } - /** - * {@inheritdoc} - */ public function getState() { return Promise::FULFILLED; } - /** - * {@inheritdoc} - */ public function wait($unwrap = true) { if ($unwrap) { diff --git a/src/Promise/HttpRejectedPromise.php b/src/Promise/HttpRejectedPromise.php index 8af97de..a489ad4 100644 --- a/src/Promise/HttpRejectedPromise.php +++ b/src/Promise/HttpRejectedPromise.php @@ -17,33 +17,29 @@ public function __construct(Exception $exception) $this->exception = $exception; } - /** - * {@inheritdoc} - */ - public function then(callable $onFulfilled = null, callable $onRejected = null) + public function then(?callable $onFulfilled = null, ?callable $onRejected = null) { if (null === $onRejected) { return $this; } try { - return new HttpFulfilledPromise($onRejected($this->exception)); + $result = $onRejected($this->exception); + if ($result instanceof Promise) { + return $result; + } + + return new HttpFulfilledPromise($result); } catch (Exception $e) { return new self($e); } } - /** - * {@inheritdoc} - */ public function getState() { return Promise::REJECTED; } - /** - * {@inheritdoc} - */ public function wait($unwrap = true) { if ($unwrap) {