From b7a85ad981e7c6d1e21bad8dc4b0f4fe4a682178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Mon, 5 Jul 2021 10:36:41 +0200 Subject: [PATCH 1/2] Simplify usage by supporting new default loop --- README.md | 73 ++++++++++++++++++---------- composer.json | 2 +- examples/01-http.php | 6 +-- examples/02-https.php | 6 +-- examples/11-cat.php | 9 +--- examples/91-benchmark-throughput.php | 18 +++---- src/DuplexResourceStream.php | 7 ++- src/ReadableResourceStream.php | 6 ++- src/ReadableStreamInterface.php | 2 +- src/WritableResourceStream.php | 7 ++- src/WritableStreamInterface.php | 2 +- tests/DuplexResourceStreamTest.php | 13 +++++ tests/ReadableResourceStreamTest.php | 13 +++++ tests/WritableResourceStreamTest.php | 13 +++++ 14 files changed, 116 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index a0f3ebb..5284462 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,7 @@ Re-attach the data source after a previous `pause()`. ```php $stream->pause(); -$loop->addTimer(1.0, function () use ($stream) { +Loop::addTimer(1.0, function () use ($stream) { $stream->resume(); }); ``` @@ -737,7 +737,7 @@ stream in order to stop waiting for the stream to flush its final data. ```php $stream->end(); -$loop->addTimer(1.0, function () use ($stream) { +Loop::addTimer(1.0, function () use ($stream) { $stream->close(); }); ``` @@ -821,7 +821,7 @@ This can be used to represent a read-only resource like a file stream opened in readable mode or a stream such as `STDIN`: ```php -$stream = new ReadableResourceStream(STDIN, $loop); +$stream = new ReadableResourceStream(STDIN); $stream->on('data', function ($chunk) { echo $chunk; }); @@ -838,7 +838,7 @@ Otherwise, it will throw an `InvalidArgumentException`: ```php // throws InvalidArgumentException -$stream = new ReadableResourceStream(false, $loop); +$stream = new ReadableResourceStream(false); ``` See also the [`DuplexResourceStream`](#readableresourcestream) for read-and-write @@ -851,7 +851,7 @@ If this fails, it will throw a `RuntimeException`: ```php // throws RuntimeException on Windows -$stream = new ReadableResourceStream(STDIN, $loop); +$stream = new ReadableResourceStream(STDIN); ``` Once the constructor is called with a valid stream resource, this class will @@ -859,6 +859,12 @@ take care of the underlying stream resource. You SHOULD only use its public API and SHOULD NOT interfere with the underlying stream resource manually. +This class takes an optional `LoopInterface|null $loop` parameter that can be used to +pass the event loop instance to use for this object. You can use a `null` value +here in order to use the [default loop](https://github.com/reactphp/event-loop#loop). +This value SHOULD NOT be given unless you're sure you want to explicitly use a +given event loop instance. + This class takes an optional `int|null $readChunkSize` parameter that controls the maximum buffer size in bytes to read at once from the stream. You can use a `null` value here in order to apply its default value. @@ -874,7 +880,7 @@ This should read until the stream resource is not readable anymore mean it reached EOF. ```php -$stream = new ReadableResourceStream(STDIN, $loop, 8192); +$stream = new ReadableResourceStream(STDIN, null, 8192); ``` > PHP bug warning: If the PHP process has explicitly been started without a @@ -883,6 +889,9 @@ $stream = new ReadableResourceStream(STDIN, $loop, 8192); stream like `php test.php < /dev/null` instead of `php test.php <&-`. See [#81](https://github.com/reactphp/stream/issues/81) for more details. +> Changelog: As of v1.2.0 the `$loop` parameter can be omitted (or skipped with a + `null` value) to use the [default loop](https://github.com/reactphp/event-loop#loop). + ### WritableResourceStream The `WritableResourceStream` is a concrete implementation of the @@ -892,7 +901,7 @@ This can be used to represent a write-only resource like a file stream opened in writable mode or a stream such as `STDOUT` or `STDERR`: ```php -$stream = new WritableResourceStream(STDOUT, $loop); +$stream = new WritableResourceStream(STDOUT); $stream->write('hello!'); $stream->end(); ``` @@ -905,7 +914,7 @@ Otherwise, it will throw an `InvalidArgumentException`: ```php // throws InvalidArgumentException -$stream = new WritableResourceStream(false, $loop); +$stream = new WritableResourceStream(false); ``` See also the [`DuplexResourceStream`](#readableresourcestream) for read-and-write @@ -918,7 +927,7 @@ If this fails, it will throw a `RuntimeException`: ```php // throws RuntimeException on Windows -$stream = new WritableResourceStream(STDOUT, $loop); +$stream = new WritableResourceStream(STDOUT); ``` Once the constructor is called with a valid stream resource, this class will @@ -933,13 +942,19 @@ For this, it uses an in-memory buffer string to collect all outstanding writes. This buffer has a soft-limit applied which defines how much data it is willing to accept before the caller SHOULD stop sending further data. +This class takes an optional `LoopInterface|null $loop` parameter that can be used to +pass the event loop instance to use for this object. You can use a `null` value +here in order to use the [default loop](https://github.com/reactphp/event-loop#loop). +This value SHOULD NOT be given unless you're sure you want to explicitly use a +given event loop instance. + This class takes an optional `int|null $writeBufferSoftLimit` parameter that controls this maximum buffer size in bytes. You can use a `null` value here in order to apply its default value. This value SHOULD NOT be changed unless you know what you're doing. ```php -$stream = new WritableResourceStream(STDOUT, $loop, 8192); +$stream = new WritableResourceStream(STDOUT, null, 8192); ``` This class takes an optional `int|null $writeChunkSize` parameter that controls @@ -954,11 +969,14 @@ This can be `-1` which means "write everything available" to the underlying stream resource. ```php -$stream = new WritableResourceStream(STDOUT, $loop, null, 8192); +$stream = new WritableResourceStream(STDOUT, null, null, 8192); ``` See also [`write()`](#write) for more details. +> Changelog: As of v1.2.0 the `$loop` parameter can be omitted (or skipped with a + `null` value) to use the [default loop](https://github.com/reactphp/event-loop#loop). + ### DuplexResourceStream The `DuplexResourceStream` is a concrete implementation of the @@ -969,7 +987,7 @@ in read and write mode mode or a stream such as a TCP/IP connection: ```php $conn = stream_socket_client('tcp://google.com:80'); -$stream = new DuplexResourceStream($conn, $loop); +$stream = new DuplexResourceStream($conn); $stream->write('hello!'); $stream->end(); ``` @@ -982,7 +1000,7 @@ Otherwise, it will throw an `InvalidArgumentException`: ```php // throws InvalidArgumentException -$stream = new DuplexResourceStream(false, $loop); +$stream = new DuplexResourceStream(false); ``` See also the [`ReadableResourceStream`](#readableresourcestream) for read-only @@ -996,7 +1014,7 @@ If this fails, it will throw a `RuntimeException`: ```php // throws RuntimeException on Windows -$stream = new DuplexResourceStream(STDOUT, $loop); +$stream = new DuplexResourceStream(STDOUT); ``` Once the constructor is called with a valid stream resource, this class will @@ -1004,6 +1022,12 @@ take care of the underlying stream resource. You SHOULD only use its public API and SHOULD NOT interfere with the underlying stream resource manually. +This class takes an optional `LoopInterface|null $loop` parameter that can be used to +pass the event loop instance to use for this object. You can use a `null` value +here in order to use the [default loop](https://github.com/reactphp/event-loop#loop). +This value SHOULD NOT be given unless you're sure you want to explicitly use a +given event loop instance. + This class takes an optional `int|null $readChunkSize` parameter that controls the maximum buffer size in bytes to read at once from the stream. You can use a `null` value here in order to apply its default value. @@ -1020,7 +1044,7 @@ mean it reached EOF. ```php $conn = stream_socket_client('tcp://google.com:80'); -$stream = new DuplexResourceStream($conn, $loop, 8192); +$stream = new DuplexResourceStream($conn, null, 8192); ``` Any `write()` calls to this class will not be performed instantly, but will @@ -1040,12 +1064,15 @@ If you want to change the write buffer soft limit, you can pass an instance of ```php $conn = stream_socket_client('tcp://google.com:80'); -$buffer = new WritableResourceStream($conn, $loop, 8192); -$stream = new DuplexResourceStream($conn, $loop, null, $buffer); +$buffer = new WritableResourceStream($conn, null, 8192); +$stream = new DuplexResourceStream($conn, null, null, $buffer); ``` See also [`WritableResourceStream`](#writableresourcestream) for more details. +> Changelog: As of v1.2.0 the `$loop` parameter can be omitted (or skipped with a + `null` value) to use the [default loop](https://github.com/reactphp/event-loop#loop). + ### ThroughStream The `ThroughStream` implements the @@ -1123,8 +1150,8 @@ This is useful for some APIs which may require a single more convenient to work with a single stream instance like this: ```php -$stdin = new ReadableResourceStream(STDIN, $loop); -$stdout = new WritableResourceStream(STDOUT, $loop); +$stdin = new ReadableResourceStream(STDIN); +$stdout = new WritableResourceStream(STDOUT); $stdio = new CompositeStream($stdin, $stdout); @@ -1154,14 +1181,10 @@ The following example can be used to pipe the contents of a source file into a destination file without having to ever read the whole file into memory: ```php -$loop = new React\EventLoop\StreamSelectLoop; - -$source = new React\Stream\ReadableResourceStream(fopen('source.txt', 'r'), $loop); -$dest = new React\Stream\WritableResourceStream(fopen('destination.txt', 'w'), $loop); +$source = new React\Stream\ReadableResourceStream(fopen('source.txt', 'r')); +$dest = new React\Stream\WritableResourceStream(fopen('destination.txt', 'w')); $source->pipe($dest); - -$loop->run(); ``` > Note that this example uses `fopen()` for illustration purposes only. diff --git a/composer.json b/composer.json index aada0be..fd90e9f 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ ], "require": { "php": ">=5.3.8", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", + "react/event-loop": "dev-master#78f7f43 as 1.2.0", "evenement/evenement": "^3.0 || ^2.0 || ^1.0" }, "require-dev": { diff --git a/examples/01-http.php b/examples/01-http.php index 3687f7c..e70691d 100644 --- a/examples/01-http.php +++ b/examples/01-http.php @@ -11,7 +11,6 @@ // $ php examples/01-http.php // $ php examples/01-http.php reactphp.org -use React\EventLoop\Factory; use React\Stream\DuplexResourceStream; require __DIR__ . '/../vendor/autoload.php'; @@ -25,8 +24,7 @@ exit(1); } -$loop = Factory::create(); -$stream = new DuplexResourceStream($resource, $loop); +$stream = new DuplexResourceStream($resource); $stream->on('data', function ($chunk) { echo $chunk; @@ -36,5 +34,3 @@ }); $stream->write("GET / HTTP/1.0\r\nHost: $host\r\n\r\n"); - -$loop->run(); diff --git a/examples/02-https.php b/examples/02-https.php index 163f7c8..1d212da 100644 --- a/examples/02-https.php +++ b/examples/02-https.php @@ -11,7 +11,6 @@ // $ php examples/02-https.php // $ php examples/02-https.php reactphp.org -use React\EventLoop\Factory; use React\Stream\DuplexResourceStream; require __DIR__ . '/../vendor/autoload.php'; @@ -25,8 +24,7 @@ exit(1); } -$loop = Factory::create(); -$stream = new DuplexResourceStream($resource, $loop); +$stream = new DuplexResourceStream($resource); $stream->on('data', function ($chunk) { echo $chunk; @@ -36,5 +34,3 @@ }); $stream->write("GET / HTTP/1.0\r\nHost: $host\r\n\r\n"); - -$loop->run(); diff --git a/examples/11-cat.php b/examples/11-cat.php index 90fadc0..818ec8e 100644 --- a/examples/11-cat.php +++ b/examples/11-cat.php @@ -8,7 +8,6 @@ // $ php examples/11-cat.php < README.md // $ echo hello | php examples/11-cat.php -use React\EventLoop\Factory; use React\Stream\ReadableResourceStream; use React\Stream\WritableResourceStream; @@ -19,10 +18,6 @@ exit(1); } -$loop = Factory::create(); - -$stdout = new WritableResourceStream(STDOUT, $loop); -$stdin = new ReadableResourceStream(STDIN, $loop); +$stdout = new WritableResourceStream(STDOUT); +$stdin = new ReadableResourceStream(STDIN); $stdin->pipe($stdout); - -$loop->run(); diff --git a/examples/91-benchmark-throughput.php b/examples/91-benchmark-throughput.php index ecf695c..4203950 100644 --- a/examples/91-benchmark-throughput.php +++ b/examples/91-benchmark-throughput.php @@ -11,6 +11,8 @@ // $ php examples/91-benchmark-throughput.php -t 10 -o zero.bin // $ php examples/91-benchmark-throughput.php -t 60 -i zero.bin +use React\EventLoop\Loop; + require __DIR__ . '/../vendor/autoload.php'; if (DIRECTORY_SEPARATOR === '\\') { @@ -27,10 +29,8 @@ $if = str_replace('/dev/fd/', 'php://fd/', $if); $of = str_replace('/dev/fd/', 'php://fd/', $of); -$loop = new React\EventLoop\StreamSelectLoop(); - // setup information stream -$info = new React\Stream\WritableResourceStream(STDERR, $loop); +$info = new React\Stream\WritableResourceStream(STDERR); if (extension_loaded('xdebug')) { $info->write('NOTICE: The "xdebug" extension is loaded, this has a major impact on performance.' . PHP_EOL); } @@ -38,25 +38,23 @@ // setup input and output streams and pipe inbetween $fh = fopen($if, 'r'); -$in = new React\Stream\ReadableResourceStream($fh, $loop); -$out = new React\Stream\WritableResourceStream(fopen($of, 'w'), $loop); +$in = new React\Stream\ReadableResourceStream($fh); +$out = new React\Stream\WritableResourceStream(fopen($of, 'w')); $in->pipe($out); // stop input stream in $t seconds $start = microtime(true); -$timeout = $loop->addTimer($t, function () use ($in, &$bytes) { +$timeout = Loop::addTimer($t, function () use ($in) { $in->close(); }); // print stream position once stream closes -$in->on('close', function () use ($fh, $start, $loop, $timeout, $info) { +$in->on('close', function () use ($fh, $start, $timeout, $info) { $t = microtime(true) - $start; - $loop->cancelTimer($timeout); + Loop::cancelTimer($timeout); $bytes = ftell($fh); $info->write('read ' . $bytes . ' byte(s) in ' . round($t, 3) . ' second(s) => ' . round($bytes / 1024 / 1024 / $t, 1) . ' MiB/s' . PHP_EOL); $info->write('peak memory usage of ' . round(memory_get_peak_usage(true) / 1024 / 1024, 1) . ' MiB' . PHP_EOL); }); - -$loop->run(); diff --git a/src/DuplexResourceStream.php b/src/DuplexResourceStream.php index c8c1c50..c3163c6 100644 --- a/src/DuplexResourceStream.php +++ b/src/DuplexResourceStream.php @@ -3,12 +3,15 @@ namespace React\Stream; use Evenement\EventEmitter; +use React\EventLoop\Loop; use React\EventLoop\LoopInterface; use InvalidArgumentException; final class DuplexResourceStream extends EventEmitter implements DuplexStreamInterface { private $stream; + + /** @var LoopInterface */ private $loop; /** @@ -35,7 +38,7 @@ final class DuplexResourceStream extends EventEmitter implements DuplexStreamInt private $closing = false; private $listening = false; - public function __construct($stream, LoopInterface $loop, $readChunkSize = null, WritableStreamInterface $buffer = null) + public function __construct($stream, LoopInterface $loop = null, $readChunkSize = null, WritableStreamInterface $buffer = null) { if (!\is_resource($stream) || \get_resource_type($stream) !== "stream") { throw new InvalidArgumentException('First parameter must be a valid stream resource'); @@ -70,7 +73,7 @@ public function __construct($stream, LoopInterface $loop, $readChunkSize = null, } $this->stream = $stream; - $this->loop = $loop; + $this->loop = $loop ?: Loop::get(); $this->bufferSize = ($readChunkSize === null) ? 65536 : (int)$readChunkSize; $this->buffer = $buffer; diff --git a/src/ReadableResourceStream.php b/src/ReadableResourceStream.php index 9d9c006..1b0b08c 100644 --- a/src/ReadableResourceStream.php +++ b/src/ReadableResourceStream.php @@ -3,6 +3,7 @@ namespace React\Stream; use Evenement\EventEmitter; +use React\EventLoop\Loop; use React\EventLoop\LoopInterface; use InvalidArgumentException; @@ -13,6 +14,7 @@ final class ReadableResourceStream extends EventEmitter implements ReadableStrea */ private $stream; + /** @var LoopInterface */ private $loop; /** @@ -38,7 +40,7 @@ final class ReadableResourceStream extends EventEmitter implements ReadableStrea private $closed = false; private $listening = false; - public function __construct($stream, LoopInterface $loop, $readChunkSize = null) + public function __construct($stream, LoopInterface $loop = null, $readChunkSize = null) { if (!\is_resource($stream) || \get_resource_type($stream) !== "stream") { throw new InvalidArgumentException('First parameter must be a valid stream resource'); @@ -69,7 +71,7 @@ public function __construct($stream, LoopInterface $loop, $readChunkSize = null) } $this->stream = $stream; - $this->loop = $loop; + $this->loop = $loop ?: Loop::get(); $this->bufferSize = ($readChunkSize === null) ? 65536 : (int)$readChunkSize; $this->resume(); diff --git a/src/ReadableStreamInterface.php b/src/ReadableStreamInterface.php index 2b4c3d0..fa3d59c 100644 --- a/src/ReadableStreamInterface.php +++ b/src/ReadableStreamInterface.php @@ -236,7 +236,7 @@ public function pause(); * ```php * $stream->pause(); * - * $loop->addTimer(1.0, function () use ($stream) { + * Loop::addTimer(1.0, function () use ($stream) { * $stream->resume(); * }); * ``` diff --git a/src/WritableResourceStream.php b/src/WritableResourceStream.php index 952f39c..1af16b1 100644 --- a/src/WritableResourceStream.php +++ b/src/WritableResourceStream.php @@ -3,11 +3,14 @@ namespace React\Stream; use Evenement\EventEmitter; +use React\EventLoop\Loop; use React\EventLoop\LoopInterface; final class WritableResourceStream extends EventEmitter implements WritableStreamInterface { private $stream; + + /** @var LoopInterface */ private $loop; /** @@ -25,7 +28,7 @@ final class WritableResourceStream extends EventEmitter implements WritableStrea private $closed = false; private $data = ''; - public function __construct($stream, LoopInterface $loop, $writeBufferSoftLimit = null, $writeChunkSize = null) + public function __construct($stream, LoopInterface $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'); @@ -44,7 +47,7 @@ public function __construct($stream, LoopInterface $loop, $writeBufferSoftLimit } $this->stream = $stream; - $this->loop = $loop; + $this->loop = $loop ?: Loop::get(); $this->softLimit = ($writeBufferSoftLimit === null) ? 65536 : (int)$writeBufferSoftLimit; $this->writeChunkSize = ($writeChunkSize === null) ? -1 : (int)$writeChunkSize; } diff --git a/src/WritableStreamInterface.php b/src/WritableStreamInterface.php index 3bc932e..9b54680 100644 --- a/src/WritableStreamInterface.php +++ b/src/WritableStreamInterface.php @@ -330,7 +330,7 @@ public function end($data = null); * * ```php * $stream->end(); - * $loop->addTimer(1.0, function () use ($stream) { + * Loop::addTimer(1.0, function () use ($stream) { * $stream->close(); * }); * ``` diff --git a/tests/DuplexResourceStreamTest.php b/tests/DuplexResourceStreamTest.php index 2432efe..2edc07e 100644 --- a/tests/DuplexResourceStreamTest.php +++ b/tests/DuplexResourceStreamTest.php @@ -20,6 +20,19 @@ public function testConstructor() new DuplexResourceStream($stream, $loop); } + public function testConstructWithoutLoopAssignsLoopAutomatically() + { + $resource = fopen('php://temp', 'r+'); + + $stream = new DuplexResourceStream($resource); + + $ref = new \ReflectionProperty($stream, 'loop'); + $ref->setAccessible(true); + $loop = $ref->getValue($stream); + + $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); + } + /** * @covers React\Stream\DuplexResourceStream::__construct * @doesNotPerformAssertions diff --git a/tests/ReadableResourceStreamTest.php b/tests/ReadableResourceStreamTest.php index 30503a6..f534488 100644 --- a/tests/ReadableResourceStreamTest.php +++ b/tests/ReadableResourceStreamTest.php @@ -19,6 +19,19 @@ public function testConstructor() new ReadableResourceStream($stream, $loop); } + public function testConstructWithoutLoopAssignsLoopAutomatically() + { + $resource = fopen('php://temp', 'r+'); + + $stream = new ReadableResourceStream($resource); + + $ref = new \ReflectionProperty($stream, 'loop'); + $ref->setAccessible(true); + $loop = $ref->getValue($stream); + + $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); + } + /** * @covers React\Stream\ReadableResourceStream::__construct * @doesNotPerformAssertions diff --git a/tests/WritableResourceStreamTest.php b/tests/WritableResourceStreamTest.php index f0a3b42..38d4259 100644 --- a/tests/WritableResourceStreamTest.php +++ b/tests/WritableResourceStreamTest.php @@ -19,6 +19,19 @@ public function testConstructor() new WritableResourceStream($stream, $loop); } + public function testConstructWithoutLoopAssignsLoopAutomatically() + { + $resource = fopen('php://temp', 'r+'); + + $stream = new WritableResourceStream($resource); + + $ref = new \ReflectionProperty($stream, 'loop'); + $ref->setAccessible(true); + $loop = $ref->getValue($stream); + + $this->assertInstanceOf('React\EventLoop\LoopInterface', $loop); + } + /** * @covers React\Stream\WritableResourceStream::__construct * @doesNotPerformAssertions From e617d632943bc8022950bcd53b8ed23c4edf4b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Mon, 5 Jul 2021 10:42:17 +0200 Subject: [PATCH 2/2] Update to stable reactphp/event-loop v1.2.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index fd90e9f..b235f5a 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ ], "require": { "php": ">=5.3.8", - "react/event-loop": "dev-master#78f7f43 as 1.2.0", + "react/event-loop": "^1.2", "evenement/evenement": "^3.0 || ^2.0 || ^1.0" }, "require-dev": {