From b6e704ab8efd0f22de9a6827f55a15a5e0e516d3 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 5 Apr 2021 12:58:37 +0200 Subject: [PATCH] Global event-loop accessor part four --- README.md | 36 ++++++++++++--- examples/01-timers.php | 10 ++-- examples/02-periodic.php | 12 ++--- examples/03-ticks.php | 10 ++-- examples/04-signals.php | 10 ++-- examples/11-consume-stdin.php | 10 ++-- examples/12-generate-yes.php | 10 ++-- examples/13-http-client-blocking.php | 10 ++-- examples/14-http-client-async.php | 17 ++++--- examples/21-http-server.php | 14 +++--- examples/91-benchmark-ticks.php | 8 ++-- examples/92-benchmark-timers.php | 8 ++-- examples/93-benchmark-ticks-delay.php | 10 ++-- examples/94-benchmark-timers-delay.php | 10 ++-- examples/95-benchmark-memory.php | 23 ++++----- src/Factory.php | 31 +++++++++++-- src/Loop.php | 50 ++++++++++++++++++++ tests/LoopTest.php | 64 ++++++++++++++++++++++++++ 18 files changed, 246 insertions(+), 97 deletions(-) create mode 100644 src/Loop.php create mode 100644 tests/LoopTest.php diff --git a/README.md b/README.md index cdff1c88..402309b1 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ single [`run()`](#run) call that is controlled by the user. * [Quickstart example](#quickstart-example) * [Usage](#usage) + * [Loop](#loop) + * [get()](#get) * [Factory](#factory) * [create()](#create) * [Loop implementations](#loop-implementations) @@ -45,32 +47,32 @@ single [`run()`](#run) call that is controlled by the user. Here is an async HTTP server built with just the event loop. ```php -$loop = React\EventLoop\Factory::create(); +use React\EventLoop\Loop; $server = stream_socket_server('tcp://127.0.0.1:8080'); stream_set_blocking($server, false); -$loop->addReadStream($server, function ($server) use ($loop) { +Loop::get()->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->addWriteStream($conn, function ($conn) use (&$data, $loop) { + Loop::get()->addWriteStream($conn, function ($conn) use (&$data) { $written = fwrite($conn, $data); if ($written === strlen($data)) { fclose($conn); - $loop->removeWriteStream($conn); + Loop::get()->removeWriteStream($conn); } else { $data = substr($data, $written); } }); }); -$loop->addPeriodicTimer(5, function () { +Loop::get()->addPeriodicTimer(5, function () { $memory = memory_get_usage() / 1024; $formatted = number_format($memory, 3).'K'; echo "Current memory usage: {$formatted}\n"; }); -$loop->run(); +Loop::get()->run(); ``` See also the [examples](examples). @@ -110,6 +112,28 @@ $loop->run(); purposes. 3. The loop is run with a single [`$loop->run()`](#run) call at the end of the program. +### Loop + +The `Loop` class exists as a convenient global accessor for the event loop. + +#### get() + +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. + +```php +use React\EventLoop\Loop; + +Loop::get()->addTimer(0.02, function () { + echo 'World!'; +}); +Loop::get()->addTimer(0.01, function () { + echo 'Hello '; +}); + +Loop::get()->run(); +``` + ### 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 e6107e46..5be2f3e2 100644 --- a/examples/01-timers.php +++ b/examples/01-timers.php @@ -1,15 +1,15 @@ addTimer(0.8, function () { +Loop::get()->addTimer(0.8, function () { echo 'world!' . PHP_EOL; }); -$loop->addTimer(0.3, function () { +Loop::get()->addTimer(0.3, function () { echo 'hello '; }); -$loop->run(); +Loop::get()->run(); diff --git a/examples/02-periodic.php b/examples/02-periodic.php index 5e138a62..6f549055 100644 --- a/examples/02-periodic.php +++ b/examples/02-periodic.php @@ -1,16 +1,16 @@ addPeriodicTimer(0.1, function () { +$timer = Loop::get()->addPeriodicTimer(0.1, function () { echo 'tick!' . PHP_EOL; }); -$loop->addTimer(1.0, function () use ($loop, $timer) { - $loop->cancelTimer($timer); +Loop::get()->addTimer(1.0, function () use ($timer) { + Loop::get()->cancelTimer($timer); echo 'Done' . PHP_EOL; }); -$loop->run(); +Loop::get()->run(); diff --git a/examples/03-ticks.php b/examples/03-ticks.php index 3f36c6d4..0eee59bd 100644 --- a/examples/03-ticks.php +++ b/examples/03-ticks.php @@ -1,15 +1,15 @@ futureTick(function () { +Loop::get()->futureTick(function () { echo 'b'; }); -$loop->futureTick(function () { +Loop::get()->futureTick(function () { echo 'c'; }); echo 'a'; -$loop->run(); +Loop::get()->run(); diff --git a/examples/04-signals.php b/examples/04-signals.php index 90b68989..dc7e0293 100644 --- a/examples/04-signals.php +++ b/examples/04-signals.php @@ -1,5 +1,7 @@ addSignal(SIGINT, $func = function ($signal) use ($loop, &$func) { +Loop::get()->addSignal(SIGINT, $func = function ($signal) use (&$func) { echo 'Signal: ', (string)$signal, PHP_EOL; - $loop->removeSignal(SIGINT, $func); + Loop::get()->removeSignal(SIGINT, $func); }); echo 'Listening for SIGINT. Use "kill -SIGINT ' . getmypid() . '" or CTRL+C' . PHP_EOL; -$loop->run(); +Loop::get()->run(); diff --git a/examples/11-consume-stdin.php b/examples/11-consume-stdin.php index 2a772455..146a55eb 100644 --- a/examples/11-consume-stdin.php +++ b/examples/11-consume-stdin.php @@ -1,6 +1,6 @@ addReadStream(STDIN, function ($stream) use ($loop) { +Loop::get()->addReadStream(STDIN, function ($stream) { $chunk = fread($stream, 64 * 1024); // reading nothing means we reached EOF if ($chunk === '') { - $loop->removeReadStream($stream); + Loop::get()->removeReadStream($stream); stream_set_blocking($stream, true); fclose($stream); return; @@ -27,4 +25,4 @@ echo strlen($chunk) . ' bytes' . PHP_EOL; }); -$loop->run(); +Loop::get()->run(); diff --git a/examples/12-generate-yes.php b/examples/12-generate-yes.php index ebc2beb4..b0da7c60 100644 --- a/examples/12-generate-yes.php +++ b/examples/12-generate-yes.php @@ -1,5 +1,7 @@ addWriteStream(STDOUT, function ($stdout) use ($loop, &$data) { +Loop::get()->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->removeWriteStream($stdout); + Loop::get()->removeWriteStream($stdout); fclose($stdout); stream_set_blocking($stdout, true); fwrite(STDERR, 'Stopped because STDOUT closed' . PHP_EOL); @@ -38,4 +38,4 @@ } }); -$loop->run(); +Loop::get()->run(); diff --git a/examples/13-http-client-blocking.php b/examples/13-http-client-blocking.php index a2dde55c..eb34b24d 100644 --- a/examples/13-http-client-blocking.php +++ b/examples/13-http-client-blocking.php @@ -1,11 +1,9 @@ addReadStream($stream, function ($stream) use ($loop) { +Loop::get()->addReadStream($stream, function ($stream) { $chunk = fread($stream, 64 * 1024); // reading nothing means we reached EOF if ($chunk === '') { echo '[END]' . PHP_EOL; - $loop->removeReadStream($stream); + Loop::get()->removeReadStream($stream); fclose($stream); return; } @@ -32,4 +30,4 @@ echo $chunk; }); -$loop->run(); +Loop::get()->run(); diff --git a/examples/14-http-client-async.php b/examples/14-http-client-async.php index c82c9887..2e3e7e35 100644 --- a/examples/14-http-client-async.php +++ b/examples/14-http-client-async.php @@ -1,11 +1,10 @@ addPeriodicTimer(0.01, function () { +$timer = Loop::get()->addPeriodicTimer(0.01, function () { echo '.'; }); // wait for connection success/error -$loop->addWriteStream($stream, function ($stream) use ($loop, $timer) { - $loop->removeWriteStream($stream); - $loop->cancelTimer($timer); +Loop::get()->addWriteStream($stream, function ($stream) use ($timer) { + Loop::get()->removeWriteStream($stream); + Loop::get()->cancelTimer($timer); // check for socket error (connection rejected) if (stream_socket_get_name($stream, true) === false) { @@ -45,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->addReadStream($stream, function ($stream) use ($loop) { + Loop::get()->addReadStream($stream, function ($stream) { $chunk = fread($stream, 64 * 1024); // reading nothing means we reached EOF if ($chunk === '') { echo '[END]' . PHP_EOL; - $loop->removeReadStream($stream); + Loop::get()->removeReadStream($stream); fclose($stream); return; } @@ -60,4 +59,4 @@ }); }); -$loop->run(); +Loop::get()->run(); diff --git a/examples/21-http-server.php b/examples/21-http-server.php index 89520cec..4e238158 100644 --- a/examples/21-http-server.php +++ b/examples/21-http-server.php @@ -1,8 +1,8 @@ addReadStream($server, function ($server) use ($loop) { +Loop::get()->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->addWriteStream($conn, function ($conn) use (&$data, $loop) { + Loop::get()->addWriteStream($conn, function ($conn) use (&$data) { $written = fwrite($conn, $data); if ($written === strlen($data)) { fclose($conn); - $loop->removeWriteStream($conn); + Loop::get()->removeWriteStream($conn); } else { $data = substr($data, $written); } }); }); -$loop->addPeriodicTimer(5, function () { +Loop::get()->addPeriodicTimer(5, function () { $memory = memory_get_usage() / 1024; $formatted = number_format($memory, 3).'K'; echo "Current memory usage: {$formatted}\n"; }); -$loop->run(); +Loop::get()->run(); diff --git a/examples/91-benchmark-ticks.php b/examples/91-benchmark-ticks.php index 3f4690b3..8a768568 100644 --- a/examples/91-benchmark-ticks.php +++ b/examples/91-benchmark-ticks.php @@ -1,15 +1,13 @@ futureTick(function () { }); + Loop::get()->futureTick(function () { }); } -$loop->run(); +Loop::get()->run(); diff --git a/examples/92-benchmark-timers.php b/examples/92-benchmark-timers.php index e2e02e49..51cb9596 100644 --- a/examples/92-benchmark-timers.php +++ b/examples/92-benchmark-timers.php @@ -1,15 +1,13 @@ addTimer(0, function () { }); + Loop::get()->addTimer(0, function () { }); } -$loop->run(); +Loop::get()->run(); diff --git a/examples/93-benchmark-ticks-delay.php b/examples/93-benchmark-ticks-delay.php index 95ee78c6..b37cfbc2 100644 --- a/examples/93-benchmark-ticks-delay.php +++ b/examples/93-benchmark-ticks-delay.php @@ -1,17 +1,15 @@ 0) { --$ticks; //$loop->addTimer(0, $tick); - $loop->futureTick($tick); + Loop::get()->futureTick($tick); } else { echo 'done'; } @@ -19,4 +17,4 @@ $tick(); -$loop->run(); +Loop::get()->run(); diff --git a/examples/94-benchmark-timers-delay.php b/examples/94-benchmark-timers-delay.php index 2d6cfa25..e8e380a2 100644 --- a/examples/94-benchmark-timers-delay.php +++ b/examples/94-benchmark-timers-delay.php @@ -1,17 +1,15 @@ 0) { --$ticks; //$loop->futureTick($tick); - $loop->addTimer(0, $tick); + Loop::get()->addTimer(0, $tick); } else { echo 'done'; } @@ -19,4 +17,4 @@ $tick(); -$loop->run(); +Loop::get()->run(); diff --git a/examples/95-benchmark-memory.php b/examples/95-benchmark-memory.php index 084c4042..14d77872 100644 --- a/examples/95-benchmark-memory.php +++ b/examples/95-benchmark-memory.php @@ -8,6 +8,7 @@ */ use React\EventLoop\Factory; +use React\EventLoop\Loop; use React\EventLoop\LoopInterface; use React\EventLoop\TimerInterface; @@ -15,10 +16,10 @@ $args = getopt('t:l:r:'); $t = isset($args['t']) ? (int)$args['t'] : 0; -$loop = isset($args['l']) && class_exists('React\EventLoop\\' . $args['l'] . 'Loop') ? 'React\EventLoop\\' . $args['l'] . 'Loop' : Factory::create(); +$loop = isset($args['l']) && class_exists('React\EventLoop\\' . $args['l'] . 'Loop') ? 'React\EventLoop\\' . $args['l'] . 'Loop' : Loop::get(); if (!($loop instanceof LoopInterface)) { - $loop = new $loop(); + Loop::set(new $loop()); } $r = isset($args['r']) ? (int)$args['r'] : 2; @@ -26,21 +27,21 @@ $runs = 0; if (5 < $t) { - $loop->addTimer($t, function () use ($loop) { - $loop->stop(); + Loop::get()->addTimer($t, function () { + Loop::get()->stop(); }); } -$loop->addPeriodicTimer(0.001, function () use (&$runs, $loop) { +Loop::get()->addPeriodicTimer(0.001, function () use (&$runs) { $runs++; - $loop->addPeriodicTimer(1, function (TimerInterface $timer) use ($loop) { - $loop->cancelTimer($timer); + Loop::get()->addPeriodicTimer(1, function (TimerInterface $timer) { + Loop::get()->cancelTimer($timer); }); }); -$loop->addPeriodicTimer($r, function () use (&$runs) { +Loop::get()->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"; @@ -50,18 +51,18 @@ }); echo "PHP Version:\t\t", phpversion(), "\n"; -echo "Loop\t\t\t", get_class($loop), "\n"; +echo "Loop\t\t\t", get_class(Loop::get()), "\n"; echo "Time\t\t\t", date('r'), "\n"; echo str_repeat('-', 50), "\n"; $beginTime = time(); -$loop->run(); +Loop::get()->run(); $endTime = time(); $timeTaken = $endTime - $beginTime; echo "PHP Version:\t\t", phpversion(), "\n"; -echo "Loop\t\t\t", get_class($loop), "\n"; +echo "Loop\t\t\t", get_class(Loop::get()), "\n"; echo "Time\t\t\t", date('r'), "\n"; echo "Time taken\t\t", $timeTaken, " seconds\n"; echo "Runs per second\t\t", round($runs / $timeTaken), "\n"; diff --git a/src/Factory.php b/src/Factory.php index d1767bf8..1843d86e 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -19,21 +19,44 @@ final class Factory * * This method should usually only be called once at the beginning of the program. * + * @deprecated Use Loop::get instead + * * @return LoopInterface */ public static function create() + { + $loop = self::construct(); + + Loop::set($loop); + + return $loop; + } + + /** + * @internal + * @return LoopInterface + */ + private static function construct() { // @codeCoverageIgnoreStart if (\function_exists('uv_loop_new')) { // only use ext-uv on PHP 7 return new ExtUvLoop(); - } elseif (\class_exists('libev\EventLoop', false)) { + } + + if (\class_exists('libev\EventLoop', false)) { return new ExtLibevLoop(); - } elseif (\class_exists('EvLoop', false)) { + } + + if (\class_exists('EvLoop', false)) { return new ExtEvLoop(); - } elseif (\class_exists('EventBase', false)) { + } + + if (\class_exists('EventBase', false)) { return new ExtEventLoop(); - } elseif (\function_exists('event_base_new') && \PHP_MAJOR_VERSION === 5) { + } + + if (\function_exists('event_base_new') && \PHP_MAJOR_VERSION === 5) { // only use ext-libevent on PHP 5 for now return new ExtLibeventLoop(); } diff --git a/src/Loop.php b/src/Loop.php new file mode 100644 index 00000000..e91291e3 --- /dev/null +++ b/src/Loop.php @@ -0,0 +1,50 @@ + + */ + public function numberOfTests() + { + return array(array(), array(), array()); + } + + /** + * @after + * @before + */ + public function unsetLoopFromLoopAccessor() + { + $ref = new ReflectionClass('\React\EventLoop\Loop'); + $prop = $ref->getProperty('instance'); + $prop->setAccessible(true); + $prop->setValue(null); + $prop->setAccessible(false); + } +}