diff --git a/src/DuplexResourceStream.php b/src/DuplexResourceStream.php index 4da2139..d6de55c 100644 --- a/src/DuplexResourceStream.php +++ b/src/DuplexResourceStream.php @@ -38,7 +38,13 @@ final class DuplexResourceStream extends EventEmitter implements DuplexStreamInt private $closing = false; private $listening = false; - public function __construct($stream, LoopInterface $loop = null, $readChunkSize = null, WritableStreamInterface $buffer = null) + /** + * @param resource $stream + * @param ?LoopInterface $loop + * @param ?int $readChunkSize + * @param ?WritableStreamInterface $buffer + */ + public function __construct($stream, $loop = null, $readChunkSize = null, $buffer = null) { if (!\is_resource($stream) || \get_resource_type($stream) !== "stream") { throw new InvalidArgumentException('First parameter must be a valid stream resource'); @@ -56,6 +62,13 @@ public function __construct($stream, LoopInterface $loop = null, $readChunkSize throw new \RuntimeException('Unable to set stream resource to non-blocking mode'); } + 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 ($buffer !== null && !$buffer instanceof WritableStreamInterface) { // manual type check to support legacy PHP < 7.1 + throw new \InvalidArgumentException('Argument #4 ($buffer) expected null|React\Stream\WritableStreamInterface'); + } + // Use unbuffered read operations on the underlying stream resource. // Reading chunks from the stream may otherwise leave unread bytes in // PHP's stream buffers which some event loop implementations do not diff --git a/src/ReadableResourceStream.php b/src/ReadableResourceStream.php index 1b0b08c..823360a 100644 --- a/src/ReadableResourceStream.php +++ b/src/ReadableResourceStream.php @@ -40,7 +40,12 @@ final class ReadableResourceStream extends EventEmitter implements ReadableStrea private $closed = false; private $listening = false; - public function __construct($stream, LoopInterface $loop = null, $readChunkSize = null) + /** + * @param resource $stream + * @param ?LoopInterface $loop + * @param ?int $readChunkSize + */ + public function __construct($stream, $loop = null, $readChunkSize = null) { if (!\is_resource($stream) || \get_resource_type($stream) !== "stream") { throw new InvalidArgumentException('First parameter must be a valid stream resource'); @@ -58,6 +63,10 @@ public function __construct($stream, LoopInterface $loop = null, $readChunkSize throw new \RuntimeException('Unable to set stream resource to non-blocking mode'); } + 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'); + } + // Use unbuffered read operations on the underlying stream resource. // Reading chunks from the stream may otherwise leave unread bytes in // PHP's stream buffers which some event loop implementations do not diff --git a/src/WritableResourceStream.php b/src/WritableResourceStream.php index 1af16b1..e3a7e74 100644 --- a/src/WritableResourceStream.php +++ b/src/WritableResourceStream.php @@ -28,7 +28,13 @@ final class WritableResourceStream extends EventEmitter implements WritableStrea private $closed = false; private $data = ''; - public function __construct($stream, LoopInterface $loop = null, $writeBufferSoftLimit = null, $writeChunkSize = null) + /** + * @param resource $stream + * @param ?LoopInterface $loop + * @param ?int $writeBufferSoftLimit + * @param ?int $writeChunkSize + */ + public function __construct($stream, $loop = null, $writeBufferSoftLimit = null, $writeChunkSize = null) { if (!\is_resource($stream) || \get_resource_type($stream) !== "stream") { throw new \InvalidArgumentException('First parameter must be a valid stream resource'); @@ -46,6 +52,10 @@ public function __construct($stream, LoopInterface $loop = null, $writeBufferSof throw new \RuntimeException('Unable to set stream resource to non-blocking mode'); } + 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->stream = $stream; $this->loop = $loop ?: Loop::get(); $this->softLimit = ($writeBufferSoftLimit === null) ? 65536 : (int)$writeBufferSoftLimit; diff --git a/tests/DuplexResourceStreamTest.php b/tests/DuplexResourceStreamTest.php index e61f14b..cc72f69 100644 --- a/tests/DuplexResourceStreamTest.php +++ b/tests/DuplexResourceStreamTest.php @@ -106,6 +106,22 @@ public function testConstructorThrowsExceptionIfStreamDoesNotSupportNonBlocking( new DuplexResourceStream($stream, $loop); } + public function testContructorThrowsExceptionForInvalidLoop() + { + $stream = fopen('php://temp', 'r+'); + + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new DuplexResourceStream($stream, 42); + } + + public function testContructorThrowsExceptionForInvalidBuffer() + { + $stream = fopen('php://temp', 'r+'); + + $this->setExpectedException('InvalidArgumentException', 'Argument #4 ($buffer) expected null|React\Stream\WritableStreamInterface'); + new DuplexResourceStream($stream, null, null, 42); + } + /** * @covers React\Stream\DuplexResourceStream::__construct * @doesNotPerformAssertions diff --git a/tests/ReadableResourceStreamTest.php b/tests/ReadableResourceStreamTest.php index f534488..8385ab8 100644 --- a/tests/ReadableResourceStreamTest.php +++ b/tests/ReadableResourceStreamTest.php @@ -105,6 +105,13 @@ public function testConstructorThrowsExceptionIfStreamDoesNotSupportNonBlocking( new ReadableResourceStream($stream, $loop); } + public function testContructorThrowsExceptionForInvalidLoop() + { + $stream = fopen('php://temp', 'r+'); + + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new ReadableResourceStream($stream, 42); + } public function testCloseShouldEmitCloseEvent() { diff --git a/tests/WritableResourceStreamTest.php b/tests/WritableResourceStreamTest.php index 678db98..35c90db 100644 --- a/tests/WritableResourceStreamTest.php +++ b/tests/WritableResourceStreamTest.php @@ -103,6 +103,14 @@ public function testConstructorThrowsExceptionIfStreamDoesNotSupportNonBlocking( new WritableResourceStream($stream, $loop); } + public function testContructorThrowsExceptionForInvalidLoop() + { + $stream = fopen('php://temp', 'r+'); + + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new WritableResourceStream($stream, 42); + } + /** * @covers React\Stream\WritableResourceStream::write * @covers React\Stream\WritableResourceStream::handleWrite