From c52f792721bf70c0fb645293d8b991695399b5a1 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Sat, 11 Mar 2017 10:54:55 +0100 Subject: [PATCH 1/2] Introduce a global loop --- src/GlobalLoop.php | 78 ++++++++++++++++++++++++++++++++++++++++ tests/GlobalLoopTest.php | 76 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 src/GlobalLoop.php create mode 100644 tests/GlobalLoopTest.php diff --git a/src/GlobalLoop.php b/src/GlobalLoop.php new file mode 100644 index 00000000..f95b4f62 --- /dev/null +++ b/src/GlobalLoop.php @@ -0,0 +1,78 @@ +futureTick(function () { + self::$didRun = true; + }); + + return self::$loop; + } + + public static function reset() + { + self::$loop = null; + self::$didRun = false; + } + + /** + * @return LoopInterface + */ + public static function create() + { + if (self::$factory) { + return self::createFromCustomFactory(self::$factory); + } + + return Factory::create(); + } + + private static function createFromCustomFactory(callable $factory) + { + $loop = call_user_func($factory); + + if (!$loop instanceof LoopInterface) { + throw new \LogicException( + sprintf( + 'The GlobalLoop factory must return an instance of LoopInterface but returned %s.', + is_object($loop) ? get_class($loop) : gettype($loop) + ) + ); + } + + return $loop; + } +} diff --git a/tests/GlobalLoopTest.php b/tests/GlobalLoopTest.php new file mode 100644 index 00000000..4ec94729 --- /dev/null +++ b/tests/GlobalLoopTest.php @@ -0,0 +1,76 @@ +assertInstanceOf('React\EventLoop\LoopInterface', GlobalLoop::get()); + } + + public function testSubsequentGetCallsReturnSameInstance() + { + $this->assertSame(GlobalLoop::get(), GlobalLoop::get()); + } + + public function testSubsequentGetCallsReturnNotSameInstanceWhenResetting() + { + $loop = GlobalLoop::get(); + + GlobalLoop::reset(); + + $this->assertNotSame($loop, GlobalLoop::get()); + } + + public function testCreatesLoopWithFactory() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface') + ->getMock(); + + $factory = $this->createCallableMock(); + $factory + ->expects($this->once()) + ->method('__invoke') + ->will($this->returnValue($loop)); + + GlobalLoop::setFactory($factory); + + $this->assertSame($loop, GlobalLoop::get()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The GlobalLoop factory must return an instance of LoopInterface but returned NULL. + */ + public function testThrowsExceptionWhenFactoryDoesNotReturnALoopInterface() + { + $factory = $this->createCallableMock(); + $factory + ->expects($this->once()) + ->method('__invoke'); + + GlobalLoop::setFactory($factory); + + GlobalLoop::get(); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Setting a factory after the global loop has been started is not allowed. + */ + public function testThrowsExceptionWhenSettingAFactoryAfterLoopIsCreated() + { + GlobalLoop::get()->run(); + + GlobalLoop::setFactory(null); + } +} From 4d5e850f6bdfdd9e6a4de0807dfddc36ae7d7e00 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Sat, 11 Mar 2017 10:56:49 +0100 Subject: [PATCH 2/2] Automatically run global loop in a shutdown function --- src/GlobalLoop.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/GlobalLoop.php b/src/GlobalLoop.php index f95b4f62..d6aad5b3 100644 --- a/src/GlobalLoop.php +++ b/src/GlobalLoop.php @@ -12,6 +12,7 @@ final class GlobalLoop private static $factory; private static $didRun = false; + private static $disableRunOnShutdown = false; public static function setFactory(callable $factory = null) { @@ -24,6 +25,11 @@ public static function setFactory(callable $factory = null) self::$factory = $factory; } + public function disableRunOnShutdown() + { + self::$disableRunOnShutdown = true; + } + /** * @return LoopInterface */ @@ -33,6 +39,14 @@ public static function get() return self::$loop; } + register_shutdown_function(function () { + if (self::$disableRunOnShutdown || self::$didRun || !self::$loop) { + return; + } + + self::$loop->run(); + }); + self::$loop = self::create(); self::$loop->futureTick(function () {