From 8cc748ec05815cc659ee8d8cb5334fc775c29f8b Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Sun, 12 Feb 2017 00:09:35 +0100 Subject: [PATCH 1/3] Add a global loop with accessor functions --- README.md | 16 ++--- composer.json | 3 +- src/GlobalLoop.php | 57 ++++++++++++++++ src/functions.php | 143 +++++++++++++++++++++++++++++++++++++++ tests/FunctionTest.php | 139 +++++++++++++++++++++++++++++++++++++ tests/GlobalLoopTest.php | 80 ++++++++++++++++++++++ 6 files changed, 426 insertions(+), 12 deletions(-) create mode 100644 src/GlobalLoop.php create mode 100644 src/functions.php create mode 100644 tests/FunctionTest.php create mode 100644 tests/GlobalLoopTest.php diff --git a/README.md b/README.md index cb027228..e4c89d87 100644 --- a/README.md +++ b/README.md @@ -40,33 +40,27 @@ All of the loops support these features: Here is an async HTTP server built with just the event loop. ```php - $loop = React\EventLoop\Factory::create(); - $server = stream_socket_server('tcp://127.0.0.1:8080'); stream_set_blocking($server, 0); - $loop->addReadStream($server, function ($server) use ($loop) { + React\EventLoop\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) { + React\EventLoop\addWriteStream($conn, function ($conn) use (&$data) { $written = fwrite($conn, $data); if ($written === strlen($data)) { fclose($conn); - $loop->removeStream($conn); + React\EventLoop\removeStream($conn); } else { $data = substr($data, $written); } }); }); - $loop->addPeriodicTimer(5, function () { + React\EventLoop\addPeriodicTimer(5, function () { $memory = memory_get_usage() / 1024; $formatted = number_format($memory, 3).'K'; echo "Current memory usage: {$formatted}\n"; }); - $loop->run(); + React\EventLoop\run(); ``` -**Note:** The factory is just for convenience. It tries to pick the best -available implementation. Libraries `SHOULD` allow the user to inject an -instance of the loop. They `MAY` use the factory when the user did not supply -a loop. diff --git a/composer.json b/composer.json index 5001a9c8..beb5cb00 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "autoload": { "psr-4": { "React\\EventLoop\\": "src" - } + }, + "files": ["src/functions.php"] } } diff --git a/src/GlobalLoop.php b/src/GlobalLoop.php new file mode 100644 index 00000000..f2286719 --- /dev/null +++ b/src/GlobalLoop.php @@ -0,0 +1,57 @@ +addReadStream($stream, $listener); +} + +/** + * Register a listener to the global event loop to be notified when a stream is + * ready to write. + * + * @param resource $stream The PHP stream resource to check. + * @param callable $listener Invoked when the stream is ready. + */ +function addWriteStream($stream, callable $listener) +{ + $loop = GlobalLoop::$loop ?: GlobalLoop::get(); + + $loop->addWriteStream($stream, $listener); +} + +/** + * Remove the read event listener from the global event loop for the given + * stream. + * + * @param resource $stream The PHP stream resource. + */ +function removeReadStream($stream) +{ + $loop = GlobalLoop::$loop ?: GlobalLoop::get(); + + $loop->removeReadStream($stream); +} + +/** + * Remove the write event listener from the global event loop for the given + * stream. + * + * @param resource $stream The PHP stream resource. + */ +function removeWriteStream($stream) +{ + $loop = GlobalLoop::$loop ?: GlobalLoop::get(); + + $loop->removeWriteStream($stream); +} + +/** + * Remove all listeners from the global event loop for the given stream. + * + * @param resource $stream The PHP stream resource. + */ +function removeStream($stream) +{ + $loop = GlobalLoop::$loop ?: GlobalLoop::get(); + + $loop->removeStream($stream); +} + +/** + * Enqueue a callback to the global event loop to be invoked once after the + * given interval. + * + * The execution order of timers scheduled to execute at the same time is + * not guaranteed. + * + * @param int|float $interval The number of seconds to wait before execution. + * @param callable $callback The callback to invoke. + * + * @return TimerInterface + */ +function addTimer($interval, callable $callback) +{ + $loop = GlobalLoop::$loop ?: GlobalLoop::get(); + + return $loop->addTimer($interval, $callback); +} + +/** + * Enqueue a callback to the global event loop to be invoked repeatedly after + * the given interval. + * + * The execution order of timers scheduled to execute at the same time is + * not guaranteed. + * + * @param int|float $interval The number of seconds to wait before execution. + * @param callable $callback The callback to invoke. + * + * @return TimerInterface + */ +function addPeriodicTimer($interval, callable $callback) +{ + $loop = GlobalLoop::$loop ?: GlobalLoop::get(); + + return $loop->addPeriodicTimer($interval, $callback); +} + +/** + * Schedule a callback to be invoked on a future tick of the global event loop. + * + * Callbacks are guaranteed to be executed in the order they are enqueued. + * + * @param callable $listener The callback to invoke. + */ +function futureTick(callable $listener) +{ + $loop = GlobalLoop::$loop ?: GlobalLoop::get(); + + $loop->futureTick($listener); +} + +/** + * Run the global event loop until there are no more tasks to perform. + */ +function run() +{ + $loop = GlobalLoop::$loop ?: GlobalLoop::get(); + + $loop->run(); +} + +/** + * Instruct the running global event loop to stop. + */ +function stop() +{ + $loop = GlobalLoop::$loop ?: GlobalLoop::get(); + + $loop->stop(); +} diff --git a/tests/FunctionTest.php b/tests/FunctionTest.php new file mode 100644 index 00000000..0fe40a22 --- /dev/null +++ b/tests/FunctionTest.php @@ -0,0 +1,139 @@ +getMockBuilder('React\EventLoop\LoopInterface') + ->getMock(); + + self::$state = GlobalLoop::$loop; + GlobalLoop::$loop = $this->globalLoop = $globalLoop; + } + + public function tearDown() + { + $this->globalLoop = null; + + GlobalLoop::$loop = self::$state; + } + + public function createStream() + { + return fopen('php://temp', 'r+'); + } + + public function testAddReadStream() + { + $stream = $this->createStream(); + $listener = function() {}; + + $this->globalLoop + ->expects($this->once()) + ->method('addReadStream') + ->with($stream, $listener); + + EventLoop\addReadStream($stream, $listener); + } + + public function testAddWriteStream() + { + $stream = $this->createStream(); + $listener = function() {}; + + $this->globalLoop + ->expects($this->once()) + ->method('addWriteStream') + ->with($stream, $listener); + + EventLoop\addWriteStream($stream, $listener); + } + + public function testRemoveReadStream() + { + $stream = $this->createStream(); + + $this->globalLoop + ->expects($this->once()) + ->method('removeReadStream') + ->with($stream); + + EventLoop\removeReadStream($stream); + } + + public function testRemoveWriteStream() + { + $stream = $this->createStream(); + + $this->globalLoop + ->expects($this->once()) + ->method('removeWriteStream') + ->with($stream); + + EventLoop\removeWriteStream($stream); + } + + public function testRemoveStream() + { + $stream = $this->createStream(); + + $this->globalLoop + ->expects($this->once()) + ->method('removeStream') + ->with($stream); + + EventLoop\removeStream($stream); + } + + public function testAddTimer() + { + $interval = 1; + $listener = function() {}; + + $this->globalLoop + ->expects($this->once()) + ->method('addTimer') + ->with($interval, $listener); + + EventLoop\addTimer($interval, $listener); + } + + public function testAddPeriodicTimer() + { + $interval = 1; + $listener = function() {}; + + $this->globalLoop + ->expects($this->once()) + ->method('addPeriodicTimer') + ->with($interval, $listener); + + EventLoop\addPeriodicTimer($interval, $listener); + } + + public function testRun() + { + $this->globalLoop + ->expects($this->once()) + ->method('run'); + + EventLoop\run(); + } + + public function testStop() + { + $this->globalLoop + ->expects($this->once()) + ->method('stop'); + + EventLoop\stop(); + } +} diff --git a/tests/GlobalLoopTest.php b/tests/GlobalLoopTest.php new file mode 100644 index 00000000..3650d875 --- /dev/null +++ b/tests/GlobalLoopTest.php @@ -0,0 +1,80 @@ +assertNull(GlobalLoop::$loop); + + $this->assertInstanceOf('React\EventLoop\LoopInterface', GlobalLoop::get()); + } + + public function testCreatesCustomLoopWithFactory() + { + $this->assertNull(GlobalLoop::$loop); + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface') + ->getMock(); + + $factory = $this->createCallableMock(); + $factory + ->expects($this->once()) + ->method('__invoke') + ->will($this->returnValue($loop)); + + GlobalLoop::setFactory($factory); + + $this->assertInstanceOf('React\EventLoop\LoopInterface', GlobalLoop::get()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The GlobalLoop factory must return an instance of LoopInterface but returned NULL. + */ + public function testThrowsExceptionWhenFactoryDoesNotReturnALoopInterface() + { + $this->assertNull(GlobalLoop::$loop); + + $factory = $this->createCallableMock(); + $factory + ->expects($this->once()) + ->method('__invoke'); + + GlobalLoop::setFactory($factory); + + $this->assertInstanceOf('React\EventLoop\LoopInterface', GlobalLoop::get()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Setting a factory after the global loop has been created is not allowed. + */ + public function testThrowsExceptionWhenSettingAFactoryAfterLoopIsCreated() + { + $this->assertNull(GlobalLoop::$loop); + + GlobalLoop::get(); + + $this->assertNotNull(GlobalLoop::$loop); + + GlobalLoop::setFactory($this->expectCallableNever()); + } +} From 9b893c35c3697b2390329563f39ff221e6b25430 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Sun, 12 Feb 2017 00:12:24 +0100 Subject: [PATCH 2/3] Automatically run global loop --- README.md | 2 -- src/GlobalLoop.php | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e4c89d87..210af1c9 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,4 @@ Here is an async HTTP server built with just the event loop. $formatted = number_format($memory, 3).'K'; echo "Current memory usage: {$formatted}\n"; }); - - React\EventLoop\run(); ``` diff --git a/src/GlobalLoop.php b/src/GlobalLoop.php index f2286719..04c8c462 100644 --- a/src/GlobalLoop.php +++ b/src/GlobalLoop.php @@ -13,6 +13,8 @@ final class GlobalLoop private static $factory = ['React\EventLoop\Factory', 'create']; + private static $disableRunOnShutdown = false; + public static function setFactory(callable $factory) { if (self::$loop) { @@ -24,6 +26,11 @@ public static function setFactory(callable $factory) self::$factory = $factory; } + public function disableRunOnShutdown() + { + self::$disableRunOnShutdown = true; + } + /** * @return LoopInterface */ @@ -33,6 +40,14 @@ public static function get() return self::$loop; } + register_shutdown_function(function () { + if (self::$disableRunOnShutdown || !self::$loop) { + return; + } + + self::$loop->run(); + }); + return self::$loop = self::create(); } From 8f5d8adb0d771932f600de304e869126ff3b16ff Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Mon, 13 Feb 2017 19:49:42 +0100 Subject: [PATCH 3/3] Remove global run() and stop() functions --- src/functions.php | 20 -------------------- tests/FunctionTest.php | 18 ------------------ 2 files changed, 38 deletions(-) diff --git a/src/functions.php b/src/functions.php index af6e27fa..38f5c116 100644 --- a/src/functions.php +++ b/src/functions.php @@ -121,23 +121,3 @@ function futureTick(callable $listener) $loop->futureTick($listener); } - -/** - * Run the global event loop until there are no more tasks to perform. - */ -function run() -{ - $loop = GlobalLoop::$loop ?: GlobalLoop::get(); - - $loop->run(); -} - -/** - * Instruct the running global event loop to stop. - */ -function stop() -{ - $loop = GlobalLoop::$loop ?: GlobalLoop::get(); - - $loop->stop(); -} diff --git a/tests/FunctionTest.php b/tests/FunctionTest.php index 0fe40a22..55c5f02d 100644 --- a/tests/FunctionTest.php +++ b/tests/FunctionTest.php @@ -118,22 +118,4 @@ public function testAddPeriodicTimer() EventLoop\addPeriodicTimer($interval, $listener); } - - public function testRun() - { - $this->globalLoop - ->expects($this->once()) - ->method('run'); - - EventLoop\run(); - } - - public function testStop() - { - $this->globalLoop - ->expects($this->once()) - ->method('stop'); - - EventLoop\stop(); - } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy