From dbf58dc853f3b2e7182a57e6562dbeab576c8159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Tue, 14 May 2024 12:22:26 +0200 Subject: [PATCH 1/6] Improve PHP 8.4+ support by avoiding implicitly nullable types --- composer.json | 10 +++++----- src/FdServer.php | 6 +++++- src/HappyEyeBallsConnector.php | 17 ++++++++++++++--- src/SecureConnector.php | 11 ++++++++++- src/SecureServer.php | 6 +++++- src/Server.php | 12 ++++++++---- src/SocketServer.php | 6 +++++- src/TcpConnector.php | 10 +++++++++- src/TcpServer.php | 6 +++++- src/TimeoutConnector.php | 11 ++++++++++- src/UnixConnector.php | 9 ++++++++- src/UnixServer.php | 6 +++++- tests/FdServerTest.php | 6 ++++++ tests/HappyEyeBallsConnectorTest.php | 10 ++++++++-- tests/SecureConnectorTest.php | 6 ++++++ tests/SecureServerTest.php | 8 ++++++++ tests/ServerTest.php | 6 ++++++ tests/SocketServerTest.php | 6 ++++++ tests/TcpConnectorTest.php | 6 ++++++ tests/TcpServerTest.php | 6 ++++++ tests/TimeoutConnectorTest.php | 8 ++++++++ tests/UnixConnectorTest.php | 6 ++++++ tests/UnixServerTest.php | 6 ++++++ 23 files changed, 161 insertions(+), 23 deletions(-) diff --git a/composer.json b/composer.json index 02c184fb..b1e1d253 100644 --- a/composer.json +++ b/composer.json @@ -28,16 +28,16 @@ "require": { "php": ">=5.3.0", "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "react/dns": "^1.11", + "react/dns": "^1.13", "react/event-loop": "^1.2", - "react/promise": "^3 || ^2.6 || ^1.2.1", - "react/stream": "^1.2" + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" }, "require-dev": { "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4 || ^3 || ^2", + "react/async": "^4.3 || ^3.3 || ^2", "react/promise-stream": "^1.4", - "react/promise-timer": "^1.10" + "react/promise-timer": "^1.11" }, "autoload": { "psr-4": { diff --git a/src/FdServer.php b/src/FdServer.php index b1ed7779..8e46719a 100644 --- a/src/FdServer.php +++ b/src/FdServer.php @@ -75,7 +75,7 @@ final class FdServer extends EventEmitter implements ServerInterface * @throws \InvalidArgumentException if the listening address is invalid * @throws \RuntimeException if listening on this address fails (already in use etc.) */ - public function __construct($fd, LoopInterface $loop = null) + public function __construct($fd, $loop = null) { if (\preg_match('#^php://fd/(\d+)$#', $fd, $m)) { $fd = (int) $m[1]; @@ -87,6 +87,10 @@ public function __construct($fd, LoopInterface $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'); + } + $this->loop = $loop ?: Loop::get(); $errno = 0; diff --git a/src/HappyEyeBallsConnector.php b/src/HappyEyeBallsConnector.php index 98b1d58c..a5511ac9 100644 --- a/src/HappyEyeBallsConnector.php +++ b/src/HappyEyeBallsConnector.php @@ -13,15 +13,26 @@ final class HappyEyeBallsConnector implements ConnectorInterface private $connector; private $resolver; - public function __construct(LoopInterface $loop = null, ConnectorInterface $connector = null, ResolverInterface $resolver = null) + /** + * @param ?LoopInterface $loop + * @param ConnectorInterface $connector + * @param ResolverInterface $resolver + */ + public function __construct($loop = null, $connector = null, $resolver = null) { // $connector and $resolver arguments are actually required, marked // optional for technical reasons only. Nullable $loop without default // requires PHP 7.1, null default is also supported in legacy PHP // versions, but required parameters are not allowed after arguments // with null default. Mark all parameters optional and check accordingly. - if ($connector === null || $resolver === null) { - throw new \InvalidArgumentException('Missing required $connector or $resolver argument'); + if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface'); + } + if (!$connector instanceof ConnectorInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #2 ($connector) expected React\Socket\ConnectorInterface'); + } + if (!$resolver instanceof ResolverInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #3 ($resolver) expected React\Dns\Resolver\ResolverInterface'); } $this->loop = $loop ?: Loop::get(); diff --git a/src/SecureConnector.php b/src/SecureConnector.php index 17c229d3..08255ac9 100644 --- a/src/SecureConnector.php +++ b/src/SecureConnector.php @@ -15,8 +15,17 @@ final class SecureConnector implements ConnectorInterface private $streamEncryption; private $context; - public function __construct(ConnectorInterface $connector, LoopInterface $loop = null, array $context = array()) + /** + * @param ConnectorInterface $connector + * @param ?LoopInterface $loop + * @param array $context + */ + public function __construct(ConnectorInterface $connector, $loop = null, array $context = array()) { + 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->connector = $connector; $this->streamEncryption = new StreamEncryption($loop ?: Loop::get(), false); $this->context = $context; diff --git a/src/SecureServer.php b/src/SecureServer.php index d0525c94..5a202d27 100644 --- a/src/SecureServer.php +++ b/src/SecureServer.php @@ -122,8 +122,12 @@ final class SecureServer extends EventEmitter implements ServerInterface * @see TcpServer * @link https://www.php.net/manual/en/context.ssl.php for TLS context options */ - public function __construct(ServerInterface $tcp, LoopInterface $loop = null, array $context = array()) + public function __construct(ServerInterface $tcp, $loop = null, array $context = array()) { + 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 (!\function_exists('stream_socket_enable_crypto')) { throw new \BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)'); // @codeCoverageIgnore } diff --git a/src/Server.php b/src/Server.php index 7d4111e8..b24c5564 100644 --- a/src/Server.php +++ b/src/Server.php @@ -43,14 +43,18 @@ final class Server extends EventEmitter implements ServerInterface * For BC reasons, you can also pass the TCP socket context options as a simple * array without wrapping this in another array under the `tcp` key. * - * @param string|int $uri - * @param LoopInterface $loop - * @param array $context + * @param string|int $uri + * @param ?LoopInterface $loop + * @param array $context * @deprecated 1.9.0 See `SocketServer` instead * @see SocketServer */ - public function __construct($uri, LoopInterface $loop = null, array $context = array()) + public function __construct($uri, $loop = null, array $context = array()) { + 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'); + } + $loop = $loop ?: Loop::get(); // sanitize TCP context options if not properly wrapped diff --git a/src/SocketServer.php b/src/SocketServer.php index b78dc3a4..e987f5f6 100644 --- a/src/SocketServer.php +++ b/src/SocketServer.php @@ -31,8 +31,12 @@ final class SocketServer extends EventEmitter implements ServerInterface * @throws \InvalidArgumentException if the listening address is invalid * @throws \RuntimeException if listening on this address fails (already in use etc.) */ - public function __construct($uri, array $context = array(), LoopInterface $loop = null) + public function __construct($uri, array $context = array(), $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'); + } + // apply default options if not explicitly given $context += array( 'tcp' => array(), diff --git a/src/TcpConnector.php b/src/TcpConnector.php index 8cfc7bf4..9d2599e8 100644 --- a/src/TcpConnector.php +++ b/src/TcpConnector.php @@ -13,8 +13,16 @@ final class TcpConnector implements ConnectorInterface private $loop; private $context; - public function __construct(LoopInterface $loop = null, array $context = array()) + /** + * @param ?LoopInterface $loop + * @param array $context + */ + public function __construct($loop = null, array $context = array()) { + if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface'); + } + $this->loop = $loop ?: Loop::get(); $this->context = $context; } diff --git a/src/TcpServer.php b/src/TcpServer.php index 235761d4..01b2b46d 100644 --- a/src/TcpServer.php +++ b/src/TcpServer.php @@ -128,8 +128,12 @@ final class TcpServer extends EventEmitter implements ServerInterface * @throws InvalidArgumentException if the listening address is invalid * @throws RuntimeException if listening on this address fails (already in use etc.) */ - public function __construct($uri, LoopInterface $loop = null, array $context = array()) + public function __construct($uri, $loop = null, array $context = array()) { + 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->loop = $loop ?: Loop::get(); // a single port has been given => assume localhost diff --git a/src/TimeoutConnector.php b/src/TimeoutConnector.php index a20ea5a6..9ef252f7 100644 --- a/src/TimeoutConnector.php +++ b/src/TimeoutConnector.php @@ -12,8 +12,17 @@ final class TimeoutConnector implements ConnectorInterface private $timeout; private $loop; - public function __construct(ConnectorInterface $connector, $timeout, LoopInterface $loop = null) + /** + * @param ConnectorInterface $connector + * @param float $timeout + * @param ?LoopInterface $loop + */ + public function __construct(ConnectorInterface $connector, $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->connector = $connector; $this->timeout = $timeout; $this->loop = $loop ?: Loop::get(); diff --git a/src/UnixConnector.php b/src/UnixConnector.php index 627d60f7..95f932cb 100644 --- a/src/UnixConnector.php +++ b/src/UnixConnector.php @@ -18,8 +18,15 @@ final class UnixConnector implements ConnectorInterface { private $loop; - public function __construct(LoopInterface $loop = null) + /** + * @param ?LoopInterface $loop + */ + public function __construct($loop = null) { + if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface'); + } + $this->loop = $loop ?: Loop::get(); } diff --git a/src/UnixServer.php b/src/UnixServer.php index cc46968d..27b014d1 100644 --- a/src/UnixServer.php +++ b/src/UnixServer.php @@ -50,8 +50,12 @@ final class UnixServer extends EventEmitter implements ServerInterface * @throws InvalidArgumentException if the listening address is invalid * @throws RuntimeException if listening on this address fails (already in use etc.) */ - public function __construct($path, LoopInterface $loop = null, array $context = array()) + public function __construct($path, $loop = null, array $context = array()) { + 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->loop = $loop ?: Loop::get(); if (\strpos($path, '://') === false) { diff --git a/tests/FdServerTest.php b/tests/FdServerTest.php index 7a97ae7d..34b1fadf 100644 --- a/tests/FdServerTest.php +++ b/tests/FdServerTest.php @@ -50,6 +50,12 @@ public function testCtorThrowsForInvalidUrl() new FdServer('tcp://127.0.0.1:8080', $loop); } + public function testCtorThrowsForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new FdServer(0, 'loop'); + } + public function testCtorThrowsForUnknownFdWithoutCallingCustomErrorHandler() { if (!is_dir('/dev/fd') || defined('HHVM_VERSION')) { diff --git a/tests/HappyEyeBallsConnectorTest.php b/tests/HappyEyeBallsConnectorTest.php index 5301b3b4..c4516a7a 100644 --- a/tests/HappyEyeBallsConnectorTest.php +++ b/tests/HappyEyeBallsConnectorTest.php @@ -40,15 +40,21 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); } + public function testConstructWithInvalidLoopThrows() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #1 ($loop) expected null|React\EventLoop\LoopInterface'); + new HappyEyeBallsConnector('loop', $this->tcp, $this->resolver); + } + public function testConstructWithoutRequiredConnectorThrows() { - $this->setExpectedException('InvalidArgumentException'); + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($connector) expected React\Socket\ConnectorInterface'); new HappyEyeBallsConnector(null, null, $this->resolver); } public function testConstructWithoutRequiredResolverThrows() { - $this->setExpectedException('InvalidArgumentException'); + $this->setExpectedException('InvalidArgumentException', 'Argument #3 ($resolver) expected React\Dns\Resolver\ResolverInterface'); new HappyEyeBallsConnector(null, $this->tcp); } diff --git a/tests/SecureConnectorTest.php b/tests/SecureConnectorTest.php index e81f4a97..c115a2b3 100644 --- a/tests/SecureConnectorTest.php +++ b/tests/SecureConnectorTest.php @@ -26,6 +26,12 @@ public function setUpConnector() $this->connector = new SecureConnector($this->tcp, $this->loop); } + public function testCtorThrowsForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new SecureConnector($this->tcp, 'loop'); + } + public function testConstructWithoutLoopAssignsLoopAutomatically() { $connector = new SecureConnector($this->tcp); diff --git a/tests/SecureServerTest.php b/tests/SecureServerTest.php index a6ddcf29..6265618f 100644 --- a/tests/SecureServerTest.php +++ b/tests/SecureServerTest.php @@ -18,6 +18,14 @@ public function setUpSkipTest() } } + public function testCtorThrowsForInvalidLoop() + { + $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); + + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new SecureServer($tcp, 'loop'); + } + public function testConstructWithoutLoopAssignsLoopAutomatically() { $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock(); diff --git a/tests/ServerTest.php b/tests/ServerTest.php index f69e6cb1..f3859cc6 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -44,6 +44,12 @@ public function testConstructorThrowsForInvalidUri() $server = new Server('invalid URI', $loop); } + public function testCtorThrowsForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new Server('127.0.0.1:0', 'loop'); + } + public function testConstructorCreatesExpectedTcpServer() { $server = new Server(0); diff --git a/tests/SocketServerTest.php b/tests/SocketServerTest.php index c7cee8ec..cd53f75c 100644 --- a/tests/SocketServerTest.php +++ b/tests/SocketServerTest.php @@ -65,6 +65,12 @@ public function testConstructorWithInvalidUriWithSchemaAndPortOnlyThrows() new SocketServer('tcp://0'); } + public function testCtorThrowsForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #3 ($loop) expected null|React\EventLoop\LoopInterface'); + new SocketServer('127.0.0.1:0', array(), 'loop'); + } + public function testConstructorCreatesExpectedTcpServer() { $socket = new SocketServer('127.0.0.1:0', array()); diff --git a/tests/TcpConnectorTest.php b/tests/TcpConnectorTest.php index fb6f871c..8e5f138d 100644 --- a/tests/TcpConnectorTest.php +++ b/tests/TcpConnectorTest.php @@ -12,6 +12,12 @@ class TcpConnectorTest extends TestCase { const TIMEOUT = 5.0; + public function testCtorThrowsForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #1 ($loop) expected null|React\EventLoop\LoopInterface'); + new TcpConnector('loop'); + } + public function testConstructWithoutLoopAssignsLoopAutomatically() { $connector = new TcpConnector(); diff --git a/tests/TcpServerTest.php b/tests/TcpServerTest.php index 8908d1c2..0c930c81 100644 --- a/tests/TcpServerTest.php +++ b/tests/TcpServerTest.php @@ -26,6 +26,12 @@ public function setUpServer() $this->port = parse_url($this->server->getAddress(), PHP_URL_PORT); } + public function testCtorThrowsForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new TcpServer(0, 'loop'); + } + public function testConstructWithoutLoopAssignsLoopAutomatically() { $server = new TcpServer(0); diff --git a/tests/TimeoutConnectorTest.php b/tests/TimeoutConnectorTest.php index fc218c46..eab59422 100644 --- a/tests/TimeoutConnectorTest.php +++ b/tests/TimeoutConnectorTest.php @@ -9,6 +9,14 @@ class TimeoutConnectorTest extends TestCase { + public function testCtorThrowsForInvalidLoop() + { + $base = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); + + $this->setExpectedException('InvalidArgumentException', 'Argument #3 ($loop) expected null|React\EventLoop\LoopInterface'); + new TimeoutConnector($base, 0.001, 'loop'); + } + public function testConstructWithoutLoopAssignsLoopAutomatically() { $base = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); diff --git a/tests/UnixConnectorTest.php b/tests/UnixConnectorTest.php index d7e314a4..5bcd7a55 100644 --- a/tests/UnixConnectorTest.php +++ b/tests/UnixConnectorTest.php @@ -19,6 +19,12 @@ public function setUpConnector() $this->connector = new UnixConnector($this->loop); } + public function testCtorThrowsForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #1 ($loop) expected null|React\EventLoop\LoopInterface'); + new UnixConnector('loop'); + } + public function testConstructWithoutLoopAssignsLoopAutomatically() { $connector = new UnixConnector(); diff --git a/tests/UnixServerTest.php b/tests/UnixServerTest.php index c148de4f..26f28d99 100644 --- a/tests/UnixServerTest.php +++ b/tests/UnixServerTest.php @@ -29,6 +29,12 @@ public function setUpServer() $this->server = new UnixServer($this->uds); } + public function testCtorThrowsForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new UnixServer($this->getRandomSocketUri(), 'loop'); + } + public function testConstructWithoutLoopAssignsLoopAutomatically() { unlink(str_replace('unix://', '', $this->uds)); From 23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 26 Jul 2024 12:38:09 +0200 Subject: [PATCH 2/6] Prepare v1.16.0 release --- CHANGELOG.md | 5 +++++ README.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db178ca6..659560f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.16.0 (2024-07-26) + +* Feature: Improve PHP 8.4+ support by avoiding implicitly nullable type declarations. + (#318 by @clue) + ## 1.15.0 (2023-12-15) * Feature: Full PHP 8.3 compatibility. diff --git a/README.md b/README.md index 18e3d913..e77e6764 100644 --- a/README.md +++ b/README.md @@ -1494,7 +1494,7 @@ This project follows [SemVer](https://semver.org/). This will install the latest supported version: ```bash -composer require react/socket:^1.15 +composer require react/socket:^1.16 ``` See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. From 3b1b210c01e70eda858c434fc2f1f3ca0868ddfd Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 26 Aug 2025 07:45:02 +0200 Subject: [PATCH 3/6] [1.x] Run tests on PHP 8.4 and update test environment Builds on top of #310 and #318 by porting #321 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 5e7d91e1..c5bd99ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,9 +11,10 @@ jobs: strategy: matrix: os: - - ubuntu-22.04 + - ubuntu-24.04 - windows-2022 php: + - 8.4 - 8.3 - 8.2 - 8.1 @@ -44,13 +45,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 - run: composer install - run: vendor/bin/phpunit --coverage-text From 68965d261dd1650d84e2546d5e78d739d531060d Mon Sep 17 00:00:00 2001 From: Worma Date: Sat, 2 Aug 2025 10:20:38 +0200 Subject: [PATCH 4/6] setAccessible() has no effect as of PHP 8.1 --- src/DnsConnector.php | 4 ++- src/SecureConnector.php | 4 ++- tests/ConnectorTest.php | 32 +++++++++++++++----- tests/FunctionalTcpServerTest.php | 8 +++-- tests/HappyEyeBallsConnectionBuilderTest.php | 24 +++++++++++---- tests/HappyEyeBallsConnectorTest.php | 4 ++- tests/SecureConnectorTest.php | 24 +++++++++++---- tests/SecureServerTest.php | 24 +++++++++++---- tests/ServerTest.php | 12 ++++++-- tests/SocketServerTest.php | 12 ++++++-- tests/TcpConnectorTest.php | 4 ++- tests/TcpServerTest.php | 4 ++- tests/TimeoutConnectorTest.php | 4 ++- tests/UnixConnectorTest.php | 4 ++- tests/UnixServerTest.php | 4 ++- 15 files changed, 126 insertions(+), 42 deletions(-) diff --git a/src/DnsConnector.php b/src/DnsConnector.php index d2fb2c7d..e5fd2384 100644 --- a/src/DnsConnector.php +++ b/src/DnsConnector.php @@ -68,7 +68,9 @@ function ($resolve, $reject) use (&$promise, &$resolved, $uri, $connector, $host // 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/src/SecureConnector.php b/src/SecureConnector.php index 08255ac9..98cc46a5 100644 --- a/src/SecureConnector.php +++ b/src/SecureConnector.php @@ -91,7 +91,9 @@ public function connect($uri) // 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/ConnectorTest.php b/tests/ConnectorTest.php index 02982dbd..ca10b788 100644 --- a/tests/ConnectorTest.php +++ b/tests/ConnectorTest.php @@ -12,11 +12,15 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $connector = new Connector(); $ref = new \ReflectionProperty($connector, 'connectors'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $connectors = $ref->getValue($connector); $ref = new \ReflectionProperty($connectors['tcp'], 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($connectors['tcp']); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); @@ -29,11 +33,15 @@ public function testConstructWithLoopAssignsGivenLoop() $connector = new Connector(array(), $loop); $ref = new \ReflectionProperty($connector, 'connectors'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $connectors = $ref->getValue($connector); $ref = new \ReflectionProperty($connectors['tcp'], 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($connectors['tcp']); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); @@ -50,7 +58,9 @@ public function testConstructWithContextAssignsGivenContext() )); $ref = new \ReflectionProperty($connector, 'connectors'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $connectors = $ref->getValue($connector); $this->assertSame($tcp, $connectors['tcp']); @@ -67,7 +77,9 @@ public function testConstructWithLegacyContextSignatureAssignsGivenContext() )); $ref = new \ReflectionProperty($connector, 'connectors'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $connectors = $ref->getValue($connector); $this->assertSame($tcp, $connectors['tcp']); @@ -80,11 +92,15 @@ public function testConstructWithLegacyLoopSignatureAssignsGivenLoop() $connector = new Connector($loop); $ref = new \ReflectionProperty($connector, 'connectors'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $connectors = $ref->getValue($connector); $ref = new \ReflectionProperty($connectors['tcp'], 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($connectors['tcp']); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); diff --git a/tests/FunctionalTcpServerTest.php b/tests/FunctionalTcpServerTest.php index 7575d321..7b46b22c 100644 --- a/tests/FunctionalTcpServerTest.php +++ b/tests/FunctionalTcpServerTest.php @@ -331,7 +331,9 @@ public function testServerPassesContextOptionsToSocket() )); $ref = new \ReflectionProperty($server, 'master'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $socket = $ref->getValue($server); $context = stream_context_get_options($socket); @@ -346,7 +348,9 @@ public function testServerPassesDefaultBacklogSizeViaContextOptionsToSocket() $server = new TcpServer(0); $ref = new \ReflectionProperty($server, 'master'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $socket = $ref->getValue($server); $context = stream_context_get_options($socket); diff --git a/tests/HappyEyeBallsConnectionBuilderTest.php b/tests/HappyEyeBallsConnectionBuilderTest.php index 581d8836..ce347473 100644 --- a/tests/HappyEyeBallsConnectionBuilderTest.php +++ b/tests/HappyEyeBallsConnectionBuilderTest.php @@ -844,13 +844,17 @@ public function testCheckCallsRejectFunctionImmediateWithoutLeavingDanglingPromi $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts); $ref = new \ReflectionProperty($builder, 'connectQueue'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($builder, array('::1')); $builder->check($this->expectCallableNever(), function () { }); $ref = new \ReflectionProperty($builder, 'connectionPromises'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $promises = $ref->getValue($builder); $this->assertEquals(array(), $promises); @@ -876,7 +880,9 @@ public function testCleanUpCancelsAllPendingConnectionAttempts() $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts); $ref = new \ReflectionProperty($builder, 'connectQueue'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($builder, array('::1', '::1')); $builder->check($this->expectCallableNever(), function () { }); @@ -904,7 +910,9 @@ public function testCleanUpCancelsAllPendingConnectionAttemptsWithoutStartingNew $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts); $ref = new \ReflectionProperty($builder, 'connectQueue'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($builder, array('::1', '::1')); $builder->check($this->expectCallableNever(), function () { }); @@ -927,7 +935,9 @@ public function testMixIpsIntoConnectQueueSometimesAssignsInOriginalOrder() $builder->mixIpsIntoConnectQueue(array('::1', '::2')); $ref = new \ReflectionProperty($builder, 'connectQueue'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $value = $ref->getValue($builder); if ($value === array('::1', '::2')) { @@ -953,7 +963,9 @@ public function testMixIpsIntoConnectQueueSometimesAssignsInReverseOrder() $builder->mixIpsIntoConnectQueue(array('::1', '::2')); $ref = new \ReflectionProperty($builder, 'connectQueue'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $value = $ref->getValue($builder); if ($value === array('::2', '::1')) { diff --git a/tests/HappyEyeBallsConnectorTest.php b/tests/HappyEyeBallsConnectorTest.php index c4516a7a..b8daa720 100644 --- a/tests/HappyEyeBallsConnectorTest.php +++ b/tests/HappyEyeBallsConnectorTest.php @@ -34,7 +34,9 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $connector = new HappyEyeBallsConnector(null, $this->tcp, $this->resolver); $ref = new \ReflectionProperty($connector, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($connector); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); diff --git a/tests/SecureConnectorTest.php b/tests/SecureConnectorTest.php index c115a2b3..c3f6e233 100644 --- a/tests/SecureConnectorTest.php +++ b/tests/SecureConnectorTest.php @@ -37,11 +37,15 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $connector = new SecureConnector($this->tcp); $ref = new \ReflectionProperty($connector, 'streamEncryption'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $streamEncryption = $ref->getValue($connector); $ref = new \ReflectionProperty($streamEncryption, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($streamEncryption); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); @@ -185,7 +189,9 @@ public function testStreamEncryptionWillBeEnabledAfterConnecting() $encryption->expects($this->once())->method('enable')->with($connection)->willReturn(new \React\Promise\Promise(function () { })); $ref = new \ReflectionProperty($this->connector, 'streamEncryption'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($this->connector, $encryption); $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->willReturn(Promise\resolve($connection)); @@ -202,7 +208,9 @@ public function testConnectionWillBeRejectedIfStreamEncryptionFailsAndClosesConn $encryption->expects($this->once())->method('enable')->willReturn(Promise\reject(new \RuntimeException('TLS error', 123))); $ref = new \ReflectionProperty($this->connector, 'streamEncryption'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($this->connector, $encryption); $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->willReturn(Promise\resolve($connection)); @@ -234,7 +242,9 @@ public function testCancelDuringStreamEncryptionCancelsEncryptionAndClosesConnec $encryption->expects($this->once())->method('enable')->willReturn($pending); $ref = new \ReflectionProperty($this->connector, 'streamEncryption'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($this->connector, $encryption); $deferred = new Deferred(); @@ -301,7 +311,9 @@ public function testRejectionDuringTlsHandshakeShouldNotCreateAnyGarbageReferenc $encryption->expects($this->once())->method('enable')->willReturn($tls->promise()); $ref = new \ReflectionProperty($this->connector, 'streamEncryption'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($this->connector, $encryption); $promise = $this->connector->connect('example.com:80'); diff --git a/tests/SecureServerTest.php b/tests/SecureServerTest.php index 6265618f..7aa812b8 100644 --- a/tests/SecureServerTest.php +++ b/tests/SecureServerTest.php @@ -33,11 +33,15 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $server = new SecureServer($tcp); $ref = new \ReflectionProperty($server, 'encryption'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $encryption = $ref->getValue($server); $ref = new \ReflectionProperty($encryption, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($encryption); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); @@ -137,11 +141,15 @@ public function testConnectionWillTryToEnableEncryptionAndWaitForHandshake() $encryption->expects($this->once())->method('enable')->willReturn($pending); $ref = new \ReflectionProperty($server, 'encryption'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($server, $encryption); $ref = new \ReflectionProperty($server, 'context'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($server, array()); $server->on('error', $this->expectCallableNever()); @@ -168,11 +176,15 @@ public function testConnectionWillBeClosedWithErrorIfEnablingEncryptionFails() $encryption->expects($this->once())->method('enable')->willReturn(\React\Promise\reject($error)); $ref = new \ReflectionProperty($server, 'encryption'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($server, $encryption); $ref = new \ReflectionProperty($server, 'context'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue($server, array()); $error = null; diff --git a/tests/ServerTest.php b/tests/ServerTest.php index f3859cc6..00f583e3 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -17,11 +17,15 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $server = new Server(0); $ref = new \ReflectionProperty($server, 'server'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcp = $ref->getValue($server); $ref = new \ReflectionProperty($tcp, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($tcp); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); @@ -115,7 +119,9 @@ public function testEmitsErrorWhenUnderlyingTcpServerEmitsError() $server = new Server(0); $ref = new \ReflectionProperty($server, 'server'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcp = $ref->getvalue($server); $error = new \RuntimeException(); diff --git a/tests/SocketServerTest.php b/tests/SocketServerTest.php index cd53f75c..d24154d5 100644 --- a/tests/SocketServerTest.php +++ b/tests/SocketServerTest.php @@ -18,11 +18,15 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $socket->close(); $ref = new \ReflectionProperty($socket, 'server'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcp = $ref->getValue($socket); $ref = new \ReflectionProperty($tcp, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($tcp); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); @@ -151,7 +155,9 @@ public function testEmitsErrorWhenUnderlyingTcpServerEmitsError() $socket = new SocketServer('127.0.0.1:0', array()); $ref = new \ReflectionProperty($socket, 'server'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $tcp = $ref->getvalue($socket); $error = new \RuntimeException(); diff --git a/tests/TcpConnectorTest.php b/tests/TcpConnectorTest.php index 8e5f138d..5b1016d7 100644 --- a/tests/TcpConnectorTest.php +++ b/tests/TcpConnectorTest.php @@ -23,7 +23,9 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $connector = new TcpConnector(); $ref = new \ReflectionProperty($connector, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($connector); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); diff --git a/tests/TcpServerTest.php b/tests/TcpServerTest.php index 0c930c81..e2aa2feb 100644 --- a/tests/TcpServerTest.php +++ b/tests/TcpServerTest.php @@ -37,7 +37,9 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $server = new TcpServer(0); $ref = new \ReflectionProperty($server, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($server); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); diff --git a/tests/TimeoutConnectorTest.php b/tests/TimeoutConnectorTest.php index eab59422..df5a2404 100644 --- a/tests/TimeoutConnectorTest.php +++ b/tests/TimeoutConnectorTest.php @@ -24,7 +24,9 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $connector = new TimeoutConnector($base, 0.01); $ref = new \ReflectionProperty($connector, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($connector); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); diff --git a/tests/UnixConnectorTest.php b/tests/UnixConnectorTest.php index 5bcd7a55..fcb57c5b 100644 --- a/tests/UnixConnectorTest.php +++ b/tests/UnixConnectorTest.php @@ -30,7 +30,9 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $connector = new UnixConnector(); $ref = new \ReflectionProperty($connector, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($connector); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); diff --git a/tests/UnixServerTest.php b/tests/UnixServerTest.php index 26f28d99..f962b921 100644 --- a/tests/UnixServerTest.php +++ b/tests/UnixServerTest.php @@ -43,7 +43,9 @@ public function testConstructWithoutLoopAssignsLoopAutomatically() $server = new UnixServer($this->uds); $ref = new \ReflectionProperty($server, 'loop'); - $ref->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $loop = $ref->getValue($server); $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); From 2ef449491508d62a4c1555d9a4728206954c4380 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 18 Nov 2025 17:41:09 +0100 Subject: [PATCH 5/6] [1.x] Update to run tests on PHP 8.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 c5bd99ce..84bbe01b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,7 @@ jobs: - ubuntu-24.04 - windows-2022 php: + - 8.5 - 8.4 - 8.3 - 8.2 @@ -51,7 +52,7 @@ jobs: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: - php-version: 8.4 + php-version: 8.5 coverage: xdebug - run: composer install - run: vendor/bin/phpunit --coverage-text From ef5b17b81f6f60504c539313f94f2d826c5faa08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Wed, 19 Nov 2025 21:47:34 +0100 Subject: [PATCH 6/6] Prepare v1.17.0 release --- CHANGELOG.md | 8 ++++++++ README.md | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 659560f5..c58f2604 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.17.0 (2025-11-19) + +* Feature: Improve PHP 8.5+ support by avoiding deprecated `setAccessible()` calls. + (#325 by @W0rma and #330 by @WyriHaximus) + +* Improve test suite and update test environment. + (#326 and #330 by @WyriHaximus) + ## 1.16.0 (2024-07-26) * Feature: Improve PHP 8.4+ support by avoiding implicitly nullable type declarations. diff --git a/README.md b/README.md index e77e6764..092590a3 100644 --- a/README.md +++ b/README.md @@ -1494,7 +1494,7 @@ This project follows [SemVer](https://semver.org/). This will install the latest supported version: ```bash -composer require react/socket:^1.16 +composer require react/socket:^1.17 ``` See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.