Skip to content

Commit

Permalink
Introduce a global loop
Browse files Browse the repository at this point in the history
  • Loading branch information
jsor committed Apr 25, 2017
1 parent 1e498d9 commit c52f792
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 0 deletions.
78 changes: 78 additions & 0 deletions src/GlobalLoop.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace React\EventLoop;

final class GlobalLoop
{
/**
* @var LoopInterface
*/
private static $loop;

private static $factory;

private static $didRun = false;

public static function setFactory(callable $factory = null)
{
if (self::$didRun) {
throw new \LogicException(
'Setting a factory after the global loop has been started is not allowed.'
);
}

self::$factory = $factory;
}

/**
* @return LoopInterface
*/
public static function get()
{
if (self::$loop) {
return self::$loop;
}

self::$loop = self::create();

self::$loop->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;
}
}
76 changes: 76 additions & 0 deletions tests/GlobalLoopTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace React\Tests\EventLoop;

use React\EventLoop\GlobalLoop;

class GlobalLoopTest extends TestCase
{
public function tearDown()
{
GlobalLoop::reset();
GlobalLoop::setFactory(null);
}

public function testCreatesDefaultLoop()
{
$this->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);
}
}

0 comments on commit c52f792

Please sign in to comment.