Skip to content

Commit 649b5ae

Browse files
committed
Introduce a global loop
1 parent 67d845d commit 649b5ae

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed

src/GlobalLoop.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
namespace React\EventLoop;
4+
5+
final class GlobalLoop
6+
{
7+
/**
8+
* @var LoopInterface
9+
*/
10+
private static $loop;
11+
12+
private static $factory;
13+
14+
private static $didRun = false;
15+
16+
public static function setFactory(callable $factory = null)
17+
{
18+
if (self::$didRun) {
19+
throw new \LogicException(
20+
'Setting a factory after the global loop has been started is not allowed.'
21+
);
22+
}
23+
24+
self::$factory = $factory;
25+
}
26+
27+
/**
28+
* @return LoopInterface
29+
*/
30+
public static function get()
31+
{
32+
if (self::$loop) {
33+
return self::$loop;
34+
}
35+
36+
self::$loop = self::create();
37+
38+
self::$loop->futureTick(function () {
39+
self::$didRun = true;
40+
});
41+
42+
return self::$loop;
43+
}
44+
45+
public static function reset()
46+
{
47+
self::$loop = null;
48+
self::$didRun = false;
49+
}
50+
51+
/**
52+
* @return LoopInterface
53+
*/
54+
public static function create()
55+
{
56+
if (self::$factory) {
57+
return self::createFromCustomFactory(self::$factory);
58+
}
59+
60+
return Factory::create();
61+
}
62+
63+
private static function createFromCustomFactory(callable $factory)
64+
{
65+
$loop = call_user_func($factory);
66+
67+
if (!$loop instanceof LoopInterface) {
68+
throw new \LogicException(
69+
sprintf(
70+
'The GlobalLoop factory must return an instance of LoopInterface but returned %s.',
71+
is_object($loop) ? get_class($loop) : gettype($loop)
72+
)
73+
);
74+
}
75+
76+
return $loop;
77+
}
78+
}

tests/GlobalLoopTest.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
namespace React\Tests\EventLoop;
4+
5+
use React\EventLoop\GlobalLoop;
6+
7+
class GlobalLoopTest extends TestCase
8+
{
9+
public function tearDown()
10+
{
11+
GlobalLoop::reset();
12+
GlobalLoop::setFactory(null);
13+
}
14+
15+
public function testCreatesDefaultLoop()
16+
{
17+
$this->assertInstanceOf('React\EventLoop\LoopInterface', GlobalLoop::get());
18+
}
19+
20+
public function testSubsequentGetCallsReturnSameInstance()
21+
{
22+
$this->assertSame(GlobalLoop::get(), GlobalLoop::get());
23+
}
24+
25+
public function testSubsequentGetCallsReturnNotSameInstanceWhenResetting()
26+
{
27+
$loop = GlobalLoop::get();
28+
29+
GlobalLoop::reset();
30+
31+
$this->assertNotSame($loop, GlobalLoop::get());
32+
}
33+
34+
public function testCreatesLoopWithFactory()
35+
{
36+
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')
37+
->getMock();
38+
39+
$factory = $this->createCallableMock();
40+
$factory
41+
->expects($this->once())
42+
->method('__invoke')
43+
->will($this->returnValue($loop));
44+
45+
GlobalLoop::setFactory($factory);
46+
47+
$this->assertSame($loop, GlobalLoop::get());
48+
}
49+
50+
/**
51+
* @expectedException \LogicException
52+
* @expectedExceptionMessage The GlobalLoop factory must return an instance of LoopInterface but returned NULL.
53+
*/
54+
public function testThrowsExceptionWhenFactoryDoesNotReturnALoopInterface()
55+
{
56+
$factory = $this->createCallableMock();
57+
$factory
58+
->expects($this->once())
59+
->method('__invoke');
60+
61+
GlobalLoop::setFactory($factory);
62+
63+
GlobalLoop::get();
64+
}
65+
66+
/**
67+
* @expectedException \LogicException
68+
* @expectedExceptionMessage Setting a factory after the global loop has been started is not allowed.
69+
*/
70+
public function testThrowsExceptionWhenSettingAFactoryAfterLoopIsCreated()
71+
{
72+
GlobalLoop::get()->run();
73+
74+
GlobalLoop::setFactory(null);
75+
}
76+
}

0 commit comments

Comments
 (0)