diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6239b6..98ceb2a 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 @@ -38,7 +39,7 @@ jobs: PHPUnit-hhvm: name: PHPUnit (HHVM) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 continue-on-error: true steps: - uses: actions/checkout@v4 diff --git a/composer.json b/composer.json index 44e4833..1ebd83a 100644 --- a/composer.json +++ b/composer.json @@ -22,9 +22,9 @@ }, "require": { "php": ">=5.3", - "clue/multicast-react": "^1.0 || ^0.2", + "clue/multicast-react": "1.x-dev#f1bd5df0309b9f8b3486b09bf37aedfb042addb6", "react/event-loop": "^1.2", - "react/promise": "^2.0 || ^1.0" + "react/promise": "^3.2 || ^2.7 || ^1.2.1" }, "require-dev": { "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" diff --git a/src/Client.php b/src/Client.php index 6a9e46a..e2410d6 100644 --- a/src/Client.php +++ b/src/Client.php @@ -28,8 +28,14 @@ class Client * @param ?LoopInterface $loop * @param ?MulticastFactory $multicast */ - public function __construct(LoopInterface $loop = null, MulticastFactory $multicast = null) + public function __construct($loop = null, $multicast = 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 ($multicast !== null && !$multicast instanceof MulticastFactory) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #2 ($multicast) expected null|Clue\React\Multicast\Factory'); + } $this->loop = $loop ?: Loop::get(); $this->multicast = $multicast ?: new MulticastFactory($this->loop); } @@ -46,11 +52,7 @@ public function search($searchTarget = 'ssdp:all', $mx = 2) $socket = $this->multicast->createSender(); // TODO: The TTL for the IP packet SHOULD default to 2 and SHOULD be configurable. - $timer = $this->loop->addTimer($mx, function() use ($socket, &$deferred) { - $deferred->resolve(); - $socket->close(); - }); - + $messages = array(); $loop = $this->loop; $deferred = new Deferred(function () use ($socket, &$timer, $loop) { // canceling resulting promise cancels timer and closes socket @@ -59,11 +61,15 @@ public function search($searchTarget = 'ssdp:all', $mx = 2) throw new RuntimeException('Cancelled'); }); + $timer = $this->loop->addTimer($mx, function() use ($socket, &$deferred, &$messages) { + $deferred->resolve($messages); + $socket->close(); + }); + $that = $this; - $socket->on('message', function ($data, $remote) use ($deferred, $that) { + $socket->on('message', function ($data, $remote) use (&$messages, $that) { $message = $that->parseMessage($data, $remote); - - $deferred->progress($message); + $messages[] = $message; }); $socket->send($data, self::ADDRESS); diff --git a/tests/ClientTest.php b/tests/ClientTest.php index d4829e8..87b6e8e 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -71,4 +71,32 @@ public function testSearchTimeout() $promise->then($this->expectCallableOnce(), $this->expectCallableNever(), $this->expectCallableNever()); } + + public function testCtorThrowsForInvalidLoop() + { + if (method_exists($this, 'expectException')) { + // PHPUnit 5.2+ + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface'); + } else { + // legacy PHPUnit + $this->setExpectedException('InvalidArgumentException', 'Argument #1 ($loop) expected null|React\EventLoop\LoopInterface'); + } + + new Client('loop'); + } + + public function testCtorThrowsForInvalidMulticast() + { + if (method_exists($this, 'expectException')) { + // PHPUnit 5.2+ + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage('Argument #2 ($multicast) expected null|Clue\React\Multicast\Factory'); + } else { + // legacy PHPUnit + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($multicast) expected null|Clue\React\Multicast\Factory'); + } + + new Client(null, 'multicast'); + } }