added new socket manager, errorhandling and testing is not finished yet
This commit is contained in:
327
libs/React/EventLoop/ExtEventLoop.php
Normal file
327
libs/React/EventLoop/ExtEventLoop.php
Normal file
@ -0,0 +1,327 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use Event;
|
||||
use EventBase;
|
||||
use EventConfig as EventBaseConfig;
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Tick\NextTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use React\EventLoop\Timer\TimerInterface;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* An ext-event based React.
|
||||
*/
|
||||
class ExtEventLoop implements LoopInterface
|
||||
{
|
||||
private $eventBase;
|
||||
private $nextTickQueue;
|
||||
private $futureTickQueue;
|
||||
private $timerCallback;
|
||||
private $timerEvents;
|
||||
private $streamCallback;
|
||||
private $streamEvents = [];
|
||||
private $streamFlags = [];
|
||||
private $readListeners = [];
|
||||
private $writeListeners = [];
|
||||
private $running;
|
||||
|
||||
public function __construct(EventBaseConfig $config = null)
|
||||
{
|
||||
$this->eventBase = new EventBase($config);
|
||||
$this->nextTickQueue = new NextTickQueue($this);
|
||||
$this->futureTickQueue = new FutureTickQueue($this);
|
||||
$this->timerEvents = new SplObjectStorage();
|
||||
|
||||
$this->createTimerCallback();
|
||||
$this->createStreamCallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addReadStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->readListeners[$key])) {
|
||||
$this->readListeners[$key] = $listener;
|
||||
$this->subscribeStreamEvent($stream, Event::READ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addWriteStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->writeListeners[$key])) {
|
||||
$this->writeListeners[$key] = $listener;
|
||||
$this->subscribeStreamEvent($stream, Event::WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->readListeners[$key])) {
|
||||
unset($this->readListeners[$key]);
|
||||
$this->unsubscribeStreamEvent($stream, Event::READ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeListeners[$key])) {
|
||||
unset($this->writeListeners[$key]);
|
||||
$this->unsubscribeStreamEvent($stream, Event::WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->streamEvents[$key])) {
|
||||
$this->streamEvents[$key]->free();
|
||||
|
||||
unset(
|
||||
$this->streamFlags[$key],
|
||||
$this->streamEvents[$key],
|
||||
$this->readListeners[$key],
|
||||
$this->writeListeners[$key]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, false);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addPeriodicTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, true);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
if ($this->isTimerActive($timer)) {
|
||||
$this->timerEvents[$timer]->free();
|
||||
$this->timerEvents->detach($timer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTimerActive(TimerInterface $timer)
|
||||
{
|
||||
return $this->timerEvents->contains($timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function nextTick(callable $listener)
|
||||
{
|
||||
$this->nextTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function futureTick(callable $listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
// @-suppression: https://github.com/reactphp/react/pull/234#discussion-diff-7759616R226
|
||||
@$this->eventBase->loop(EventBase::LOOP_ONCE | EventBase::LOOP_NONBLOCK);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$flags = EventBase::LOOP_ONCE;
|
||||
if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
|
||||
$flags |= EventBase::LOOP_NONBLOCK;
|
||||
} elseif (!$this->streamEvents && !$this->timerEvents->count()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// @-suppression: https://github.com/reactphp/react/pull/234#discussion-diff-7759616R226
|
||||
@$this->eventBase->loop($flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a timer for execution.
|
||||
*
|
||||
* @param TimerInterface $timer
|
||||
*/
|
||||
private function scheduleTimer(TimerInterface $timer)
|
||||
{
|
||||
$flags = Event::TIMEOUT;
|
||||
|
||||
if ($timer->isPeriodic()) {
|
||||
$flags |= Event::PERSIST;
|
||||
}
|
||||
|
||||
$event = new Event($this->eventBase, -1, $flags, $this->timerCallback, $timer);
|
||||
$this->timerEvents[$timer] = $event;
|
||||
|
||||
$event->add($timer->getInterval());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ext-event Event object, or update the existing one.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param integer $flag Event::READ or Event::WRITE
|
||||
*/
|
||||
private function subscribeStreamEvent($stream, $flag)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->streamEvents[$key])) {
|
||||
$event = $this->streamEvents[$key];
|
||||
$flags = ($this->streamFlags[$key] |= $flag);
|
||||
|
||||
$event->del();
|
||||
$event->set($this->eventBase, $stream, Event::PERSIST | $flags, $this->streamCallback);
|
||||
} else {
|
||||
$event = new Event($this->eventBase, $stream, Event::PERSIST | $flag, $this->streamCallback);
|
||||
|
||||
$this->streamEvents[$key] = $event;
|
||||
$this->streamFlags[$key] = $flag;
|
||||
}
|
||||
|
||||
$event->add();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the ext-event Event object for this stream to stop listening to
|
||||
* the given event type, or remove it entirely if it's no longer needed.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param integer $flag Event::READ or Event::WRITE
|
||||
*/
|
||||
private function unsubscribeStreamEvent($stream, $flag)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
$flags = $this->streamFlags[$key] &= ~$flag;
|
||||
|
||||
if (0 === $flags) {
|
||||
$this->removeStream($stream);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$event = $this->streamEvents[$key];
|
||||
|
||||
$event->del();
|
||||
$event->set($this->eventBase, $stream, Event::PERSIST | $flags, $this->streamCallback);
|
||||
$event->add();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of timer events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createTimerCallback()
|
||||
{
|
||||
$this->timerCallback = function ($_, $_, $timer) {
|
||||
call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
if (!$timer->isPeriodic() && $this->isTimerActive($timer)) {
|
||||
$this->cancelTimer($timer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of stream events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createStreamCallback()
|
||||
{
|
||||
$this->streamCallback = function ($stream, $flags) {
|
||||
$key = (int) $stream;
|
||||
|
||||
if (Event::READ === (Event::READ & $flags) && isset($this->readListeners[$key])) {
|
||||
call_user_func($this->readListeners[$key], $stream, $this);
|
||||
}
|
||||
|
||||
if (Event::WRITE === (Event::WRITE & $flags) && isset($this->writeListeners[$key])) {
|
||||
call_user_func($this->writeListeners[$key], $stream, $this);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
21
libs/React/EventLoop/Factory.php
Normal file
21
libs/React/EventLoop/Factory.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
class Factory
|
||||
{
|
||||
public static function create()
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if (function_exists('event_base_new')) {
|
||||
return new LibEventLoop();
|
||||
} elseif (class_exists('libev\EventLoop', false)) {
|
||||
return new LibEvLoop;
|
||||
} elseif (class_exists('EventBase', false)) {
|
||||
return new ExtEventLoop;
|
||||
}
|
||||
|
||||
return new StreamSelectLoop();
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
218
libs/React/EventLoop/LibEvLoop.php
Normal file
218
libs/React/EventLoop/LibEvLoop.php
Normal file
@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use libev\EventLoop;
|
||||
use libev\IOEvent;
|
||||
use libev\TimerEvent;
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Tick\NextTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use React\EventLoop\Timer\TimerInterface;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* @see https://github.com/m4rw3r/php-libev
|
||||
* @see https://gist.github.com/1688204
|
||||
*/
|
||||
class LibEvLoop implements LoopInterface
|
||||
{
|
||||
private $loop;
|
||||
private $nextTickQueue;
|
||||
private $futureTickQueue;
|
||||
private $timerEvents;
|
||||
private $readEvents = [];
|
||||
private $writeEvents = [];
|
||||
private $running;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->loop = new EventLoop();
|
||||
$this->nextTickQueue = new NextTickQueue($this);
|
||||
$this->futureTickQueue = new FutureTickQueue($this);
|
||||
$this->timerEvents = new SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addReadStream($stream, callable $listener)
|
||||
{
|
||||
$callback = function () use ($stream, $listener) {
|
||||
call_user_func($listener, $stream, $this);
|
||||
};
|
||||
|
||||
$event = new IOEvent($callback, $stream, IOEvent::READ);
|
||||
$this->loop->add($event);
|
||||
|
||||
$this->readEvents[(int) $stream] = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addWriteStream($stream, callable $listener)
|
||||
{
|
||||
$callback = function () use ($stream, $listener) {
|
||||
call_user_func($listener, $stream, $this);
|
||||
};
|
||||
|
||||
$event = new IOEvent($callback, $stream, IOEvent::WRITE);
|
||||
$this->loop->add($event);
|
||||
|
||||
$this->writeEvents[(int) $stream] = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->readEvents[$key])) {
|
||||
$this->readEvents[$key]->stop();
|
||||
unset($this->readEvents[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeEvents[$key])) {
|
||||
$this->writeEvents[$key]->stop();
|
||||
unset($this->writeEvents[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeStream($stream)
|
||||
{
|
||||
$this->removeReadStream($stream);
|
||||
$this->removeWriteStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, false);
|
||||
|
||||
$callback = function () use ($timer) {
|
||||
call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
if ($this->isTimerActive($timer)) {
|
||||
$this->cancelTimer($timer);
|
||||
}
|
||||
};
|
||||
|
||||
$event = new TimerEvent($callback, $timer->getInterval());
|
||||
$this->timerEvents->attach($timer, $event);
|
||||
$this->loop->add($event);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addPeriodicTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, true);
|
||||
|
||||
$callback = function () use ($timer) {
|
||||
call_user_func($timer->getCallback(), $timer);
|
||||
};
|
||||
|
||||
$event = new TimerEvent($callback, $interval, $interval);
|
||||
$this->timerEvents->attach($timer, $event);
|
||||
$this->loop->add($event);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
if (isset($this->timerEvents[$timer])) {
|
||||
$this->loop->remove($this->timerEvents[$timer]);
|
||||
$this->timerEvents->detach($timer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTimerActive(TimerInterface $timer)
|
||||
{
|
||||
return $this->timerEvents->contains($timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function nextTick(callable $listener)
|
||||
{
|
||||
$this->nextTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function futureTick(callable $listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$this->loop->run(EventLoop::RUN_ONCE | EventLoop::RUN_NOWAIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$flags = EventLoop::RUN_ONCE;
|
||||
if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
|
||||
$flags |= EventLoop::RUN_NOWAIT;
|
||||
} elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->loop->run($flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
}
|
343
libs/React/EventLoop/LibEventLoop.php
Normal file
343
libs/React/EventLoop/LibEventLoop.php
Normal file
@ -0,0 +1,343 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use Event;
|
||||
use EventBase;
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Tick\NextTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use React\EventLoop\Timer\TimerInterface;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* An ext-libevent based React.
|
||||
*/
|
||||
class LibEventLoop implements LoopInterface
|
||||
{
|
||||
const MICROSECONDS_PER_SECOND = 1000000;
|
||||
|
||||
private $eventBase;
|
||||
private $nextTickQueue;
|
||||
private $futureTickQueue;
|
||||
private $timerCallback;
|
||||
private $timerEvents;
|
||||
private $streamCallback;
|
||||
private $streamEvents = [];
|
||||
private $streamFlags = [];
|
||||
private $readListeners = [];
|
||||
private $writeListeners = [];
|
||||
private $running;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->eventBase = event_base_new();
|
||||
$this->nextTickQueue = new NextTickQueue($this);
|
||||
$this->futureTickQueue = new FutureTickQueue($this);
|
||||
$this->timerEvents = new SplObjectStorage();
|
||||
|
||||
$this->createTimerCallback();
|
||||
$this->createStreamCallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addReadStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->readListeners[$key])) {
|
||||
$this->readListeners[$key] = $listener;
|
||||
$this->subscribeStreamEvent($stream, EV_READ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addWriteStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->writeListeners[$key])) {
|
||||
$this->writeListeners[$key] = $listener;
|
||||
$this->subscribeStreamEvent($stream, EV_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->readListeners[$key])) {
|
||||
unset($this->readListeners[$key]);
|
||||
$this->unsubscribeStreamEvent($stream, EV_READ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeListeners[$key])) {
|
||||
unset($this->writeListeners[$key]);
|
||||
$this->unsubscribeStreamEvent($stream, EV_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->streamEvents[$key])) {
|
||||
$event = $this->streamEvents[$key];
|
||||
|
||||
event_del($event);
|
||||
event_free($event);
|
||||
|
||||
unset(
|
||||
$this->streamFlags[$key],
|
||||
$this->streamEvents[$key],
|
||||
$this->readListeners[$key],
|
||||
$this->writeListeners[$key]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, false);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addPeriodicTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, true);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
if ($this->isTimerActive($timer)) {
|
||||
$event = $this->timerEvents[$timer];
|
||||
|
||||
event_del($event);
|
||||
event_free($event);
|
||||
|
||||
$this->timerEvents->detach($timer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTimerActive(TimerInterface $timer)
|
||||
{
|
||||
return $this->timerEvents->contains($timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function nextTick(callable $listener)
|
||||
{
|
||||
$this->nextTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function futureTick(callable $listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
event_base_loop($this->eventBase, EVLOOP_ONCE | EVLOOP_NONBLOCK);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$flags = EVLOOP_ONCE;
|
||||
if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
|
||||
$flags |= EVLOOP_NONBLOCK;
|
||||
} elseif (!$this->streamEvents && !$this->timerEvents->count()) {
|
||||
break;
|
||||
}
|
||||
|
||||
event_base_loop($this->eventBase, $flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a timer for execution.
|
||||
*
|
||||
* @param TimerInterface $timer
|
||||
*/
|
||||
private function scheduleTimer(TimerInterface $timer)
|
||||
{
|
||||
$this->timerEvents[$timer] = $event = event_timer_new();
|
||||
|
||||
event_timer_set($event, $this->timerCallback, $timer);
|
||||
event_base_set($event, $this->eventBase);
|
||||
event_add($event, $timer->getInterval() * self::MICROSECONDS_PER_SECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ext-libevent event resource, or update the existing one.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param integer $flag EV_READ or EV_WRITE
|
||||
*/
|
||||
private function subscribeStreamEvent($stream, $flag)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->streamEvents[$key])) {
|
||||
$event = $this->streamEvents[$key];
|
||||
$flags = $this->streamFlags[$key] |= $flag;
|
||||
|
||||
event_del($event);
|
||||
event_set($event, $stream, EV_PERSIST | $flags, $this->streamCallback);
|
||||
} else {
|
||||
$event = event_new();
|
||||
|
||||
event_set($event, $stream, EV_PERSIST | $flag, $this->streamCallback);
|
||||
event_base_set($event, $this->eventBase);
|
||||
|
||||
$this->streamEvents[$key] = $event;
|
||||
$this->streamFlags[$key] = $flag;
|
||||
}
|
||||
|
||||
event_add($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the ext-libevent event resource for this stream to stop listening to
|
||||
* the given event type, or remove it entirely if it's no longer needed.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param integer $flag EV_READ or EV_WRITE
|
||||
*/
|
||||
private function unsubscribeStreamEvent($stream, $flag)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
$flags = $this->streamFlags[$key] &= ~$flag;
|
||||
|
||||
if (0 === $flags) {
|
||||
$this->removeStream($stream);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$event = $this->streamEvents[$key];
|
||||
|
||||
event_del($event);
|
||||
event_set($event, $stream, EV_PERSIST | $flags, $this->streamCallback);
|
||||
event_add($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of timer events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createTimerCallback()
|
||||
{
|
||||
$this->timerCallback = function ($_, $_, $timer) {
|
||||
call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
// Timer already cancelled ...
|
||||
if (!$this->isTimerActive($timer)) {
|
||||
return;
|
||||
|
||||
// Reschedule periodic timers ...
|
||||
} elseif ($timer->isPeriodic()) {
|
||||
event_add(
|
||||
$this->timerEvents[$timer],
|
||||
$timer->getInterval() * self::MICROSECONDS_PER_SECOND
|
||||
);
|
||||
|
||||
// Clean-up one shot timers ...
|
||||
} else {
|
||||
$this->cancelTimer($timer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of stream events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createStreamCallback()
|
||||
{
|
||||
$this->streamCallback = function ($stream, $flags) {
|
||||
$key = (int) $stream;
|
||||
|
||||
if (EV_READ === (EV_READ & $flags) && isset($this->readListeners[$key])) {
|
||||
call_user_func($this->readListeners[$key], $stream, $this);
|
||||
}
|
||||
|
||||
if (EV_WRITE === (EV_WRITE & $flags) && isset($this->writeListeners[$key])) {
|
||||
call_user_func($this->writeListeners[$key], $stream, $this);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
121
libs/React/EventLoop/LoopInterface.php
Normal file
121
libs/React/EventLoop/LoopInterface.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use React\EventLoop\Timer\TimerInterface;
|
||||
|
||||
interface LoopInterface
|
||||
{
|
||||
/**
|
||||
* Register a listener to be notified when a stream is ready to read.
|
||||
*
|
||||
* @param resource $stream The PHP stream resource to check.
|
||||
* @param callable $listener Invoked when the stream is ready.
|
||||
*/
|
||||
public function addReadStream($stream, callable $listener);
|
||||
|
||||
/**
|
||||
* Register a listener 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.
|
||||
*/
|
||||
public function addWriteStream($stream, callable $listener);
|
||||
|
||||
/**
|
||||
* Remove the read event listener for the given stream.
|
||||
*
|
||||
* @param resource $stream The PHP stream resource.
|
||||
*/
|
||||
public function removeReadStream($stream);
|
||||
|
||||
/**
|
||||
* Remove the write event listener for the given stream.
|
||||
*
|
||||
* @param resource $stream The PHP stream resource.
|
||||
*/
|
||||
public function removeWriteStream($stream);
|
||||
|
||||
/**
|
||||
* Remove all listeners for the given stream.
|
||||
*
|
||||
* @param resource $stream The PHP stream resource.
|
||||
*/
|
||||
public function removeStream($stream);
|
||||
|
||||
/**
|
||||
* Enqueue a callback 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
|
||||
*/
|
||||
public function addTimer($interval, callable $callback);
|
||||
|
||||
/**
|
||||
* Enqueue a callback 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
|
||||
*/
|
||||
public function addPeriodicTimer($interval, callable $callback);
|
||||
|
||||
/**
|
||||
* Cancel a pending timer.
|
||||
*
|
||||
* @param TimerInterface $timer The timer to cancel.
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer);
|
||||
|
||||
/**
|
||||
* Check if a given timer is active.
|
||||
*
|
||||
* @param TimerInterface $timer The timer to check.
|
||||
*
|
||||
* @return boolean True if the timer is still enqueued for execution.
|
||||
*/
|
||||
public function isTimerActive(TimerInterface $timer);
|
||||
|
||||
/**
|
||||
* Schedule a callback to be invoked on the next tick of the event loop.
|
||||
*
|
||||
* Callbacks are guaranteed to be executed in the order they are enqueued,
|
||||
* before any timer or stream events.
|
||||
*
|
||||
* @param callable $listener The callback to invoke.
|
||||
*/
|
||||
public function nextTick(callable $listener);
|
||||
|
||||
/**
|
||||
* Schedule a callback to be invoked on a future tick of the event loop.
|
||||
*
|
||||
* Callbacks are guaranteed to be executed in the order they are enqueued.
|
||||
*
|
||||
* @param callable $listener The callback to invoke.
|
||||
*/
|
||||
public function futureTick(callable $listener);
|
||||
|
||||
/**
|
||||
* Perform a single iteration of the event loop.
|
||||
*/
|
||||
public function tick();
|
||||
|
||||
/**
|
||||
* Run the event loop until there are no more tasks to perform.
|
||||
*/
|
||||
public function run();
|
||||
|
||||
/**
|
||||
* Instruct a running event loop to stop.
|
||||
*/
|
||||
public function stop();
|
||||
}
|
262
libs/React/EventLoop/StreamSelectLoop.php
Normal file
262
libs/React/EventLoop/StreamSelectLoop.php
Normal file
@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Tick\NextTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use React\EventLoop\Timer\TimerInterface;
|
||||
use React\EventLoop\Timer\Timers;
|
||||
|
||||
/**
|
||||
* A stream_select() based React.
|
||||
*/
|
||||
class StreamSelectLoop implements LoopInterface
|
||||
{
|
||||
const MICROSECONDS_PER_SECOND = 1000000;
|
||||
|
||||
private $nextTickQueue;
|
||||
private $futureTickQueue;
|
||||
private $timers;
|
||||
private $readStreams = [];
|
||||
private $readListeners = [];
|
||||
private $writeStreams = [];
|
||||
private $writeListeners = [];
|
||||
private $running;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->nextTickQueue = new NextTickQueue($this);
|
||||
$this->futureTickQueue = new FutureTickQueue($this);
|
||||
$this->timers = new Timers();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addReadStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->readStreams[$key])) {
|
||||
$this->readStreams[$key] = $stream;
|
||||
$this->readListeners[$key] = $listener;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addWriteStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->writeStreams[$key])) {
|
||||
$this->writeStreams[$key] = $stream;
|
||||
$this->writeListeners[$key] = $listener;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
unset(
|
||||
$this->readStreams[$key],
|
||||
$this->readListeners[$key]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
unset(
|
||||
$this->writeStreams[$key],
|
||||
$this->writeListeners[$key]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeStream($stream)
|
||||
{
|
||||
$this->removeReadStream($stream);
|
||||
$this->removeWriteStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, false);
|
||||
|
||||
$this->timers->add($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addPeriodicTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, true);
|
||||
|
||||
$this->timers->add($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
$this->timers->cancel($timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTimerActive(TimerInterface $timer)
|
||||
{
|
||||
return $this->timers->contains($timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function nextTick(callable $listener)
|
||||
{
|
||||
$this->nextTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function futureTick(callable $listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$this->timers->tick();
|
||||
|
||||
$this->waitForStreamActivity(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$this->timers->tick();
|
||||
|
||||
// Next-tick or future-tick queues have pending callbacks ...
|
||||
if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
|
||||
$timeout = 0;
|
||||
|
||||
// There is a pending timer, only block until it is due ...
|
||||
} elseif ($scheduledAt = $this->timers->getFirst()) {
|
||||
$timeout = $scheduledAt - $this->timers->getTime();
|
||||
if ($timeout < 0) {
|
||||
$timeout = 0;
|
||||
} else {
|
||||
$timeout *= self::MICROSECONDS_PER_SECOND;
|
||||
}
|
||||
|
||||
// The only possible event is stream activity, so wait forever ...
|
||||
} elseif ($this->readStreams || $this->writeStreams) {
|
||||
$timeout = null;
|
||||
|
||||
// There's nothing left to do ...
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->waitForStreamActivity($timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait/check for stream activity, or until the next timer is due.
|
||||
*/
|
||||
private function waitForStreamActivity($timeout)
|
||||
{
|
||||
$read = $this->readStreams;
|
||||
$write = $this->writeStreams;
|
||||
|
||||
$this->streamSelect($read, $write, $timeout);
|
||||
|
||||
foreach ($read as $stream) {
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->readListeners[$key])) {
|
||||
call_user_func($this->readListeners[$key], $stream, $this);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($write as $stream) {
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeListeners[$key])) {
|
||||
call_user_func($this->writeListeners[$key], $stream, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulate a stream_select() implementation that does not break when passed
|
||||
* empty stream arrays.
|
||||
*
|
||||
* @param array &$read An array of read streams to select upon.
|
||||
* @param array &$write An array of write streams to select upon.
|
||||
* @param integer|null $timeout Activity timeout in microseconds, or null to wait forever.
|
||||
*
|
||||
* @return integer The total number of streams that are ready for read/write.
|
||||
*/
|
||||
protected function streamSelect(array &$read, array &$write, $timeout)
|
||||
{
|
||||
if ($read || $write) {
|
||||
$except = null;
|
||||
|
||||
return stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout);
|
||||
}
|
||||
|
||||
usleep($timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
59
libs/React/EventLoop/Tick/FutureTickQueue.php
Normal file
59
libs/React/EventLoop/Tick/FutureTickQueue.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop\Tick;
|
||||
|
||||
use React\EventLoop\LoopInterface;
|
||||
use SplQueue;
|
||||
|
||||
class FutureTickQueue
|
||||
{
|
||||
private $eventLoop;
|
||||
private $queue;
|
||||
|
||||
/**
|
||||
* @param LoopInterface $eventLoop The event loop passed as the first parameter to callbacks.
|
||||
*/
|
||||
public function __construct(LoopInterface $eventLoop)
|
||||
{
|
||||
$this->eventLoop = $eventLoop;
|
||||
$this->queue = new SplQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback to be invoked on a future tick of the event loop.
|
||||
*
|
||||
* Callbacks are guaranteed to be executed in the order they are enqueued.
|
||||
*
|
||||
* @param callable $listener The callback to invoke.
|
||||
*/
|
||||
public function add(callable $listener)
|
||||
{
|
||||
$this->queue->enqueue($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the callback queue.
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
// Only invoke as many callbacks as were on the queue when tick() was called.
|
||||
$count = $this->queue->count();
|
||||
|
||||
while ($count--) {
|
||||
call_user_func(
|
||||
$this->queue->dequeue(),
|
||||
$this->eventLoop
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the next tick queue is empty.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->queue->isEmpty();
|
||||
}
|
||||
}
|
57
libs/React/EventLoop/Tick/NextTickQueue.php
Normal file
57
libs/React/EventLoop/Tick/NextTickQueue.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop\Tick;
|
||||
|
||||
use React\EventLoop\LoopInterface;
|
||||
use SplQueue;
|
||||
|
||||
class NextTickQueue
|
||||
{
|
||||
private $eventLoop;
|
||||
private $queue;
|
||||
|
||||
/**
|
||||
* @param LoopInterface $eventLoop The event loop passed as the first parameter to callbacks.
|
||||
*/
|
||||
public function __construct(LoopInterface $eventLoop)
|
||||
{
|
||||
$this->eventLoop = $eventLoop;
|
||||
$this->queue = new SplQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback to be invoked on the next tick of the event loop.
|
||||
*
|
||||
* Callbacks are guaranteed to be executed in the order they are enqueued,
|
||||
* before any timer or stream events.
|
||||
*
|
||||
* @param callable $listener The callback to invoke.
|
||||
*/
|
||||
public function add(callable $listener)
|
||||
{
|
||||
$this->queue->enqueue($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the callback queue.
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
while (!$this->queue->isEmpty()) {
|
||||
call_user_func(
|
||||
$this->queue->dequeue(),
|
||||
$this->eventLoop
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the next tick queue is empty.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->queue->isEmpty();
|
||||
}
|
||||
}
|
102
libs/React/EventLoop/Timer/Timer.php
Normal file
102
libs/React/EventLoop/Timer/Timer.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\LoopInterface;
|
||||
|
||||
class Timer implements TimerInterface
|
||||
{
|
||||
const MIN_INTERVAL = 0.000001;
|
||||
|
||||
protected $loop;
|
||||
protected $interval;
|
||||
protected $callback;
|
||||
protected $periodic;
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Constructor initializes the fields of the Timer
|
||||
*
|
||||
* @param LoopInterface $loop The loop with which this timer is associated
|
||||
* @param float $interval The interval after which this timer will execute, in seconds
|
||||
* @param callable $callback The callback that will be executed when this timer elapses
|
||||
* @param bool $periodic Whether the time is periodic
|
||||
* @param mixed $data Arbitrary data associated with timer
|
||||
*/
|
||||
public function __construct(LoopInterface $loop, $interval, callable $callback, $periodic = false, $data = null)
|
||||
{
|
||||
if ($interval < self::MIN_INTERVAL) {
|
||||
$interval = self::MIN_INTERVAL;
|
||||
}
|
||||
|
||||
$this->loop = $loop;
|
||||
$this->interval = (float) $interval;
|
||||
$this->callback = $callback;
|
||||
$this->periodic = (bool) $periodic;
|
||||
$this->data = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLoop()
|
||||
{
|
||||
return $this->loop;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInterval()
|
||||
{
|
||||
return $this->interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCallback()
|
||||
{
|
||||
return $this->callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPeriodic()
|
||||
{
|
||||
return $this->periodic;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isActive()
|
||||
{
|
||||
return $this->loop->isTimerActive($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cancel()
|
||||
{
|
||||
$this->loop->cancelTimer($this);
|
||||
}
|
||||
}
|
62
libs/React/EventLoop/Timer/TimerInterface.php
Normal file
62
libs/React/EventLoop/Timer/TimerInterface.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\LoopInterface;
|
||||
|
||||
interface TimerInterface
|
||||
{
|
||||
/**
|
||||
* Get the loop with which this timer is associated
|
||||
*
|
||||
* @return LoopInterface
|
||||
*/
|
||||
public function getLoop();
|
||||
|
||||
/**
|
||||
* Get the interval after which this timer will execute, in seconds
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getInterval();
|
||||
|
||||
/**
|
||||
* Get the callback that will be executed when this timer elapses
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function getCallback();
|
||||
|
||||
/**
|
||||
* Set arbitrary data associated with timer
|
||||
*
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function setData($data);
|
||||
|
||||
/**
|
||||
* Get arbitrary data associated with timer
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData();
|
||||
|
||||
/**
|
||||
* Determine whether the time is periodic
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPeriodic();
|
||||
|
||||
/**
|
||||
* Determine whether the time is active
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isActive();
|
||||
|
||||
/**
|
||||
* Cancel this timer
|
||||
*/
|
||||
public function cancel();
|
||||
}
|
100
libs/React/EventLoop/Timer/Timers.php
Normal file
100
libs/React/EventLoop/Timer/Timers.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop\Timer;
|
||||
|
||||
use SplObjectStorage;
|
||||
use SplPriorityQueue;
|
||||
|
||||
class Timers
|
||||
{
|
||||
private $time;
|
||||
private $timers;
|
||||
private $scheduler;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->timers = new SplObjectStorage();
|
||||
$this->scheduler = new SplPriorityQueue();
|
||||
}
|
||||
|
||||
public function updateTime()
|
||||
{
|
||||
return $this->time = microtime(true);
|
||||
}
|
||||
|
||||
public function getTime()
|
||||
{
|
||||
return $this->time ?: $this->updateTime();
|
||||
}
|
||||
|
||||
public function add(TimerInterface $timer)
|
||||
{
|
||||
$interval = $timer->getInterval();
|
||||
$scheduledAt = $interval + $this->getTime();
|
||||
|
||||
$this->timers->attach($timer, $scheduledAt);
|
||||
$this->scheduler->insert($timer, -$scheduledAt);
|
||||
}
|
||||
|
||||
public function contains(TimerInterface $timer)
|
||||
{
|
||||
return $this->timers->contains($timer);
|
||||
}
|
||||
|
||||
public function cancel(TimerInterface $timer)
|
||||
{
|
||||
$this->timers->detach($timer);
|
||||
}
|
||||
|
||||
public function getFirst()
|
||||
{
|
||||
while ($this->scheduler->count()) {
|
||||
$timer = $this->scheduler->top();
|
||||
|
||||
if ($this->timers->contains($timer)) {
|
||||
return $this->timers[$timer];
|
||||
}
|
||||
|
||||
$this->scheduler->extract();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
{
|
||||
return count($this->timers) === 0;
|
||||
}
|
||||
|
||||
public function tick()
|
||||
{
|
||||
$time = $this->updateTime();
|
||||
$timers = $this->timers;
|
||||
$scheduler = $this->scheduler;
|
||||
|
||||
while (!$scheduler->isEmpty()) {
|
||||
$timer = $scheduler->top();
|
||||
|
||||
if (!isset($timers[$timer])) {
|
||||
$scheduler->extract();
|
||||
$timers->detach($timer);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($timers[$timer] >= $time) {
|
||||
break;
|
||||
}
|
||||
|
||||
$scheduler->extract();
|
||||
call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
if ($timer->isPeriodic() && isset($timers[$timer])) {
|
||||
$timers[$timer] = $scheduledAt = $timer->getInterval() + $time;
|
||||
$scheduler->insert($timer, -$scheduledAt);
|
||||
} else {
|
||||
$timers->detach($timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user