diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 465821a..0b1b36b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,11 +17,6 @@ jobs: - 7.3 - 7.2 - 7.1 - - 7.0 - - 5.6 - - 5.5 - - 5.4 - - 5.3 steps: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 diff --git a/README.md b/README.md index 13ed842..92b57f0 100644 --- a/README.md +++ b/README.md @@ -75,8 +75,8 @@ Once the promise is fulfilled, this function will return whatever the promise resolved to. Once the promise is rejected, this will throw whatever the promise rejected -with. If the promise did not reject with an `Exception` or `Throwable` (PHP 7+), -then this function will throw an `UnexpectedValueException` instead. +with. If the promise did not reject with an `Exception` or `Throwable`, then +this function will throw an `UnexpectedValueException` instead. ```php try { @@ -222,9 +222,15 @@ $ composer require react/async:dev-main See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. This project aims to run on any platform and thus does not require any PHP -extensions and supports running on legacy PHP 5.3 through current PHP 8+. +extensions and supports running on PHP 7.1 through current PHP 8+. It's *highly recommended to use the latest supported PHP version* for this project. +We're committed to providing long-term support (LTS) options and to provide a +smooth upgrade path. If you're using an older PHP version, you may use the +[`2.x` branch](https://github.com/reactphp/async/tree/2.x) which provides a +compatible API but does not take advantage of newer language features. You may +target both versions at the same time to support a wider range of PHP versions. + ## Tests To run the test suite, you first need to clone this repo and then install all diff --git a/composer.json b/composer.json index 609b5f5..a839932 100644 --- a/composer.json +++ b/composer.json @@ -26,12 +26,12 @@ } ], "require": { - "php": ">=5.3.2", + "php": ">=7.1", "react/event-loop": "^1.2", "react/promise": "^2.8 || ^1.2.1" }, "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.3 || ^7.5" }, "autoload": { "files": [ diff --git a/phpunit.xml.legacy b/phpunit.xml.legacy index fbb43e8..2157b25 100644 --- a/phpunit.xml.legacy +++ b/phpunit.xml.legacy @@ -2,7 +2,7 @@ diff --git a/src/functions.php b/src/functions.php index 0f2ef7e..7f644e1 100644 --- a/src/functions.php +++ b/src/functions.php @@ -28,8 +28,8 @@ * resolved to. * * Once the promise is rejected, this will throw whatever the promise rejected - * with. If the promise did not reject with an `Exception` or `Throwable` (PHP 7+), - * then this function will throw an `UnexpectedValueException` instead. + * with. If the promise did not reject with an `Exception` or `Throwable`, then + * this function will throw an `UnexpectedValueException` instead. * * ```php * try { @@ -45,7 +45,7 @@ * @param PromiseInterface $promise * @return mixed returns whatever the promise resolves to * @throws \Exception when the promise is rejected with an `Exception` - * @throws \Throwable when the promise is rejected with a `Throwable` (PHP 7+) + * @throws \Throwable when the promise is rejected with a `Throwable` * @throws \UnexpectedValueException when the promise is rejected with an unexpected value (Promise API v1 or v2 only) */ function await(PromiseInterface $promise) @@ -79,7 +79,7 @@ function ($error) use (&$exception, &$rejected, &$wait) { if ($rejected) { // promise is rejected with an unexpected value (Promise API v1 or v2 only) - if (!$exception instanceof \Exception && !$exception instanceof \Throwable) { + if (!$exception instanceof \Throwable) { $exception = new \UnexpectedValueException( 'Promise rejected with unexpected value of type ' . (is_object($exception) ? get_class($exception) : gettype($exception)) ); @@ -95,18 +95,18 @@ function ($error) use (&$exception, &$rejected, &$wait) { * @param array> $tasks * @return PromiseInterface,Exception> */ -function parallel(array $tasks) +function parallel(array $tasks): PromiseInterface { - $pending = array(); + $pending = []; $deferred = new Deferred(function () use (&$pending) { foreach ($pending as $promise) { if ($promise instanceof CancellablePromiseInterface) { $promise->cancel(); } } - $pending = array(); + $pending = []; }); - $results = array(); + $results = []; $errored = false; $numTasks = count($tasks); @@ -123,7 +123,7 @@ function parallel(array $tasks) $promise->cancel(); } } - $pending = array(); + $pending = []; }; foreach ($tasks as $i => $task) { @@ -153,7 +153,7 @@ function parallel(array $tasks) * @param array> $tasks * @return PromiseInterface,Exception> */ -function series(array $tasks) +function series(array $tasks): PromiseInterface { $pending = null; $deferred = new Deferred(function () use (&$pending) { @@ -162,7 +162,7 @@ function series(array $tasks) } $pending = null; }); - $results = array(); + $results = []; /** @var callable():void $next */ $taskCallback = function ($result) use (&$results, &$next) { @@ -193,7 +193,7 @@ function series(array $tasks) * @param array> $tasks * @return PromiseInterface */ -function waterfall(array $tasks) +function waterfall(array $tasks): PromiseInterface { $pending = null; $deferred = new Deferred(function () use (&$pending) { diff --git a/tests/AwaitTest.php b/tests/AwaitTest.php index 1acc7e3..95a8b5f 100644 --- a/tests/AwaitTest.php +++ b/tests/AwaitTest.php @@ -14,7 +14,8 @@ public function testAwaitThrowsExceptionWhenPromiseIsRejectedWithException() throw new \Exception('test'); }); - $this->setExpectedException('Exception', 'test'); + $this->expectException(\Exception::class); + $this->expectExceptionMessage('test'); React\Async\await($promise); } @@ -28,7 +29,8 @@ public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWith $reject(false); }); - $this->setExpectedException('UnexpectedValueException', 'Promise rejected with unexpected value of type bool'); + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessage('Promise rejected with unexpected value of type bool'); React\Async\await($promise); } @@ -42,20 +44,20 @@ public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWith $reject(null); }); - $this->setExpectedException('UnexpectedValueException', 'Promise rejected with unexpected value of type NULL'); + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessage('Promise rejected with unexpected value of type NULL'); React\Async\await($promise); } - /** - * @requires PHP 7 - */ public function testAwaitThrowsErrorWhenPromiseIsRejectedWithError() { $promise = new Promise(function ($_, $reject) { throw new \Error('Test', 42); }); - $this->setExpectedException('Error', 'Test', 42); + $this->expectException(\Error::class); + $this->expectExceptionMessage('Test'); + $this->expectExceptionCode(42); React\Async\await($promise); } @@ -84,8 +86,8 @@ public function testAwaitReturnsValueWhenPromiseIsFulfilledEvenWhenOtherTimerSto public function testAwaitShouldNotCreateAnyGarbageReferencesForResolvedPromise() { - if (class_exists('React\Promise\When') && PHP_VERSION_ID >= 50400) { - $this->markTestSkipped('Not supported on legacy Promise v1 API with PHP 5.4+'); + if (class_exists('React\Promise\When')) { + $this->markTestSkipped('Not supported on legacy Promise v1 API'); } gc_collect_cycles(); @@ -126,8 +128,8 @@ public function testAwaitShouldNotCreateAnyGarbageReferencesForPromiseRejectedWi $this->markTestSkipped('Promises must be rejected with a \Throwable instance since Promise v3'); } - if (class_exists('React\Promise\When') && PHP_VERSION_ID >= 50400) { - $this->markTestSkipped('Not supported on legacy Promise v1 API with PHP 5.4+'); + if (class_exists('React\Promise\When')) { + $this->markTestSkipped('Not supported on legacy Promise v1 API'); } gc_collect_cycles(); @@ -144,21 +146,4 @@ public function testAwaitShouldNotCreateAnyGarbageReferencesForPromiseRejectedWi $this->assertEquals(0, gc_collect_cycles()); } - - public function setExpectedException($exception, $exceptionMessage = '', $exceptionCode = null) - { - if (method_exists($this, 'expectException')) { - // PHPUnit 5+ - $this->expectException($exception); - if ($exceptionMessage !== '') { - $this->expectExceptionMessage($exceptionMessage); - } - if ($exceptionCode !== null) { - $this->expectExceptionCode($exceptionCode); - } - } else { - // legacy PHPUnit 4 - parent::setExpectedException($exception, $exceptionMessage, $exceptionCode); - } - } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 48f2879..ee0f476 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,6 +2,7 @@ namespace React\Tests\Async; +use PHPUnit\Framework\MockObject\MockBuilder; use PHPUnit\Framework\TestCase as BaseTestCase; class TestCase extends BaseTestCase @@ -39,12 +40,12 @@ protected function expectCallableNever() protected function createCallableMock() { - if (method_exists('PHPUnit\Framework\MockObject\MockBuilder', 'addMethods')) { + if (method_exists(MockBuilder::class, 'addMethods')) { // PHPUnit 9+ - return $this->getMockBuilder('stdClass')->addMethods(array('__invoke'))->getMock(); + return $this->getMockBuilder(\stdClass::class)->addMethods(['__invoke'])->getMock(); } else { - // legacy PHPUnit 4 - PHPUnit 8 - return $this->getMockBuilder('stdClass')->setMethods(array('__invoke'))->getMock(); + // PHPUnit < 9 + return $this->getMockBuilder(\stdClass::class)->setMethods(['__invoke'])->getMock(); } } }