From 9f16e24c0d464c86e0cbb7664d6344ef5a7ebcdf Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Wed, 22 May 2024 08:16:50 +0200 Subject: [PATCH 1/7] [1.x] Improve PHP 8.4+ support by avoiding implicitly nullable types This changeset backports #223 from `3.x` to `1.x` to improve PHP 8.4+ support by avoiding implicitly nullable types as discussed in https://github.com/reactphp/promise/pull/260. The same idea applies, but v1 requires manual type checks to support legacy PHP versions as the nullable type syntax requires PHP 7.1+ otherwise. Builds on top of #223, #204 and #218 --- composer.json | 6 +++--- src/Query/TcpTransportExecutor.php | 6 +++++- src/Query/TimeoutExecutor.php | 11 +++++++++- src/Query/UdpTransportExecutor.php | 6 +++++- src/Resolver/Factory.php | 16 +++++++++++++-- tests/Query/TcpTransportExecutorTest.php | 7 +++++++ tests/Query/TimeoutExecutorTest.php | 7 +++++++ tests/Query/UdpTransportExecutorTest.php | 7 +++++++ tests/Resolver/FactoryTest.php | 26 ++++++++++++++++++++++++ 9 files changed, 84 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index de22b26f..4fe5c0da 100644 --- a/composer.json +++ b/composer.json @@ -29,12 +29,12 @@ "php": ">=5.3.0", "react/cache": "^1.0 || ^0.6 || ^0.5", "react/event-loop": "^1.2", - "react/promise": "^3.0 || ^2.7 || ^1.2.1" + "react/promise": "^3.2 || ^2.7 || ^1.2.1" }, "require-dev": { "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4 || ^3 || ^2", - "react/promise-timer": "^1.9" + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" }, "autoload": { "psr-4": { diff --git a/src/Query/TcpTransportExecutor.php b/src/Query/TcpTransportExecutor.php index bfaedbae..669fd012 100644 --- a/src/Query/TcpTransportExecutor.php +++ b/src/Query/TcpTransportExecutor.php @@ -134,7 +134,7 @@ class TcpTransportExecutor implements ExecutorInterface * @param string $nameserver * @param ?LoopInterface $loop */ - public function __construct($nameserver, LoopInterface $loop = null) + public function __construct($nameserver, $loop = null) { if (\strpos($nameserver, '[') === false && \substr_count($nameserver, ':') >= 2 && \strpos($nameserver, '://') === false) { // several colons, but not enclosed in square brackets => enclose IPv6 address in square brackets @@ -146,6 +146,10 @@ public function __construct($nameserver, LoopInterface $loop = null) throw new \InvalidArgumentException('Invalid nameserver address given'); } + if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + } + $this->nameserver = 'tcp://' . $parts['host'] . ':' . (isset($parts['port']) ? $parts['port'] : 53); $this->loop = $loop ?: Loop::get(); $this->parser = new Parser(); diff --git a/src/Query/TimeoutExecutor.php b/src/Query/TimeoutExecutor.php index 06c51b15..b61fea6d 100644 --- a/src/Query/TimeoutExecutor.php +++ b/src/Query/TimeoutExecutor.php @@ -12,8 +12,17 @@ final class TimeoutExecutor implements ExecutorInterface private $loop; private $timeout; - public function __construct(ExecutorInterface $executor, $timeout, LoopInterface $loop = null) + /** + * @param ExecutorInterface $executor + * @param float $timeout + * @param ?LoopInterface $loop + */ + public function __construct(ExecutorInterface $executor, $timeout, $loop = null) { + if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #3 ($loop) expected null|React\EventLoop\LoopInterface'); + } + $this->executor = $executor; $this->loop = $loop ?: Loop::get(); $this->timeout = $timeout; diff --git a/src/Query/UdpTransportExecutor.php b/src/Query/UdpTransportExecutor.php index 30a3d705..a8cbfafa 100644 --- a/src/Query/UdpTransportExecutor.php +++ b/src/Query/UdpTransportExecutor.php @@ -98,7 +98,7 @@ final class UdpTransportExecutor implements ExecutorInterface * @param string $nameserver * @param ?LoopInterface $loop */ - public function __construct($nameserver, LoopInterface $loop = null) + public function __construct($nameserver, $loop = null) { if (\strpos($nameserver, '[') === false && \substr_count($nameserver, ':') >= 2 && \strpos($nameserver, '://') === false) { // several colons, but not enclosed in square brackets => enclose IPv6 address in square brackets @@ -110,6 +110,10 @@ public function __construct($nameserver, LoopInterface $loop = null) throw new \InvalidArgumentException('Invalid nameserver address given'); } + if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + } + $this->nameserver = 'udp://' . $parts['host'] . ':' . (isset($parts['port']) ? $parts['port'] : 53); $this->loop = $loop ?: Loop::get(); $this->parser = new Parser(); diff --git a/src/Resolver/Factory.php b/src/Resolver/Factory.php index 5fe608cb..52658951 100644 --- a/src/Resolver/Factory.php +++ b/src/Resolver/Factory.php @@ -36,8 +36,12 @@ final class Factory * @throws \InvalidArgumentException for invalid DNS server address * @throws \UnderflowException when given DNS Config object has an empty list of nameservers */ - public function create($config, LoopInterface $loop = null) + public function create($config, $loop = null) { + if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + } + $executor = $this->decorateHostsFileExecutor($this->createExecutor($config, $loop ?: Loop::get())); return new Resolver($executor); @@ -59,8 +63,16 @@ public function create($config, LoopInterface $loop = null) * @throws \InvalidArgumentException for invalid DNS server address * @throws \UnderflowException when given DNS Config object has an empty list of nameservers */ - public function createCached($config, LoopInterface $loop = null, CacheInterface $cache = null) + public function createCached($config, $loop = null, $cache = null) { + if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + } + + if ($cache !== null && !$cache instanceof CacheInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #3 ($cache) expected null|React\Cache\CacheInterface'); + } + // default to keeping maximum of 256 responses in cache unless explicitly given if (!($cache instanceof CacheInterface)) { $cache = new ArrayCache(256); diff --git a/tests/Query/TcpTransportExecutorTest.php b/tests/Query/TcpTransportExecutorTest.php index 33dddacd..6c25a6db 100644 --- a/tests/Query/TcpTransportExecutorTest.php +++ b/tests/Query/TcpTransportExecutorTest.php @@ -943,4 +943,11 @@ public function testQueryAgainAfterPreviousQueryResolvedWillReuseSocketAndCancel // trigger second query $executor->query($query); } + + /** @test */ + public function constructorThrowsExceptionForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new TcpTransportExecutor('tcp://127.0.0.1:53', 'loop'); + } } diff --git a/tests/Query/TimeoutExecutorTest.php b/tests/Query/TimeoutExecutorTest.php index b7857783..e4ed5788 100644 --- a/tests/Query/TimeoutExecutorTest.php +++ b/tests/Query/TimeoutExecutorTest.php @@ -185,4 +185,11 @@ public function testRejectsPromiseAndCancelsPendingQueryWhenTimeoutTriggers() $this->assertInstanceOf('React\Dns\Query\TimeoutException', $exception); $this->assertEquals('DNS query for igor.io (A) timed out' , $exception->getMessage()); } + + /** @test */ + public function constructorThrowsExceptionForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #3 ($loop) expected null|React\EventLoop\LoopInterface'); + new TimeoutExecutor($this->executor, 5.0, 'loop'); + } } diff --git a/tests/Query/UdpTransportExecutorTest.php b/tests/Query/UdpTransportExecutorTest.php index 3f04d9ad..eb1afbaf 100644 --- a/tests/Query/UdpTransportExecutorTest.php +++ b/tests/Query/UdpTransportExecutorTest.php @@ -375,4 +375,11 @@ public function testQueryResolvesIfServerSendsValidResponse() $this->assertInstanceOf('React\Dns\Model\Message', $response); } + + /** @test */ + public function constructorThrowsExceptionForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new UdpTransportExecutor('udp://127.0.0.1:53', 'loop'); + } } diff --git a/tests/Resolver/FactoryTest.php b/tests/Resolver/FactoryTest.php index af758b51..585ee985 100644 --- a/tests/Resolver/FactoryTest.php +++ b/tests/Resolver/FactoryTest.php @@ -399,6 +399,32 @@ public function createCachedShouldCreateResolverWithCachingExecutorWithCustomCac $this->assertSame($cache, $cacheProperty); } + /** @test */ + public function createThrowsExceptionForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + $factory = new Factory(); + $factory->create('config', 'loop'); + } + + /** @test */ + public function createCachedThrowsExceptionForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + $factory = new Factory(); + $factory->createCached('config', 'loop'); + } + + /** @test */ + public function createCachedThrowsExceptionForInvalidCache() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + $this->setExpectedException('InvalidArgumentException', 'Argument #3 ($cache) expected null|React\Cache\CacheInterface'); + $factory = new Factory(); + $factory->createCached('config', $loop, 'cache'); + } + private function getResolverPrivateExecutor($resolver) { $executor = $this->getResolverPrivateMemberValue($resolver, 'executor'); From eb8ae001b5a455665c89c1df97f6fb682f8fb0f5 Mon Sep 17 00:00:00 2001 From: Simon Frings Date: Thu, 13 Jun 2024 16:18:03 +0200 Subject: [PATCH 2/7] Prepare v1.13.0 release --- CHANGELOG.md | 5 +++++ README.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a810eff4..bc1055fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.13.0 (2024-06-13) + +* Feature: Improve PHP 8.4+ support by avoiding implicitly nullable type declarations. + (#224 by @WyriHaximus) + ## 1.12.0 (2023-11-29) * Feature: Full PHP 8.3 compatibility. diff --git a/README.md b/README.md index 3b3cf022..9f83a944 100644 --- a/README.md +++ b/README.md @@ -410,7 +410,7 @@ This project follows [SemVer](https://semver.org/). This will install the latest supported version: ```bash -composer require react/dns:^1.12 +composer require react/dns:^1.13 ``` See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. From f4702f2668708167a80365ba1268e1be1cc2b05b Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 26 Aug 2025 09:25:10 +0200 Subject: [PATCH 3/7] [1.x] Increase query count in excessive TCP query tests This ports #237 from 3.x to 1.x. In a recent Ubuntu 24.04 runner update something changed that makes PHP code seemingly more performant. In #236 this issue is shown to have something to do with 24.04 because without code changes it works on 22.04. However, that doesn't fix the underlying issue. While I'm not sure what changed, by increasing these two numbers by 10 folding them we're back at passing tests. --- tests/Query/TcpTransportExecutorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Query/TcpTransportExecutorTest.php b/tests/Query/TcpTransportExecutorTest.php index 6c25a6db..3a08ca22 100644 --- a/tests/Query/TcpTransportExecutorTest.php +++ b/tests/Query/TcpTransportExecutorTest.php @@ -336,7 +336,7 @@ public function testQueryStaysPendingWhenClientCanNotSendExcessiveMessageInOneCh $query = new Query('google' . str_repeat('.com', 100), Message::TYPE_A, Message::CLASS_IN); // send a bunch of queries and keep reference to last promise - for ($i = 0; $i < 2000; ++$i) { + for ($i = 0; $i < 20000; ++$i) { $promise = $executor->query($query); } @@ -371,7 +371,7 @@ public function testQueryRejectsWhenClientKeepsSendingWhenServerClosesSocketWith // send a bunch of queries and keep reference to last promise $exception = null; - for ($i = 0; $i < 2000; ++$i) { + for ($i = 0; $i < 20000; ++$i) { $promise = $executor->query($query); $promise->then(null, function (\Exception $reason) use (&$exception) { $exception = $reason; From cc01bb3e318133cd03840e9bb8fcac3f4d47828d Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 26 Aug 2025 07:51:06 +0200 Subject: [PATCH 4/7] [1.x] Run tests on PHP 8.4 and update test environment Builds on #240 and #218 by backporting #233 to v1. --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65fe5d47..8a15fb4c 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-22.04 + runs-on: ubuntu-24.04 strategy: matrix: php: + - 8.4 - 8.3 - 8.2 - 8.1 @@ -39,13 +40,13 @@ jobs: PHPUnit-macOS: name: PHPUnit (macOS) - runs-on: macos-12 + runs-on: macos-14 continue-on-error: true steps: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: - php-version: 8.2 + php-version: 8.4 coverage: xdebug ini-file: development - run: composer install From 7dc1637155059ec22b126baedadfdebdad693840 Mon Sep 17 00:00:00 2001 From: Worma Date: Sat, 2 Aug 2025 10:12:57 +0200 Subject: [PATCH 5/7] setAccessible() has no effect as of PHP 8.1 --- src/Query/RetryExecutor.php | 4 +- tests/Query/TcpTransportExecutorTest.php | 48 +++++-- tests/Query/TimeoutExecutorTest.php | 4 +- tests/Query/UdpTransportExecutorTest.php | 16 ++- tests/Resolver/FactoryTest.php | 152 +++++++++++++++++------ 5 files changed, 168 insertions(+), 56 deletions(-) diff --git a/src/Query/RetryExecutor.php b/src/Query/RetryExecutor.php index 880609b2..11c123b8 100644 --- a/src/Query/RetryExecutor.php +++ b/src/Query/RetryExecutor.php @@ -50,7 +50,9 @@ public function tryQuery(Query $query, $retries) // avoid garbage references by replacing all closures in call stack. // what a lovely piece of code! $r = new \ReflectionProperty('Exception', 'trace'); - $r->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $r->setAccessible(true); + } $trace = $r->getValue($e); // Exception trace arguments are not available on some PHP 7.4 installs diff --git a/tests/Query/TcpTransportExecutorTest.php b/tests/Query/TcpTransportExecutorTest.php index 3a08ca22..3bdf3831 100644 --- a/tests/Query/TcpTransportExecutorTest.php +++ b/tests/Query/TcpTransportExecutorTest.php @@ -24,7 +24,9 @@ public function testCtorShouldAcceptNameserverAddresses($input, $expected) $executor = new TcpTransportExecutor($input, $loop); $ref = new \ReflectionProperty($executor, 'nameserver'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $value = $ref->getValue($executor); $this->assertEquals($expected, $value); @@ -65,7 +67,9 @@ public function testCtorWithoutLoopShouldAssignDefaultLoop() $executor = new TcpTransportExecutor('127.0.0.1'); $ref = new \ReflectionProperty($executor, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($executor); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); @@ -127,7 +131,9 @@ public function testQueryRejectsIfServerConnectionFails() $executor = new TcpTransportExecutor('::1', $loop); $ref = new \ReflectionProperty($executor, 'nameserver'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($executor, '///'); $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); @@ -306,7 +312,9 @@ public function testQueryStaysPendingWhenClientCanNotSendExcessiveMessageInOneCh $promise->then($this->expectCallableNever(), $this->expectCallableNever()); $ref = new \ReflectionProperty($executor, 'writePending'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $writePending = $ref->getValue($executor); $this->assertTrue($writePending); @@ -348,7 +356,9 @@ public function testQueryStaysPendingWhenClientCanNotSendExcessiveMessageInOneCh $promise->then($this->expectCallableNever(), $this->expectCallableNever()); $ref = new \ReflectionProperty($executor, 'writePending'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $writePending = $ref->getValue($executor); $this->assertTrue($writePending); @@ -389,7 +399,9 @@ public function testQueryRejectsWhenClientKeepsSendingWhenServerClosesSocketWith $executor->handleWritable(); $ref = new \ReflectionProperty($executor, 'writePending'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $writePending = $ref->getValue($executor); // We expect an EPIPE (Broken pipe) on second write. @@ -744,7 +756,9 @@ public function testQueryResolvesIfServerSendsBackResponseMessageAndWillStartIdl // use outgoing buffer as response message $ref = new \ReflectionProperty($executor, 'writeBuffer'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $data = $ref->getValue($executor); $client = stream_socket_accept($server); @@ -779,7 +793,9 @@ public function testQueryResolvesIfServerSendsBackResponseMessageAfterCancelling // use outgoing buffer as response message $ref = new \ReflectionProperty($executor, 'writeBuffer'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $data = $ref->getValue($executor); $client = stream_socket_accept($server); @@ -812,7 +828,9 @@ public function testQueryResolvesIfServerSendsBackResponseMessageAfterCancelling // use outgoing buffer as response message $ref = new \ReflectionProperty($executor, 'writeBuffer'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $data = $ref->getValue($executor); $client = stream_socket_accept($server); @@ -853,7 +871,9 @@ public function testTriggerIdleTimerAfterPreviousQueryResolvedWillCloseIdleSocke // use outgoing buffer as response message $ref = new \ReflectionProperty($executor, 'writeBuffer'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $data = $ref->getValue($executor); $client = stream_socket_accept($server); @@ -891,7 +911,9 @@ public function testClosingConnectionAfterPreviousQueryResolvedWillCancelIdleTim // use outgoing buffer as response message $ref = new \ReflectionProperty($executor, 'writeBuffer'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $data = $ref->getValue($executor); $client = stream_socket_accept($server); @@ -929,7 +951,9 @@ public function testQueryAgainAfterPreviousQueryResolvedWillReuseSocketAndCancel // use outgoing buffer as response message $ref = new \ReflectionProperty($executor, 'writeBuffer'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $data = $ref->getValue($executor); $client = stream_socket_accept($server); diff --git a/tests/Query/TimeoutExecutorTest.php b/tests/Query/TimeoutExecutorTest.php index e4ed5788..a0bc3abb 100644 --- a/tests/Query/TimeoutExecutorTest.php +++ b/tests/Query/TimeoutExecutorTest.php @@ -34,7 +34,9 @@ public function testCtorWithoutLoopShouldAssignDefaultLoop() $executor = new TimeoutExecutor($this->executor, 5.0); $ref = new \ReflectionProperty($executor, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($executor); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); diff --git a/tests/Query/UdpTransportExecutorTest.php b/tests/Query/UdpTransportExecutorTest.php index eb1afbaf..4036b675 100644 --- a/tests/Query/UdpTransportExecutorTest.php +++ b/tests/Query/UdpTransportExecutorTest.php @@ -24,7 +24,9 @@ public function testCtorShouldAcceptNameserverAddresses($input, $expected) $executor = new UdpTransportExecutor($input, $loop); $ref = new \ReflectionProperty($executor, 'nameserver'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $value = $ref->getValue($executor); $this->assertEquals($expected, $value); @@ -65,7 +67,9 @@ public function testCtorWithoutLoopShouldAssignDefaultLoop() $executor = new UdpTransportExecutor('127.0.0.1'); $ref = new \ReflectionProperty($executor, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($executor); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); @@ -132,7 +136,9 @@ public function testQueryRejectsIfServerConnectionFails() $executor = new UdpTransportExecutor('::1', $loop); $ref = new \ReflectionProperty($executor, 'nameserver'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($executor, '///'); $query = new Query('google.com', Message::TYPE_A, Message::CLASS_IN); @@ -161,7 +167,9 @@ public function testQueryRejectsIfSendToServerFailsAfterConnectionWithoutCalling // increase hard-coded maximum packet size to allow sending excessive data $ref = new \ReflectionProperty($executor, 'maxPacketSize'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($executor, PHP_INT_MAX); $error = null; diff --git a/tests/Resolver/FactoryTest.php b/tests/Resolver/FactoryTest.php index 585ee985..4a14df03 100644 --- a/tests/Resolver/FactoryTest.php +++ b/tests/Resolver/FactoryTest.php @@ -33,13 +33,17 @@ public function createWithoutSchemeShouldCreateResolverWithSelectiveUdpAndTcpExe $this->assertInstanceOf('React\Dns\Query\CoopExecutor', $coopExecutor); $ref = new \ReflectionProperty($coopExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $retryExecutor = $ref->getValue($coopExecutor); $this->assertInstanceOf('React\Dns\Query\RetryExecutor', $retryExecutor); $ref = new \ReflectionProperty($retryExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $selectiveExecutor = $ref->getValue($retryExecutor); $this->assertInstanceOf('React\Dns\Query\SelectiveTransportExecutor', $selectiveExecutor); @@ -47,13 +51,17 @@ public function createWithoutSchemeShouldCreateResolverWithSelectiveUdpAndTcpExe // udp below: $ref = new \ReflectionProperty($selectiveExecutor, 'datagramExecutor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $timeoutExecutor = $ref->getValue($selectiveExecutor); $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $udpExecutor = $ref->getValue($timeoutExecutor); $this->assertInstanceOf('React\Dns\Query\UdpTransportExecutor', $udpExecutor); @@ -61,13 +69,17 @@ public function createWithoutSchemeShouldCreateResolverWithSelectiveUdpAndTcpExe // tcp below: $ref = new \ReflectionProperty($selectiveExecutor, 'streamExecutor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $timeoutExecutor = $ref->getValue($selectiveExecutor); $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcpExecutor = $ref->getValue($timeoutExecutor); $this->assertInstanceOf('React\Dns\Query\TcpTransportExecutor', $tcpExecutor); @@ -88,19 +100,25 @@ public function createWithUdpSchemeShouldCreateResolverWithUdpExecutorStack() $this->assertInstanceOf('React\Dns\Query\CoopExecutor', $coopExecutor); $ref = new \ReflectionProperty($coopExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $retryExecutor = $ref->getValue($coopExecutor); $this->assertInstanceOf('React\Dns\Query\RetryExecutor', $retryExecutor); $ref = new \ReflectionProperty($retryExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $timeoutExecutor = $ref->getValue($retryExecutor); $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $udpExecutor = $ref->getValue($timeoutExecutor); $this->assertInstanceOf('React\Dns\Query\UdpTransportExecutor', $udpExecutor); @@ -121,19 +139,25 @@ public function createWithTcpSchemeShouldCreateResolverWithTcpExecutorStack() $this->assertInstanceOf('React\Dns\Query\CoopExecutor', $coopExecutor); $ref = new \ReflectionProperty($coopExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $retryExecutor = $ref->getValue($coopExecutor); $this->assertInstanceOf('React\Dns\Query\RetryExecutor', $retryExecutor); $ref = new \ReflectionProperty($retryExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $timeoutExecutor = $ref->getValue($retryExecutor); $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcpExecutor = $ref->getValue($timeoutExecutor); $this->assertInstanceOf('React\Dns\Query\TcpTransportExecutor', $tcpExecutor); @@ -157,19 +181,25 @@ public function createWithConfigWithTcpNameserverSchemeShouldCreateResolverWithT $this->assertInstanceOf('React\Dns\Query\CoopExecutor', $coopExecutor); $ref = new \ReflectionProperty($coopExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $retryExecutor = $ref->getValue($coopExecutor); $this->assertInstanceOf('React\Dns\Query\RetryExecutor', $retryExecutor); $ref = new \ReflectionProperty($retryExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $timeoutExecutor = $ref->getValue($retryExecutor); $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcpExecutor = $ref->getValue($timeoutExecutor); $this->assertInstanceOf('React\Dns\Query\TcpTransportExecutor', $tcpExecutor); @@ -194,49 +224,65 @@ public function createWithConfigWithTwoNameserversWithTcpSchemeShouldCreateResol $this->assertInstanceOf('React\Dns\Query\CoopExecutor', $coopExecutor); $ref = new \ReflectionProperty($coopExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $retryExecutor = $ref->getValue($coopExecutor); $this->assertInstanceOf('React\Dns\Query\RetryExecutor', $retryExecutor); $ref = new \ReflectionProperty($retryExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $fallbackExecutor = $ref->getValue($retryExecutor); $this->assertInstanceOf('React\Dns\Query\FallbackExecutor', $fallbackExecutor); $ref = new \ReflectionProperty($fallbackExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $timeoutExecutor = $ref->getValue($fallbackExecutor); $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcpExecutor = $ref->getValue($timeoutExecutor); $this->assertInstanceOf('React\Dns\Query\TcpTransportExecutor', $tcpExecutor); $ref = new \ReflectionProperty($tcpExecutor, 'nameserver'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $nameserver = $ref->getValue($tcpExecutor); $this->assertEquals('tcp://8.8.8.8:53', $nameserver); $ref = new \ReflectionProperty($fallbackExecutor, 'fallback'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $timeoutExecutor = $ref->getValue($fallbackExecutor); $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcpExecutor = $ref->getValue($timeoutExecutor); $this->assertInstanceOf('React\Dns\Query\TcpTransportExecutor', $tcpExecutor); $ref = new \ReflectionProperty($tcpExecutor, 'nameserver'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $nameserver = $ref->getValue($tcpExecutor); $this->assertEquals('tcp://1.1.1.1:53', $nameserver); @@ -262,73 +308,97 @@ public function createWithConfigWithThreeNameserversWithTcpSchemeShouldCreateRes $this->assertInstanceOf('React\Dns\Query\CoopExecutor', $coopExecutor); $ref = new \ReflectionProperty($coopExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $retryExecutor = $ref->getValue($coopExecutor); $this->assertInstanceOf('React\Dns\Query\RetryExecutor', $retryExecutor); $ref = new \ReflectionProperty($retryExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $fallbackExecutor = $ref->getValue($retryExecutor); $this->assertInstanceOf('React\Dns\Query\FallbackExecutor', $fallbackExecutor); $ref = new \ReflectionProperty($fallbackExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $timeoutExecutor = $ref->getValue($fallbackExecutor); $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcpExecutor = $ref->getValue($timeoutExecutor); $this->assertInstanceOf('React\Dns\Query\TcpTransportExecutor', $tcpExecutor); $ref = new \ReflectionProperty($tcpExecutor, 'nameserver'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $nameserver = $ref->getValue($tcpExecutor); $this->assertEquals('tcp://8.8.8.8:53', $nameserver); $ref = new \ReflectionProperty($fallbackExecutor, 'fallback'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $fallbackExecutor = $ref->getValue($fallbackExecutor); $this->assertInstanceOf('React\Dns\Query\FallbackExecutor', $fallbackExecutor); $ref = new \ReflectionProperty($fallbackExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $timeoutExecutor = $ref->getValue($fallbackExecutor); $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcpExecutor = $ref->getValue($timeoutExecutor); $this->assertInstanceOf('React\Dns\Query\TcpTransportExecutor', $tcpExecutor); $ref = new \ReflectionProperty($tcpExecutor, 'nameserver'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $nameserver = $ref->getValue($tcpExecutor); $this->assertEquals('tcp://1.1.1.1:53', $nameserver); $ref = new \ReflectionProperty($fallbackExecutor, 'fallback'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $timeoutExecutor = $ref->getValue($fallbackExecutor); $this->assertInstanceOf('React\Dns\Query\TimeoutExecutor', $timeoutExecutor); $ref = new \ReflectionProperty($timeoutExecutor, 'executor'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcpExecutor = $ref->getValue($timeoutExecutor); $this->assertInstanceOf('React\Dns\Query\TcpTransportExecutor', $tcpExecutor); $ref = new \ReflectionProperty($tcpExecutor, 'nameserver'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $nameserver = $ref->getValue($tcpExecutor); $this->assertEquals('tcp://9.9.9.9:53', $nameserver); @@ -432,7 +502,9 @@ private function getResolverPrivateExecutor($resolver) // extract underlying executor that may be wrapped in multiple layers of hosts file executors while ($executor instanceof HostsFileExecutor) { $reflector = new \ReflectionProperty('React\Dns\Query\HostsFileExecutor', 'fallback'); - $reflector->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $reflector->setAccessible(true); + } $executor = $reflector->getValue($executor); } @@ -443,14 +515,18 @@ private function getResolverPrivateExecutor($resolver) private function getResolverPrivateMemberValue($resolver, $field) { $reflector = new \ReflectionProperty('React\Dns\Resolver\Resolver', $field); - $reflector->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $reflector->setAccessible(true); + } return $reflector->getValue($resolver); } private function getCachingExecutorPrivateMemberValue($resolver, $field) { $reflector = new \ReflectionProperty('React\Dns\Query\CachingExecutor', $field); - $reflector->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $reflector->setAccessible(true); + } return $reflector->getValue($resolver); } } From acaf8baa622640861715a202bedcf41441f72e8c Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Wed, 12 Nov 2025 09:32:32 +0100 Subject: [PATCH 6/7] [1.x] Run CI jobs on PHP8.5 --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a15fb4c..5da29cab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,7 @@ jobs: strategy: matrix: php: + - 8.5 - 8.4 - 8.3 - 8.2 @@ -46,7 +47,7 @@ jobs: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: - php-version: 8.4 + php-version: 8.5 coverage: xdebug ini-file: development - run: composer install From 7562c05391f42701c1fccf189c8225fece1cd7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Tue, 18 Nov 2025 20:33:51 +0100 Subject: [PATCH 7/7] Prepare v1.14.0 release --- CHANGELOG.md | 8 ++++++++ README.md | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc1055fc..3064849c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.14.0 (2025-11-18) + +* Feature: Improve PHP 8.5+ support by avoiding deprecated `setAccessible()` calls. + (#238 by @W0rma and #243 by @WyriHaximus) + +* Improve test suite, update test environment and increase query count in excessive TCP query tests. + (#239 and #240 by @WyriHaximus) + ## 1.13.0 (2024-06-13) * Feature: Improve PHP 8.4+ support by avoiding implicitly nullable type declarations. diff --git a/README.md b/README.md index 9f83a944..b6e8fe4c 100644 --- a/README.md +++ b/README.md @@ -410,7 +410,7 @@ This project follows [SemVer](https://semver.org/). This will install the latest supported version: ```bash -composer require react/dns:^1.13 +composer require react/dns:^1.14 ``` See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.