diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79e1ca3..fdd061f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,10 +7,12 @@ on: jobs: PHPUnit: name: PHPUnit (PHP ${{ matrix.php }}) - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: php: + - 8.3 + - 8.2 - 8.1 - 8.0 - 7.4 @@ -23,7 +25,7 @@ jobs: - 5.4 - 5.3 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} @@ -37,14 +39,21 @@ jobs: PHPUnit-hhvm: name: PHPUnit (HHVM) - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 continue-on-error: true + services: + redis: + image: redis steps: - - uses: actions/checkout@v2 - - uses: azjezz/setup-hhvm@v1 + - uses: actions/checkout@v4 + - 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: composer self-update --2.2 # downgrade Composer for HHVM - - run: hhvm $(which composer) install - - run: docker run --net=host -d redis - - run: REDIS_URI=localhost:6379 hhvm vendor/bin/phpunit + args: hhvm composer.phar install + - name: Run REDIS_URI=redis:6379 hhvm vendor/bin/phpunit + uses: docker://hhvm/hhvm:3.30-lts-latest + with: + args: hhvm vendor/bin/phpunit + env: + REDIS_URI: redis:6379 diff --git a/CHANGELOG.md b/CHANGELOG.md index c5e8e66..4ef05f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 2.7.0 (2024-01-05) + +This is a compatibility release that contains backported features from the v3 branch. +Once v3 is released, it will be the way forward for this project. + +* Feature: Forward compatibility with Promise v3. + (#152 by @clue) + +* Feature: Full PHP 8.3 compatibility and update test suite. + (#151 by @clue) + ## 2.6.0 (2022-05-09) * Feature: Support PHP 8.1 release. diff --git a/README.md b/README.md index 5492572..c15fbee 100644 --- a/README.md +++ b/README.md @@ -610,7 +610,7 @@ This project follows [SemVer](https://semver.org/). This will install the latest supported version: ```bash -$ composer require clue/redis-react:^2.6 +$ composer require clue/redis-react:^2.7 ``` See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. diff --git a/composer.json b/composer.json index c1752cc..82dba29 100644 --- a/composer.json +++ b/composer.json @@ -15,18 +15,22 @@ "clue/redis-protocol": "0.3.*", "evenement/evenement": "^3.0 || ^2.0 || ^1.0", "react/event-loop": "^1.2", - "react/promise": "^2.0 || ^1.1", - "react/promise-timer": "^1.8", - "react/socket": "^1.9" + "react/promise": "^3 || ^2.0 || ^1.1", + "react/promise-timer": "^1.9", + "react/socket": "^1.12" }, "require-dev": { - "clue/block-react": "^1.1", - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + "clue/block-react": "^1.5", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "autoload": { - "psr-4": { "Clue\\React\\Redis\\": "src/" } + "psr-4": { + "Clue\\React\\Redis\\": "src/" + } }, "autoload-dev": { - "psr-4": { "Clue\\Tests\\React\\Redis\\": "tests/" } + "psr-4": { + "Clue\\Tests\\React\\Redis\\": "tests/" + } } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5093fa5..22e4b94 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- PHPUnit configuration file with new format for PHPUnit 9.3+ --> -<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" +<!-- PHPUnit configuration file with new format for PHPUnit 9.6+ --> +<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.6/phpunit.xsd" bootstrap="vendor/autoload.php" cacheResult="false" colors="true" @@ -17,4 +17,7 @@ <directory>./src/</directory> </include> </coverage> + <php> + <ini name="error_reporting" value="-1" /> + </php> </phpunit> diff --git a/phpunit.xml.legacy b/phpunit.xml.legacy index 8d93c4f..5e5303d 100644 --- a/phpunit.xml.legacy +++ b/phpunit.xml.legacy @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- PHPUnit configuration file with old format for PHPUnit 9.2 or older --> +<!-- PHPUnit configuration file with old format for legacy PHPUnit --> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/4.8/phpunit.xsd" bootstrap="vendor/autoload.php" @@ -15,4 +15,7 @@ <directory>./src/</directory> </whitelist> </filter> + <php> + <ini name="error_reporting" value="-1" /> + </php> </phpunit> diff --git a/src/Factory.php b/src/Factory.php index 4e94905..fca8288 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -27,7 +27,7 @@ class Factory * @param ?ConnectorInterface $connector * @param ?ProtocolFactory $protocol */ - public function __construct(LoopInterface $loop = null, ConnectorInterface $connector = null, ProtocolFactory $protocol = null) + public function __construct(?LoopInterface $loop = null, ?ConnectorInterface $connector = null, ?ProtocolFactory $protocol = null) { $this->loop = $loop ?: Loop::get(); $this->connector = $connector ?: new Connector(array(), $this->loop); @@ -84,6 +84,8 @@ public function createClient($uri) // either close successful connection or cancel pending connection attempt $connecting->then(function (ConnectionInterface $connection) { $connection->close(); + }, function () { + // ignore to avoid reporting unhandled rejection }); $connecting->cancel(); }); diff --git a/src/LazyClient.php b/src/LazyClient.php index d82b257..0e42cc8 100644 --- a/src/LazyClient.php +++ b/src/LazyClient.php @@ -168,6 +168,8 @@ public function close() if ($this->promise !== null) { $this->promise->then(function (Client $redis) { $redis->close(); + }, function () { + // ignore to avoid reporting unhandled rejection }); if ($this->promise !== null) { $this->promise->cancel(); diff --git a/src/StreamingClient.php b/src/StreamingClient.php index 8afd84d..7eacc5a 100644 --- a/src/StreamingClient.php +++ b/src/StreamingClient.php @@ -28,7 +28,7 @@ class StreamingClient extends EventEmitter implements Client private $subscribed = 0; private $psubscribed = 0; - public function __construct(DuplexStreamInterface $stream, ParserInterface $parser = null, SerializerInterface $serializer = null) + public function __construct(DuplexStreamInterface $stream, ?ParserInterface $parser = null, ?SerializerInterface $serializer = null) { if ($parser === null || $serializer === null) { $factory = new ProtocolFactory(); diff --git a/tests/FactoryLazyClientTest.php b/tests/FactoryLazyClientTest.php index 8b5005b..fb394d1 100644 --- a/tests/FactoryLazyClientTest.php +++ b/tests/FactoryLazyClientTest.php @@ -34,13 +34,13 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() public function testWillConnectWithDefaultPort() { - $this->connector->expects($this->never())->method('connect')->with('redis.example.com:6379')->willReturn(Promise\reject(new \RuntimeException())); + $this->connector->expects($this->never())->method('connect'); $this->factory->createLazyClient('redis.example.com'); } public function testWillConnectToLocalhost() { - $this->connector->expects($this->never())->method('connect')->with('localhost:1337')->willReturn(Promise\reject(new \RuntimeException())); + $this->connector->expects($this->never())->method('connect'); $this->factory->createLazyClient('localhost:1337'); } @@ -147,7 +147,7 @@ public function testWillWriteSelectCommandIfRedisUnixUriContainsDbQueryParameter public function testWillRejectIfConnectorRejects() { - $this->connector->expects($this->never())->method('connect')->with('127.0.0.1:2')->willReturn(Promise\reject(new \RuntimeException())); + $this->connector->expects($this->never())->method('connect'); $redis = $this->factory->createLazyClient('redis://127.0.0.1:2'); $this->assertInstanceOf('Clue\React\Redis\Client', $redis); diff --git a/tests/FactoryStreamingClientTest.php b/tests/FactoryStreamingClientTest.php index 882af76..6ce03a5 100644 --- a/tests/FactoryStreamingClientTest.php +++ b/tests/FactoryStreamingClientTest.php @@ -44,13 +44,17 @@ public function testCtor() public function testWillConnectWithDefaultPort() { $this->connector->expects($this->once())->method('connect')->with('redis.example.com:6379')->willReturn(Promise\reject(new \RuntimeException())); - $this->factory->createClient('redis.example.com'); + $promise = $this->factory->createClient('redis.example.com'); + + $promise->then(null, $this->expectCallableOnce()); // avoid reporting unhandled rejection } public function testWillConnectToLocalhost() { $this->connector->expects($this->once())->method('connect')->with('localhost:1337')->willReturn(Promise\reject(new \RuntimeException())); - $this->factory->createClient('localhost:1337'); + $promise = $this->factory->createClient('localhost:1337'); + + $promise->then(null, $this->expectCallableOnce()); // avoid reporting unhandled rejection } public function testWillResolveIfConnectorResolves() diff --git a/tests/LazyClientTest.php b/tests/LazyClientTest.php index 2ad644e..e1af1c6 100644 --- a/tests/LazyClientTest.php +++ b/tests/LazyClientTest.php @@ -148,7 +148,10 @@ public function testPingAfterPreviousFactoryRejectsUnderlyingClientWillCreateNew new Promise(function () { }) ); - $this->redis->ping(); + $promise = $this->redis->ping(); + + $promise->then(null, $this->expectCallableOnce()); // avoid reporting unhandled rejection + $deferred->reject($error); $this->redis->ping(); @@ -213,7 +216,7 @@ public function testPingAfterPingWillNotStartIdleTimerWhenFirstPingResolves() $this->redis->ping(); $this->redis->ping(); - $deferred->resolve(); + $deferred->resolve(null); } public function testPingAfterPingWillStartAndCancelIdleTimerWhenSecondPingStartsAfterFirstResolves() @@ -232,15 +235,15 @@ public function testPingAfterPingWillStartAndCancelIdleTimerWhenSecondPingStarts $this->loop->expects($this->once())->method('cancelTimer')->with($timer); $this->redis->ping(); - $deferred->resolve(); + $deferred->resolve(null); $this->redis->ping(); } public function testPingFollowedByIdleTimerWillCloseUnderlyingConnectionWithoutCloseEvent() { $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock(); - $client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve()); - $client->expects($this->once())->method('close')->willReturn(\React\Promise\resolve()); + $client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve(null)); + $client->expects($this->once())->method('close'); $this->factory->expects($this->once())->method('createClient')->willReturn(\React\Promise\resolve($client)); @@ -295,14 +298,17 @@ public function testCloseAfterPingWillEmitCloseWithoutErrorWhenUnderlyingClientC $this->redis->on('error', $this->expectCallableNever()); $this->redis->on('close', $this->expectCallableOnce()); - $this->redis->ping(); + $promise = $this->redis->ping(); + + $promise->then(null, $this->expectCallableOnce()); // avoid reporting unhandled rejection + $this->redis->close(); } public function testCloseAfterPingWillCloseUnderlyingClientConnectionWhenAlreadyResolved() { $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock(); - $client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve()); + $client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve(null)); $client->expects($this->once())->method('close'); $deferred = new Deferred(); @@ -327,7 +333,7 @@ public function testCloseAfterPingWillCancelIdleTimerWhenPingIsAlreadyResolved() $this->loop->expects($this->once())->method('cancelTimer')->with($timer); $this->redis->ping(); - $deferred->resolve(); + $deferred->resolve(null); $this->redis->close(); } @@ -404,7 +410,7 @@ public function testEmitsNoErrorEventWhenUnderlyingClientEmitsError() $error = new \RuntimeException(); $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock(); - $client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve()); + $client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve(null)); $deferred = new Deferred(); $this->factory->expects($this->once())->method('createClient')->willReturn($deferred->promise()); @@ -419,7 +425,7 @@ public function testEmitsNoErrorEventWhenUnderlyingClientEmitsError() public function testEmitsNoCloseEventWhenUnderlyingClientEmitsClose() { $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock(); - $client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve()); + $client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve(null)); $deferred = new Deferred(); $this->factory->expects($this->once())->method('createClient')->willReturn($deferred->promise()); @@ -453,7 +459,7 @@ public function testEmitsNoCloseEventButWillCancelIdleTimerWhenUnderlyingConnect $this->redis->on('close', $this->expectCallableNever()); $this->redis->ping(); - $deferred->resolve(); + $deferred->resolve(null); $this->assertTrue(is_callable($closeHandler)); $closeHandler(); @@ -463,7 +469,7 @@ public function testEmitsMessageEventWhenUnderlyingClientEmitsMessageForPubSubCh { $messageHandler = null; $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock(); - $client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve()); + $client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve(null)); $client->expects($this->any())->method('on')->willReturnCallback(function ($event, $callback) use (&$messageHandler) { if ($event === 'message') { $messageHandler = $callback; @@ -485,7 +491,7 @@ public function testEmitsUnsubscribeAndPunsubscribeEventsWhenUnderlyingClientClo { $allHandler = null; $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock(); - $client->expects($this->exactly(6))->method('__call')->willReturn(\React\Promise\resolve()); + $client->expects($this->exactly(6))->method('__call')->willReturn(\React\Promise\resolve(null)); $client->expects($this->any())->method('on')->willReturnCallback(function ($event, $callback) use (&$allHandler) { if (!isset($allHandler[$event])) { $allHandler[$event] = $callback;