diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb3a76ec..d2c89d7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,10 +7,11 @@ on: jobs: PHPUnit: name: PHPUnit (PHP ${{ matrix.php }}) - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: php: + - 8.2 - 8.1 - 8.0 - 7.4 @@ -22,11 +23,12 @@ jobs: - 5.5 - 5.4 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} coverage: xdebug + ini-file: development - run: composer install - run: vendor/bin/phpunit --coverage-text if: ${{ matrix.php >= 7.3 }} @@ -35,12 +37,16 @@ jobs: PHPUnit-hhvm: name: PHPUnit (HHVM) - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 continue-on-error: true steps: - - uses: actions/checkout@v2 - - uses: azjezz/setup-hhvm@v1 + - uses: actions/checkout@v3 + - run: cp "$(which composer)" composer.phar && ./composer.phar self-update --2.2 # downgrade Composer for HHVM + - name: Run hhvm composer.phar install + uses: docker://hhvm/hhvm:3.30-lts-latest with: - version: lts-3.30 - - run: hhvm $(which composer) install - - run: hhvm vendor/bin/phpunit + args: hhvm composer.phar install + - name: Run hhvm vendor/bin/phpunit + uses: docker://hhvm/hhvm:3.30-lts-latest + with: + args: hhvm vendor/bin/phpunit diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f785eb5..7825b339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ CHANGELOG for 2.x ================= +## 2.10.0 (2023-05-02) + +* Feature: Support Disjunctive Normal Form Types (DNF types) for PHP 8.2+. + (#237 by @nhedger) + + Feature: Add full support for PHP 8.2. + (#233 by @WyriHaximus and #241 by @clue) + +* Improve examples in documentation. + (#226 by @nhedger) + +* Improve test suite and project setup and report failed assertions. + (#215 and #217 by @SimonFrings and #241 by @clue) + * 2.9.0 (2022-02-11) * Feature: Support union types and address deprecation of `ReflectionType::getClass()` (PHP 8+). diff --git a/README.md b/README.md index d904a1d8..9449b920 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ Promise A lightweight implementation of [CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP. -[![CI status](https://github.com/reactphp/promise/workflows/CI/badge.svg?branch=2.x)](https://github.com/reactphp/promise/actions) +[![CI status](https://github.com/reactphp/promise/actions/workflows/ci.yml/badge.svg?branch=2.x)](https://github.com/reactphp/promise/actions) +[![installs on Packagist](https://img.shields.io/packagist/dt/react/promise?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/react/promise) Table of Contents ----------------- @@ -849,7 +850,7 @@ This project follows [SemVer](https://semver.org/). This will install the latest supported version: ```bash -$ composer require react/promise:^2.9 +composer require react/promise:^2.10 ``` See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. diff --git a/composer.json b/composer.json index f933f153..2a48ed1d 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.36" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5c44c780..42422782 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,11 +1,12 @@ - - + + convertDeprecationsToExceptions="true"> ./tests/ @@ -19,4 +20,12 @@ ./src/functions_include.php + + + + + + + + diff --git a/phpunit.xml.legacy b/phpunit.xml.legacy index 84ac9123..74ae9a12 100644 --- a/phpunit.xml.legacy +++ b/phpunit.xml.legacy @@ -1,6 +1,6 @@ - + + + + + + + + + diff --git a/src/functions.php b/src/functions.php index 429f0e73..2177dc23 100644 --- a/src/functions.php +++ b/src/functions.php @@ -354,7 +354,7 @@ function _checkTypehint(callable $callback, $object) // Extract the type of the argument and handle different possibilities $type = $expectedException->getType(); - + $isTypeUnion = true; $types = []; @@ -379,14 +379,18 @@ function _checkTypehint(callable $callback, $object) } foreach ($types as $type) { - if (!$type instanceof \ReflectionNamedType) { - throw new \LogicException('This implementation does not support groups of intersection or union types'); - } - - // A named-type can be either a class-name or a built-in type like string, int, array, etc. - $matches = ($type->isBuiltin() && \gettype($object) === $type->getName()) - || (new \ReflectionClass($type->getName()))->isInstance($object); + if ($type instanceof \ReflectionIntersectionType) { + foreach ($type->getTypes() as $typeToMatch) { + if (!($matches = ($typeToMatch->isBuiltin() && \gettype($object) === $typeToMatch->getName()) + || (new \ReflectionClass($typeToMatch->getName()))->isInstance($object))) { + break; + } + } + } else { + $matches = ($type->isBuiltin() && \gettype($object) === $type->getName()) + || (new \ReflectionClass($type->getName()))->isInstance($object); + } // If we look for a single match (union), we can return early on match // If we look for a full match (intersection), we can return early on mismatch diff --git a/tests/FunctionCheckTypehintTest.php b/tests/FunctionCheckTypehintTest.php index b8d3fbe7..ed923d50 100644 --- a/tests/FunctionCheckTypehintTest.php +++ b/tests/FunctionCheckTypehintTest.php @@ -117,6 +117,39 @@ public function shouldAcceptStaticClassCallbackWithIntersectionTypehint() self::assertTrue(_checkTypehint(['React\Promise\CallbackWithIntersectionTypehintClass', 'testCallbackStatic'], new CountableException())); } + /** + * @test + * @requires PHP 8.2 + */ + public function shouldAcceptInvokableObjectCallbackWithDNFTypehint() + { + self::assertFalse(_checkTypehint(new CallbackWithDNFTypehintClass(), new \RuntimeException())); + self::assertTrue(_checkTypehint(new CallbackWithDNFTypehintClass(), new ArrayAccessibleException())); + self::assertTrue(_checkTypehint(new CallbackWithDNFTypehintClass(), new CountableException())); + } + + /** + * @test + * @requires PHP 8.2 + */ + public function shouldAcceptObjectMethodCallbackWithDNFTypehint() + { + self::assertFalse(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new \RuntimeException())); + self::assertTrue(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new CountableException())); + self::assertTrue(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new ArrayAccessibleException())); + } + + /** + * @test + * @requires PHP 8.2 + */ + public function shouldAcceptStaticClassCallbackWithDNFTypehint() + { + self::assertFalse(_checkTypehint(['React\Promise\CallbackWithDNFTypehintClass', 'testCallbackStatic'], new \RuntimeException())); + self::assertTrue(_checkTypehint(['React\Promise\CallbackWithDNFTypehintClass', 'testCallbackStatic'], new CountableException())); + self::assertTrue(_checkTypehint(['React\Promise\CallbackWithDNFTypehintClass', 'testCallbackStatic'], new ArrayAccessibleException())); + } + /** @test */ public function shouldAcceptClosureCallbackWithoutTypehint() { diff --git a/tests/fixtures/ArrayAccessibleException.php b/tests/fixtures/ArrayAccessibleException.php new file mode 100644 index 00000000..71b7ad2a --- /dev/null +++ b/tests/fixtures/ArrayAccessibleException.php @@ -0,0 +1,22 @@ +