From eef0298ae13bab89c9e70e8f9360529938f9d0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Mon, 28 Jun 2021 14:27:52 +0200 Subject: [PATCH] Add static Loop methods --- README.md | 200 ++++++++++++++++++++----- examples/01-timers.php | 6 +- examples/02-periodic.php | 8 +- examples/03-ticks.php | 6 +- examples/04-signals.php | 6 +- examples/11-consume-stdin.php | 6 +- examples/12-generate-yes.php | 6 +- examples/13-http-client-blocking.php | 6 +- examples/14-http-client-async.php | 14 +- examples/21-http-server.php | 10 +- examples/91-benchmark-ticks.php | 4 +- examples/92-benchmark-timers.php | 4 +- examples/93-benchmark-ticks-delay.php | 4 +- examples/94-benchmark-timers-delay.php | 4 +- examples/95-benchmark-memory.php | 15 +- src/Loop.php | 150 +++++++++++++++++++ tests/LoopTest.php | 152 +++++++++++++++++++ 17 files changed, 515 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index 853766b2..b5559f6c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ single [`run()`](#run) call that is controlled by the user. * [Quickstart example](#quickstart-example) * [Usage](#usage) * [Loop](#loop) + * [Loop methods](#loop-methods) * [get()](#get) * [Factory](#factory) * [create()](#create) @@ -52,88 +53,215 @@ use React\EventLoop\Loop; $server = stream_socket_server('tcp://127.0.0.1:8080'); stream_set_blocking($server, false); -Loop::get()->addReadStream($server, function ($server) { +Loop::addReadStream($server, function ($server) { $conn = stream_socket_accept($server); $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n"; - Loop::get()->addWriteStream($conn, function ($conn) use (&$data) { + Loop::addWriteStream($conn, function ($conn) use (&$data) { $written = fwrite($conn, $data); if ($written === strlen($data)) { fclose($conn); - Loop::get()->removeWriteStream($conn); + Loop::removeWriteStream($conn); } else { $data = substr($data, $written); } }); }); -Loop::get()->addPeriodicTimer(5, function () { +Loop::addPeriodicTimer(5, function () { $memory = memory_get_usage() / 1024; $formatted = number_format($memory, 3).'K'; echo "Current memory usage: {$formatted}\n"; }); -Loop::get()->run(); +Loop::run(); ``` See also the [examples](examples). ## Usage -Typical applications use a single event loop which is created at the beginning -and run at the end of the program. +As of `v1.2.0`, typical applications would use the [`Loop` object](#loop) +to use the currently active event loop instance like this: ```php -// [1] -$loop = React\EventLoop\Factory::create(); +use React\EventLoop\Loop; -// [2] -$loop->addPeriodicTimer(1, function () { - echo "Tick\n"; +$timer = Loop::addPeriodicTimer(0.1, function () { + echo "Tick" . PHP_EOL; +}); +Loop::addTimer(1.0, function () use ($timer) { + Loop::cancelTimer($timer); + echo 'Done' . PHP_EOL; }); -$stream = new React\Stream\ReadableResourceStream( - fopen('file.txt', 'r'), - $loop -); +Loop::run(); +``` + +As an alternative, you can also explicitly create an event loop instance at the +beginning, reuse it throughout your program and finally run it at the end of the +program like this: + +```php +$loop = React\EventLoop\Loop::get(); // or deprecated React\EventLoop\Factory::create(); + +$timer = $loop->addPeriodicTimer(0.1, function () { + echo "Tick" . PHP_EOL; +}); +$loop->addTimer(1.0, function () use ($loop, $timer) { + $loop->cancelTimer($timer); + echo 'Done' . PHP_EOL; +}); -// [3] $loop->run(); ``` -1. The loop instance is created at the beginning of the program. A convenience - factory [`React\EventLoop\Factory::create()`](#create) is provided by this library which - picks the best available [loop implementation](#loop-implementations). -2. The loop instance is used directly or passed to library and application code. - In this example, a periodic timer is registered with the event loop which - simply outputs `Tick` every second and a - [readable stream](https://github.com/reactphp/stream#readableresourcestream) - is created by using ReactPHP's - [stream component](https://github.com/reactphp/stream) for demonstration - purposes. -3. The loop is run with a single [`$loop->run()`](#run) call at the end of the program. +While the former is more concise, the latter is more explicit. +In both cases, the program would perform the exact same steps. + +1. The event loop instance is created at the beginning of the program. This is + implicitly done the first time you call the [`Loop` class](#loop) or + explicitly when using the deprecated [`Factory::create() method`](#create) + (or manually instantiating any of the [loop implementation](#loop-implementations)). +2. The event loop is used directly or passed as an instance to library and + application code. In this example, a periodic timer is registered with the + event loop which simply outputs `Tick` every fraction of a second until another + timer stops the periodic timer after a second. +3. The event loop is run at the end of the program with a single [`run()`](#run) + call at the end of the program. + +As of `v1.2.0`, we highly recommend using the [`Loop` class](#loop). +The explicit loop instructions are still valid and may still be useful in some +applications, especially for a transition period towards the more concise style. ### Loop The `Loop` class exists as a convenient global accessor for the event loop. -#### get() +#### Loop methods -The `get(): LoopInterface` method is the preferred way to get and use the event loop. With -it there is no need to always pass the loop around anymore. +The `Loop` class provides all methods that exist on the [`LoopInterface`](#loopinterface) +as static methods: + +* [run()](#run) +* [stop()](#stop) +* [addTimer()](#addtimer) +* [addPeriodicTimer()](#addperiodictimer) +* [cancelTimer()](#canceltimer) +* [futureTick()](#futuretick) +* [addSignal()](#addsignal) +* [removeSignal()](#removesignal) +* [addReadStream()](#addreadstream) +* [addWriteStream()](#addwritestream) +* [removeReadStream()](#removereadstream) +* [removeWriteStream()](#removewritestream) + +If you're working with the event loop in your application code, it's often +easiest to directly interface with the static methods defined on the `Loop` class +like this: ```php use React\EventLoop\Loop; -Loop::get()->addTimer(0.02, function () { - echo 'World!'; +$timer = Loop::addPeriodicTimer(0.1, function () { + echo 'tick!' . PHP_EOL; }); -Loop::get()->addTimer(0.01, function () { - echo 'Hello '; + +Loop::addTimer(1.0, function () use ($timer) { + Loop::cancelTimer($timer); + echo 'Done' . PHP_EOL; }); -Loop::get()->run(); +Loop::run(); ``` +On the other hand, if you're familiar with object-oriented programming (OOP) and +dependency injection (DI), you may want to inject an event loop instance and +invoke instance methods on the `LoopInterface` like this: + +```php +use React\EventLoop\Loop; +use React\EventLoop\LoopInterface; + +class Greeter +{ + private $loop; + + public function __construct(LoopInterface $loop) + { + $this->loop = $loop; + } + + public function greet(string $name) + { + $this->loop->addTimer(1.0, function () use ($name) { + echo 'Hello ' . $name . '!' . PHP_EOL; + }); + } +} + +$greeter = new Greeter(Loop::get()); +$greeter->greet('Alice'); +$greeter->greet('Bob'); + +Loop::run(); +``` + +Each static method call will be forwarded as-is to the underlying event loop +instance by using the [`Loop::get()`](#get) call internally. +See [`LoopInterface`](#loopinterface) for more details about available methods. + +#### get() + +The `get(): LoopInterface` method can be used to +get the currently active event loop instance. + +This method will always return the same event loop instance throughout the +lifetime of your application. + +```php +use React\EventLoop\Loop; +use React\EventLoop\LoopInterface; + +$loop = Loop::get(); + +assert($loop instanceof LoopInterface); +assert($loop === Loop::get()); +``` + +This is particularly useful if you're using object-oriented programming (OOP) +and dependency injection (DI). In this case, you may want to inject an event +loop instance and invoke instance methods on the `LoopInterface` like this: + +```php +use React\EventLoop\Loop; +use React\EventLoop\LoopInterface; + +class Greeter +{ + private $loop; + + public function __construct(LoopInterface $loop) + { + $this->loop = $loop; + } + + public function greet(string $name) + { + $this->loop->addTimer(1.0, function () use ($name) { + echo 'Hello ' . $name . '!' . PHP_EOL; + }); + } +} + +$greeter = new Greeter(Loop::get()); +$greeter->greet('Alice'); +$greeter->greet('Bob'); + +Loop::run(); +``` + +See [`LoopInterface`](#loopinterface) for more details about available methods. + ### Factory The `Factory` class exists as a convenient way to pick the best available diff --git a/examples/01-timers.php b/examples/01-timers.php index 5be2f3e2..a7bf3945 100644 --- a/examples/01-timers.php +++ b/examples/01-timers.php @@ -4,12 +4,12 @@ require __DIR__ . '/../vendor/autoload.php'; -Loop::get()->addTimer(0.8, function () { +Loop::addTimer(0.8, function () { echo 'world!' . PHP_EOL; }); -Loop::get()->addTimer(0.3, function () { +Loop::addTimer(0.3, function () { echo 'hello '; }); -Loop::get()->run(); +Loop::run(); diff --git a/examples/02-periodic.php b/examples/02-periodic.php index 6f549055..0604f846 100644 --- a/examples/02-periodic.php +++ b/examples/02-periodic.php @@ -4,13 +4,13 @@ require __DIR__ . '/../vendor/autoload.php'; -$timer = Loop::get()->addPeriodicTimer(0.1, function () { +$timer = Loop::addPeriodicTimer(0.1, function () { echo 'tick!' . PHP_EOL; }); -Loop::get()->addTimer(1.0, function () use ($timer) { - Loop::get()->cancelTimer($timer); +Loop::addTimer(1.0, function () use ($timer) { + Loop::cancelTimer($timer); echo 'Done' . PHP_EOL; }); -Loop::get()->run(); +Loop::run(); diff --git a/examples/03-ticks.php b/examples/03-ticks.php index 0eee59bd..4b2077da 100644 --- a/examples/03-ticks.php +++ b/examples/03-ticks.php @@ -4,12 +4,12 @@ require __DIR__ . '/../vendor/autoload.php'; -Loop::get()->futureTick(function () { +Loop::futureTick(function () { echo 'b'; }); -Loop::get()->futureTick(function () { +Loop::futureTick(function () { echo 'c'; }); echo 'a'; -Loop::get()->run(); +Loop::run(); diff --git a/examples/04-signals.php b/examples/04-signals.php index dc7e0293..ceca3521 100644 --- a/examples/04-signals.php +++ b/examples/04-signals.php @@ -9,11 +9,11 @@ exit(1); } -Loop::get()->addSignal(SIGINT, $func = function ($signal) use (&$func) { +Loop::addSignal(SIGINT, $func = function ($signal) use (&$func) { echo 'Signal: ', (string)$signal, PHP_EOL; - Loop::get()->removeSignal(SIGINT, $func); + Loop::removeSignal(SIGINT, $func); }); echo 'Listening for SIGINT. Use "kill -SIGINT ' . getmypid() . '" or CTRL+C' . PHP_EOL; -Loop::get()->run(); +Loop::run(); diff --git a/examples/11-consume-stdin.php b/examples/11-consume-stdin.php index 146a55eb..f567d84a 100644 --- a/examples/11-consume-stdin.php +++ b/examples/11-consume-stdin.php @@ -11,12 +11,12 @@ // read everything from STDIN and report number of bytes // for illustration purposes only, should use react/stream instead -Loop::get()->addReadStream(STDIN, function ($stream) { +Loop::addReadStream(STDIN, function ($stream) { $chunk = fread($stream, 64 * 1024); // reading nothing means we reached EOF if ($chunk === '') { - Loop::get()->removeReadStream($stream); + Loop::removeReadStream($stream); stream_set_blocking($stream, true); fclose($stream); return; @@ -25,4 +25,4 @@ echo strlen($chunk) . ' bytes' . PHP_EOL; }); -Loop::get()->run(); +Loop::run(); diff --git a/examples/12-generate-yes.php b/examples/12-generate-yes.php index b0da7c60..4424b8ec 100644 --- a/examples/12-generate-yes.php +++ b/examples/12-generate-yes.php @@ -17,13 +17,13 @@ // write data to STDOUT whenever its write buffer accepts data // for illustrations purpose only, should use react/stream instead -Loop::get()->addWriteStream(STDOUT, function ($stdout) use (&$data) { +Loop::addWriteStream(STDOUT, function ($stdout) use (&$data) { // try to write data $r = fwrite($stdout, $data); // nothing could be written despite being writable => closed if ($r === 0) { - Loop::get()->removeWriteStream($stdout); + Loop::removeWriteStream($stdout); fclose($stdout); stream_set_blocking($stdout, true); fwrite(STDERR, 'Stopped because STDOUT closed' . PHP_EOL); @@ -38,4 +38,4 @@ } }); -Loop::get()->run(); +Loop::run(); diff --git a/examples/13-http-client-blocking.php b/examples/13-http-client-blocking.php index eb34b24d..efd8cc86 100644 --- a/examples/13-http-client-blocking.php +++ b/examples/13-http-client-blocking.php @@ -16,13 +16,13 @@ fwrite($stream, "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n"); // wait for HTTP response -Loop::get()->addReadStream($stream, function ($stream) { +Loop::addReadStream($stream, function ($stream) { $chunk = fread($stream, 64 * 1024); // reading nothing means we reached EOF if ($chunk === '') { echo '[END]' . PHP_EOL; - Loop::get()->removeReadStream($stream); + Loop::removeReadStream($stream); fclose($stream); return; } @@ -30,4 +30,4 @@ echo $chunk; }); -Loop::get()->run(); +Loop::run(); diff --git a/examples/14-http-client-async.php b/examples/14-http-client-async.php index 2e3e7e35..ceed3ec7 100644 --- a/examples/14-http-client-async.php +++ b/examples/14-http-client-async.php @@ -23,14 +23,14 @@ // print progress every 10ms echo 'Connecting'; -$timer = Loop::get()->addPeriodicTimer(0.01, function () { +$timer = Loop::addPeriodicTimer(0.01, function () { echo '.'; }); // wait for connection success/error -Loop::get()->addWriteStream($stream, function ($stream) use ($timer) { - Loop::get()->removeWriteStream($stream); - Loop::get()->cancelTimer($timer); +Loop::addWriteStream($stream, function ($stream) use ($timer) { + Loop::removeWriteStream($stream); + Loop::cancelTimer($timer); // check for socket error (connection rejected) if (stream_socket_get_name($stream, true) === false) { @@ -44,13 +44,13 @@ fwrite($stream, "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n"); // wait for HTTP response - Loop::get()->addReadStream($stream, function ($stream) { + Loop::addReadStream($stream, function ($stream) { $chunk = fread($stream, 64 * 1024); // reading nothing means we reached EOF if ($chunk === '') { echo '[END]' . PHP_EOL; - Loop::get()->removeReadStream($stream); + Loop::removeReadStream($stream); fclose($stream); return; } @@ -59,4 +59,4 @@ }); }); -Loop::get()->run(); +Loop::run(); diff --git a/examples/21-http-server.php b/examples/21-http-server.php index 4e238158..e000eb51 100644 --- a/examples/21-http-server.php +++ b/examples/21-http-server.php @@ -13,24 +13,24 @@ stream_set_blocking($server, false); // wait for incoming connections on server socket -Loop::get()->addReadStream($server, function ($server) { +Loop::addReadStream($server, function ($server) { $conn = stream_socket_accept($server); $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n"; - Loop::get()->addWriteStream($conn, function ($conn) use (&$data) { + Loop::addWriteStream($conn, function ($conn) use (&$data) { $written = fwrite($conn, $data); if ($written === strlen($data)) { fclose($conn); - Loop::get()->removeWriteStream($conn); + Loop::removeWriteStream($conn); } else { $data = substr($data, $written); } }); }); -Loop::get()->addPeriodicTimer(5, function () { +Loop::addPeriodicTimer(5, function () { $memory = memory_get_usage() / 1024; $formatted = number_format($memory, 3).'K'; echo "Current memory usage: {$formatted}\n"; }); -Loop::get()->run(); +Loop::run(); diff --git a/examples/91-benchmark-ticks.php b/examples/91-benchmark-ticks.php index 8a768568..452abbac 100644 --- a/examples/91-benchmark-ticks.php +++ b/examples/91-benchmark-ticks.php @@ -7,7 +7,7 @@ $n = isset($argv[1]) ? (int)$argv[1] : 1000 * 100; for ($i = 0; $i < $n; ++$i) { - Loop::get()->futureTick(function () { }); + Loop::futureTick(function () { }); } -Loop::get()->run(); +Loop::run(); diff --git a/examples/92-benchmark-timers.php b/examples/92-benchmark-timers.php index 51cb9596..da381f16 100644 --- a/examples/92-benchmark-timers.php +++ b/examples/92-benchmark-timers.php @@ -7,7 +7,7 @@ $n = isset($argv[1]) ? (int)$argv[1] : 1000 * 100; for ($i = 0; $i < $n; ++$i) { - Loop::get()->addTimer(0, function () { }); + Loop::addTimer(0, function () { }); } -Loop::get()->run(); +Loop::run(); diff --git a/examples/93-benchmark-ticks-delay.php b/examples/93-benchmark-ticks-delay.php index b37cfbc2..ac5094f3 100644 --- a/examples/93-benchmark-ticks-delay.php +++ b/examples/93-benchmark-ticks-delay.php @@ -9,7 +9,7 @@ if ($ticks > 0) { --$ticks; //$loop->addTimer(0, $tick); - Loop::get()->futureTick($tick); + Loop::futureTick($tick); } else { echo 'done'; } @@ -17,4 +17,4 @@ $tick(); -Loop::get()->run(); +Loop::run(); diff --git a/examples/94-benchmark-timers-delay.php b/examples/94-benchmark-timers-delay.php index e8e380a2..eb4fc5cb 100644 --- a/examples/94-benchmark-timers-delay.php +++ b/examples/94-benchmark-timers-delay.php @@ -9,7 +9,7 @@ if ($ticks > 0) { --$ticks; //$loop->futureTick($tick); - Loop::get()->addTimer(0, $tick); + Loop::addTimer(0, $tick); } else { echo 'done'; } @@ -17,4 +17,4 @@ $tick(); -Loop::get()->run(); +Loop::run(); diff --git a/examples/95-benchmark-memory.php b/examples/95-benchmark-memory.php index 14d77872..06735bd2 100644 --- a/examples/95-benchmark-memory.php +++ b/examples/95-benchmark-memory.php @@ -7,7 +7,6 @@ * php 95-benchmark-memory.php -t 30 -l StreamSelect -r 10 */ -use React\EventLoop\Factory; use React\EventLoop\Loop; use React\EventLoop\LoopInterface; use React\EventLoop\TimerInterface; @@ -27,21 +26,21 @@ $runs = 0; if (5 < $t) { - Loop::get()->addTimer($t, function () { - Loop::get()->stop(); + Loop::addTimer($t, function () { + Loop::stop(); }); } -Loop::get()->addPeriodicTimer(0.001, function () use (&$runs) { +Loop::addPeriodicTimer(0.001, function () use (&$runs) { $runs++; - Loop::get()->addPeriodicTimer(1, function (TimerInterface $timer) { - Loop::get()->cancelTimer($timer); + Loop::addPeriodicTimer(1, function (TimerInterface $timer) { + Loop::cancelTimer($timer); }); }); -Loop::get()->addPeriodicTimer($r, function () use (&$runs) { +Loop::addPeriodicTimer($r, function () use (&$runs) { $kmem = round(memory_get_usage() / 1024); $kmemReal = round(memory_get_usage(true) / 1024); echo "Runs:\t\t\t$runs\n"; @@ -57,7 +56,7 @@ echo str_repeat('-', 50), "\n"; $beginTime = time(); -Loop::get()->run(); +Loop::run(); $endTime = time(); $timeTaken = $endTime - $beginTime; diff --git a/src/Loop.php b/src/Loop.php index e91291e3..fed27cba 100644 --- a/src/Loop.php +++ b/src/Loop.php @@ -47,4 +47,154 @@ public static function set(LoopInterface $loop) { self::$instance = $loop; } + + /** + * [Advanced] Register a listener to be notified when a stream is ready to read. + * + * @param resource $stream + * @param callable $listener + * @return void + * @throws \Exception + * @see LoopInterface::addReadStream() + */ + public static function addReadStream($stream, $listener) + { + self::get()->addReadStream($stream, $listener); + } + + /** + * [Advanced] Register a listener to be notified when a stream is ready to write. + * + * @param resource $stream + * @param callable $listener + * @return void + * @throws \Exception + * @see LoopInterface::addWriteStream() + */ + public static function addWriteStream($stream, $listener) + { + self::get()->addWriteStream($stream, $listener); + } + + /** + * Remove the read event listener for the given stream. + * + * @param resource $stream + * @return void + * @see LoopInterface::removeReadStream() + */ + public static function removeReadStream($stream) + { + self::get()->removeReadStream($stream); + } + + /** + * Remove the write event listener for the given stream. + * + * @param resource $stream + * @return void + * @see LoopInterface::removeWriteStream() + */ + public static function removeWriteStream($stream) + { + self::get()->removeWriteStream($stream); + } + + /** + * Enqueue a callback to be invoked once after the given interval. + * + * @param float $interval + * @param callable $callback + * @return TimerInterface + * @see LoopInterface::addTimer() + */ + public static function addTimer($interval, $callback) + { + return self::get()->addTimer($interval, $callback); + } + + /** + * Enqueue a callback to be invoked repeatedly after the given interval. + * + * @param float $interval + * @param callable $callback + * @return TimerInterface + * @see LoopInterface::addPeriodicTimer() + */ + public static function addPeriodicTimer($interval, $callback) + { + return self::get()->addPeriodicTimer($interval, $callback); + } + + /** + * Cancel a pending timer. + * + * @param TimerInterface $timer + * @return void + * @see LoopInterface::cancelTimer() + */ + public static function cancelTimer(TimerInterface $timer) + { + return self::get()->cancelTimer($timer); + } + + /** + * Schedule a callback to be invoked on a future tick of the event loop. + * + * @param callable $listener + * @return void + * @see LoopInterface::futureTick() + */ + public static function futureTick($listener) + { + self::get()->futureTick($listener); + } + + /** + * Register a listener to be notified when a signal has been caught by this process. + * + * @param int $signal + * @param callable $listener + * @return void + * @see LoopInterface::addSignal() + */ + public static function addSignal($signal, $listener) + { + self::get()->addSignal($signal, $listener); + } + + /** + * Removes a previously added signal listener. + * + * @param int $signal + * @param callable $listener + * @return void + * @see LoopInterface::removeSignal() + */ + public static function removeSignal($signal, $listener) + { + self::get()->removeSignal($signal, $listener); + } + + /** + * Run the event loop until there are no more tasks to perform. + * + * @return void + * @see LoopInterface::run() + */ + public static function run() + { + self::get()->run(); + } + + /** + * Instruct a running event loop to stop. + * + * @return void + * @see LoopInterface::stop() + */ + public static function stop() + { + self::get()->stop(); + } } diff --git a/tests/LoopTest.php b/tests/LoopTest.php index a653035a..f3a13d34 100644 --- a/tests/LoopTest.php +++ b/tests/LoopTest.php @@ -49,6 +49,158 @@ public function numberOfTests() return array(array(), array(), array()); } + public function testStaticAddReadStreamCallsAddReadStreamOnLoopInstance() + { + $stream = tmpfile(); + $listener = function () { }; + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addReadStream')->with($stream, $listener); + + Loop::set($loop); + + Loop::addReadStream($stream, $listener); + } + + public function testStaticAddWriteStreamCallsAddWriteStreamOnLoopInstance() + { + $stream = tmpfile(); + $listener = function () { }; + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addWriteStream')->with($stream, $listener); + + Loop::set($loop); + + Loop::addWriteStream($stream, $listener); + } + + public function testStaticRemoveReadStreamCallsRemoveReadStreamOnLoopInstance() + { + $stream = tmpfile(); + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('removeReadStream')->with($stream); + + Loop::set($loop); + + Loop::removeReadStream($stream); + } + + public function testStaticRemoveWriteStreamCallsRemoveWriteStreamOnLoopInstance() + { + $stream = tmpfile(); + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('removeWriteStream')->with($stream); + + Loop::set($loop); + + Loop::removeWriteStream($stream); + } + + public function testStaticAddTimerCallsAddTimerOnLoopInstanceAndReturnsTimerInstance() + { + $interval = 1.0; + $callback = function () { }; + $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock(); + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addTimer')->with($interval, $callback)->willReturn($timer); + + Loop::set($loop); + + $ret = Loop::addTimer($interval, $callback); + + $this->assertSame($timer, $ret); + } + + public function testStaticAddPeriodicTimerCallsAddPeriodicTimerOnLoopInstanceAndReturnsTimerInstance() + { + $interval = 1.0; + $callback = function () { }; + $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock(); + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addPeriodicTimer')->with($interval, $callback)->willReturn($timer); + + Loop::set($loop); + + $ret = Loop::addPeriodicTimer($interval, $callback); + + $this->assertSame($timer, $ret); + } + + public function testStaticCancelTimerCallsCancelTimerOnLoopInstance() + { + $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock(); + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('cancelTimer')->with($timer); + + Loop::set($loop); + + Loop::cancelTimer($timer); + } + + public function testStaticFutureTickCallsFutureTickOnLoopInstance() + { + $listener = function () { }; + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('futureTick')->with($listener); + + Loop::set($loop); + + Loop::futureTick($listener); + } + + public function testStaticAddSignalCallsAddSignalOnLoopInstance() + { + $signal = 1; + $listener = function () { }; + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('addSignal')->with($signal, $listener); + + Loop::set($loop); + + Loop::addSignal($signal, $listener); + } + + public function testStaticRemoveSignalCallsRemoveSignalOnLoopInstance() + { + $signal = 1; + $listener = function () { }; + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('removeSignal')->with($signal, $listener); + + Loop::set($loop); + + Loop::removeSignal($signal, $listener); + } + + public function testStaticRunCallsRunOnLoopInstance() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('run')->with(); + + Loop::set($loop); + + Loop::run(); + } + + public function testStaticStopCallsStopOnLoopInstance() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop->expects($this->once())->method('stop')->with(); + + Loop::set($loop); + + Loop::stop(); + } + /** * @after * @before