diff --git a/composer.json b/composer.json index 02c184f..b1e1d25 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 b1ed777..8e46719 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 98b1d58..a5511ac 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 17c229d..08255ac 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 d0525c9..5a202d2 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 7d4111e..b24c556 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 b78dc3a..e987f5f 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 8cfc7bf..9d2599e 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 235761d..01b2b46 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 a20ea5a..9ef252f 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 627d60f..95f932c 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 cc46968..27b014d 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 7a97ae7..34b1fad 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 5301b3b..c4516a7 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 e81f4a9..c115a2b 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 a6ddcf2..6265618 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 f69e6cb..f3859cc 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 c7cee8e..cd53f75 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 fb6f871..8e5f138 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 8908d1c..0c930c8 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 fc218c4..eab5942 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 d7e314a..5bcd7a5 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 c148de4..26f28d9 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));