From 77720b887fe05feffd6b4494e2f9cbe34195d4ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Sun, 11 Jan 2026 21:57:20 +0100 Subject: [PATCH] Improve PHP 8.4+ support by avoiding implicitly nullable types --- .github/workflows/ci.yml | 1 + composer.json | 6 +++--- src/Client.php | 10 +++++++++- src/Factory.php | 13 ++++++++++++- tests/ClientTest.php | 6 ++++++ tests/FactoryTest.php | 12 ++++++++++++ 6 files changed, 43 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4bcf71..8555965 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,7 @@ jobs: strategy: matrix: php: + - 8.4 - 8.3 - 8.2 - 8.1 diff --git a/composer.json b/composer.json index 9299d77..75a3141 100644 --- a/composer.json +++ b/composer.json @@ -14,12 +14,12 @@ "php": ">=5.3", "evenement/evenement": "^3.0 || ^2.0 || ^1.0", "react/event-loop": "^1.2", - "react/promise": "^3.0 || ^2.9 || ^1.1", - "react/socket": "^1.14" + "react/promise": "^3.2 || ^2.9 || ^1.1", + "react/socket": "^1.16" }, "require-dev": { "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4.2 || ^3.2 || ^2.2" + "react/async": "^4.3 || ^3.2 || ^2.2" }, "autoload": { "psr-4": { diff --git a/src/Client.php b/src/Client.php index 98307cd..ffc2aa6 100644 --- a/src/Client.php +++ b/src/Client.php @@ -33,8 +33,16 @@ class Client extends EventEmitter private $actionId = 0; - public function __construct(ConnectionInterface $stream, Parser $parser = null) + /** + * @param ConnectionInterface $stream + * @param ?Parser $parser + */ + public function __construct(ConnectionInterface $stream, $parser = null) { + if ($parser !== null && !$parser instanceof Parser) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #2 ($parser) expected null|Clue\React\Ami\Protocol\Parser'); + } + if ($parser === null) { $parser = new Parser(); } diff --git a/src/Factory.php b/src/Factory.php index 1169a27..b8ee591 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -43,8 +43,19 @@ class Factory { private $connector; - public function __construct(LoopInterface $loop = null, ConnectorInterface $connector = null) + /** + * @param ?LoopInterface $loop + * @param ?ConnectorInterface $connector + */ + public function __construct($loop = null, $connector = 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'); + } + if ($connector !== null && !$connector instanceof ConnectorInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #2 ($connector) expected null|React\Socket\ConnectorInterface'); + } + if ($connector === null) { $connector = new Connector(array(), $loop); } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 44ce726..4b93506 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -43,6 +43,12 @@ public function testUnexpectedResponseEmitsErrorAndClosesClient() $client->handleMessage(new Response(array('ActionID' => 1))); } + public function testCtorThrowsForInvalidParser() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($parser) expected null|Clue\React\Ami\Protocol\Parser'); + new Client($this->createStreamMock(), 'parser'); + } + private function createStreamMock() { if (method_exists('PHPUnit\Framework\MockObject\MockBuilder', 'onlyMethods')) { diff --git a/tests/FactoryTest.php b/tests/FactoryTest.php index 2fe2ff7..607c899 100644 --- a/tests/FactoryTest.php +++ b/tests/FactoryTest.php @@ -31,6 +31,18 @@ public function testDefaultCtorCreatesConnectorAutomatically() $this->assertInstanceOf('React\Socket\Connector', $connector); } + public function testCtorThrowsForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #1 ($loop) expected null|React\EventLoop\LoopInterface'); + new Factory('loop'); + } + + public function testCtorThrowsForInvalidConnector() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($connector) expected null|React\Socket\ConnectorInterface'); + new Factory(null, 'connector'); + } + public function testCreateClientUsesDefaultPortForTcpConnection() { $promise = new Promise(function () { });