summaryrefslogtreecommitdiffstats
path: root/vendor/react/event-loop/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/react/event-loop/src')
-rw-r--r--vendor/react/event-loop/src/ExtEvLoop.php253
-rw-r--r--vendor/react/event-loop/src/ExtEventLoop.php275
-rw-r--r--vendor/react/event-loop/src/ExtLibevLoop.php201
-rw-r--r--vendor/react/event-loop/src/ExtLibeventLoop.php285
-rw-r--r--vendor/react/event-loop/src/ExtUvLoop.php342
-rw-r--r--vendor/react/event-loop/src/Factory.php75
-rw-r--r--vendor/react/event-loop/src/Loop.php266
-rw-r--r--vendor/react/event-loop/src/LoopInterface.php472
-rw-r--r--vendor/react/event-loop/src/SignalsHandler.php63
-rw-r--r--vendor/react/event-loop/src/StreamSelectLoop.php330
-rw-r--r--vendor/react/event-loop/src/Tick/FutureTickQueue.php60
-rw-r--r--vendor/react/event-loop/src/Timer/Timer.php55
-rw-r--r--vendor/react/event-loop/src/Timer/Timers.php113
-rw-r--r--vendor/react/event-loop/src/TimerInterface.php27
14 files changed, 2817 insertions, 0 deletions
diff --git a/vendor/react/event-loop/src/ExtEvLoop.php b/vendor/react/event-loop/src/ExtEvLoop.php
new file mode 100644
index 0000000..a3fcec6
--- /dev/null
+++ b/vendor/react/event-loop/src/ExtEvLoop.php
@@ -0,0 +1,253 @@
+<?php
+
+namespace React\EventLoop;
+
+use Ev;
+use EvIo;
+use EvLoop;
+use React\EventLoop\Tick\FutureTickQueue;
+use React\EventLoop\Timer\Timer;
+use SplObjectStorage;
+
+/**
+ * An `ext-ev` based event loop.
+ *
+ * This loop uses the [`ev` PECL extension](https://pecl.php.net/package/ev),
+ * that provides an interface to `libev` library.
+ * `libev` itself supports a number of system-specific backends (epoll, kqueue).
+ *
+ * This loop is known to work with PHP 5.4 through PHP 8+.
+ *
+ * @see http://php.net/manual/en/book.ev.php
+ * @see https://bitbucket.org/osmanov/pecl-ev/overview
+ */
+class ExtEvLoop implements LoopInterface
+{
+ /**
+ * @var EvLoop
+ */
+ private $loop;
+
+ /**
+ * @var FutureTickQueue
+ */
+ private $futureTickQueue;
+
+ /**
+ * @var SplObjectStorage
+ */
+ private $timers;
+
+ /**
+ * @var EvIo[]
+ */
+ private $readStreams = array();
+
+ /**
+ * @var EvIo[]
+ */
+ private $writeStreams = array();
+
+ /**
+ * @var bool
+ */
+ private $running;
+
+ /**
+ * @var SignalsHandler
+ */
+ private $signals;
+
+ /**
+ * @var \EvSignal[]
+ */
+ private $signalEvents = array();
+
+ public function __construct()
+ {
+ $this->loop = new EvLoop();
+ $this->futureTickQueue = new FutureTickQueue();
+ $this->timers = new SplObjectStorage();
+ $this->signals = new SignalsHandler();
+ }
+
+ public function addReadStream($stream, $listener)
+ {
+ $key = (int)$stream;
+
+ if (isset($this->readStreams[$key])) {
+ return;
+ }
+
+ $callback = $this->getStreamListenerClosure($stream, $listener);
+ $event = $this->loop->io($stream, Ev::READ, $callback);
+ $this->readStreams[$key] = $event;
+ }
+
+ /**
+ * @param resource $stream
+ * @param callable $listener
+ *
+ * @return \Closure
+ */
+ private function getStreamListenerClosure($stream, $listener)
+ {
+ return function () use ($stream, $listener) {
+ \call_user_func($listener, $stream);
+ };
+ }
+
+ public function addWriteStream($stream, $listener)
+ {
+ $key = (int)$stream;
+
+ if (isset($this->writeStreams[$key])) {
+ return;
+ }
+
+ $callback = $this->getStreamListenerClosure($stream, $listener);
+ $event = $this->loop->io($stream, Ev::WRITE, $callback);
+ $this->writeStreams[$key] = $event;
+ }
+
+ public function removeReadStream($stream)
+ {
+ $key = (int)$stream;
+
+ if (!isset($this->readStreams[$key])) {
+ return;
+ }
+
+ $this->readStreams[$key]->stop();
+ unset($this->readStreams[$key]);
+ }
+
+ public function removeWriteStream($stream)
+ {
+ $key = (int)$stream;
+
+ if (!isset($this->writeStreams[$key])) {
+ return;
+ }
+
+ $this->writeStreams[$key]->stop();
+ unset($this->writeStreams[$key]);
+ }
+
+ public function addTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, false);
+
+ $that = $this;
+ $timers = $this->timers;
+ $callback = function () use ($timer, $timers, $that) {
+ \call_user_func($timer->getCallback(), $timer);
+
+ if ($timers->contains($timer)) {
+ $that->cancelTimer($timer);
+ }
+ };
+
+ $event = $this->loop->timer($timer->getInterval(), 0.0, $callback);
+ $this->timers->attach($timer, $event);
+
+ return $timer;
+ }
+
+ public function addPeriodicTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, true);
+
+ $callback = function () use ($timer) {
+ \call_user_func($timer->getCallback(), $timer);
+ };
+
+ $event = $this->loop->timer($timer->getInterval(), $timer->getInterval(), $callback);
+ $this->timers->attach($timer, $event);
+
+ return $timer;
+ }
+
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if (!isset($this->timers[$timer])) {
+ return;
+ }
+
+ $event = $this->timers[$timer];
+ $event->stop();
+ $this->timers->detach($timer);
+ }
+
+ public function futureTick($listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->futureTickQueue->tick();
+
+ $hasPendingCallbacks = !$this->futureTickQueue->isEmpty();
+ $wasJustStopped = !$this->running;
+ $nothingLeftToDo = !$this->readStreams
+ && !$this->writeStreams
+ && !$this->timers->count()
+ && $this->signals->isEmpty();
+
+ $flags = Ev::RUN_ONCE;
+ if ($wasJustStopped || $hasPendingCallbacks) {
+ $flags |= Ev::RUN_NOWAIT;
+ } elseif ($nothingLeftToDo) {
+ break;
+ }
+
+ $this->loop->run($flags);
+ }
+ }
+
+ public function stop()
+ {
+ $this->running = false;
+ }
+
+ public function __destruct()
+ {
+ /** @var TimerInterface $timer */
+ foreach ($this->timers as $timer) {
+ $this->cancelTimer($timer);
+ }
+
+ foreach ($this->readStreams as $key => $stream) {
+ $this->removeReadStream($key);
+ }
+
+ foreach ($this->writeStreams as $key => $stream) {
+ $this->removeWriteStream($key);
+ }
+ }
+
+ public function addSignal($signal, $listener)
+ {
+ $this->signals->add($signal, $listener);
+
+ if (!isset($this->signalEvents[$signal])) {
+ $this->signalEvents[$signal] = $this->loop->signal($signal, function() use ($signal) {
+ $this->signals->call($signal);
+ });
+ }
+ }
+
+ public function removeSignal($signal, $listener)
+ {
+ $this->signals->remove($signal, $listener);
+
+ if (isset($this->signalEvents[$signal])) {
+ $this->signalEvents[$signal]->stop();
+ unset($this->signalEvents[$signal]);
+ }
+ }
+}
diff --git a/vendor/react/event-loop/src/ExtEventLoop.php b/vendor/react/event-loop/src/ExtEventLoop.php
new file mode 100644
index 0000000..b162a40
--- /dev/null
+++ b/vendor/react/event-loop/src/ExtEventLoop.php
@@ -0,0 +1,275 @@
+<?php
+
+namespace React\EventLoop;
+
+use BadMethodCallException;
+use Event;
+use EventBase;
+use React\EventLoop\Tick\FutureTickQueue;
+use React\EventLoop\Timer\Timer;
+use SplObjectStorage;
+
+/**
+ * An `ext-event` based event loop.
+ *
+ * This uses the [`event` PECL extension](https://pecl.php.net/package/event),
+ * that provides an interface to `libevent` library.
+ * `libevent` itself supports a number of system-specific backends (epoll, kqueue).
+ *
+ * This loop is known to work with PHP 5.4 through PHP 8+.
+ *
+ * @link https://pecl.php.net/package/event
+ */
+final class ExtEventLoop implements LoopInterface
+{
+ private $eventBase;
+ private $futureTickQueue;
+ private $timerCallback;
+ private $timerEvents;
+ private $streamCallback;
+ private $readEvents = array();
+ private $writeEvents = array();
+ private $readListeners = array();
+ private $writeListeners = array();
+ private $readRefs = array();
+ private $writeRefs = array();
+ private $running;
+ private $signals;
+ private $signalEvents = array();
+
+ public function __construct()
+ {
+ if (!\class_exists('EventBase', false)) {
+ throw new BadMethodCallException('Cannot create ExtEventLoop, ext-event extension missing');
+ }
+
+ // support arbitrary file descriptors and not just sockets
+ // Windows only has limited file descriptor support, so do not require this (will fail otherwise)
+ // @link http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html#_setting_up_a_complicated_event_base
+ $config = new \EventConfig();
+ if (\DIRECTORY_SEPARATOR !== '\\') {
+ $config->requireFeatures(\EventConfig::FEATURE_FDS);
+ }
+
+ $this->eventBase = new EventBase($config);
+ $this->futureTickQueue = new FutureTickQueue();
+ $this->timerEvents = new SplObjectStorage();
+ $this->signals = new SignalsHandler();
+
+ $this->createTimerCallback();
+ $this->createStreamCallback();
+ }
+
+ public function __destruct()
+ {
+ // explicitly clear all references to Event objects to prevent SEGFAULTs on Windows
+ foreach ($this->timerEvents as $timer) {
+ $this->timerEvents->detach($timer);
+ }
+
+ $this->readEvents = array();
+ $this->writeEvents = array();
+ }
+
+ public function addReadStream($stream, $listener)
+ {
+ $key = (int) $stream;
+ if (isset($this->readListeners[$key])) {
+ return;
+ }
+
+ $event = new Event($this->eventBase, $stream, Event::PERSIST | Event::READ, $this->streamCallback);
+ $event->add();
+ $this->readEvents[$key] = $event;
+ $this->readListeners[$key] = $listener;
+
+ // ext-event does not increase refcount on stream resources for PHP 7+
+ // manually keep track of stream resource to prevent premature garbage collection
+ if (\PHP_VERSION_ID >= 70000) {
+ $this->readRefs[$key] = $stream;
+ }
+ }
+
+ public function addWriteStream($stream, $listener)
+ {
+ $key = (int) $stream;
+ if (isset($this->writeListeners[$key])) {
+ return;
+ }
+
+ $event = new Event($this->eventBase, $stream, Event::PERSIST | Event::WRITE, $this->streamCallback);
+ $event->add();
+ $this->writeEvents[$key] = $event;
+ $this->writeListeners[$key] = $listener;
+
+ // ext-event does not increase refcount on stream resources for PHP 7+
+ // manually keep track of stream resource to prevent premature garbage collection
+ if (\PHP_VERSION_ID >= 70000) {
+ $this->writeRefs[$key] = $stream;
+ }
+ }
+
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->readEvents[$key])) {
+ $this->readEvents[$key]->free();
+ unset(
+ $this->readEvents[$key],
+ $this->readListeners[$key],
+ $this->readRefs[$key]
+ );
+ }
+ }
+
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->writeEvents[$key])) {
+ $this->writeEvents[$key]->free();
+ unset(
+ $this->writeEvents[$key],
+ $this->writeListeners[$key],
+ $this->writeRefs[$key]
+ );
+ }
+ }
+
+ public function addTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, false);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ public function addPeriodicTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, true);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if ($this->timerEvents->contains($timer)) {
+ $this->timerEvents[$timer]->free();
+ $this->timerEvents->detach($timer);
+ }
+ }
+
+ public function futureTick($listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ public function addSignal($signal, $listener)
+ {
+ $this->signals->add($signal, $listener);
+
+ if (!isset($this->signalEvents[$signal])) {
+ $this->signalEvents[$signal] = Event::signal($this->eventBase, $signal, array($this->signals, 'call'));
+ $this->signalEvents[$signal]->add();
+ }
+ }
+
+ public function removeSignal($signal, $listener)
+ {
+ $this->signals->remove($signal, $listener);
+
+ if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) {
+ $this->signalEvents[$signal]->free();
+ unset($this->signalEvents[$signal]);
+ }
+ }
+
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->futureTickQueue->tick();
+
+ $flags = EventBase::LOOP_ONCE;
+ if (!$this->running || !$this->futureTickQueue->isEmpty()) {
+ $flags |= EventBase::LOOP_NONBLOCK;
+ } elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) {
+ break;
+ }
+
+ $this->eventBase->loop($flags);
+ }
+ }
+
+ 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 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()
+ {
+ $timers = $this->timerEvents;
+ $this->timerCallback = function ($_, $__, $timer) use ($timers) {
+ \call_user_func($timer->getCallback(), $timer);
+
+ if (!$timer->isPeriodic() && $timers->contains($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()
+ {
+ $read =& $this->readListeners;
+ $write =& $this->writeListeners;
+ $this->streamCallback = function ($stream, $flags) use (&$read, &$write) {
+ $key = (int) $stream;
+
+ if (Event::READ === (Event::READ & $flags) && isset($read[$key])) {
+ \call_user_func($read[$key], $stream);
+ }
+
+ if (Event::WRITE === (Event::WRITE & $flags) && isset($write[$key])) {
+ \call_user_func($write[$key], $stream);
+ }
+ };
+ }
+}
diff --git a/vendor/react/event-loop/src/ExtLibevLoop.php b/vendor/react/event-loop/src/ExtLibevLoop.php
new file mode 100644
index 0000000..c303fdd
--- /dev/null
+++ b/vendor/react/event-loop/src/ExtLibevLoop.php
@@ -0,0 +1,201 @@
+<?php
+
+namespace React\EventLoop;
+
+use BadMethodCallException;
+use libev\EventLoop;
+use libev\IOEvent;
+use libev\SignalEvent;
+use libev\TimerEvent;
+use React\EventLoop\Tick\FutureTickQueue;
+use React\EventLoop\Timer\Timer;
+use SplObjectStorage;
+
+/**
+ * [Deprecated] An `ext-libev` based event loop.
+ *
+ * This uses an [unofficial `libev` extension](https://github.com/m4rw3r/php-libev),
+ * that provides an interface to `libev` library.
+ * `libev` itself supports a number of system-specific backends (epoll, kqueue).
+ *
+ * This loop does only work with PHP 5.
+ * An update for PHP 7 is [unlikely](https://github.com/m4rw3r/php-libev/issues/8)
+ * to happen any time soon.
+ *
+ * @see https://github.com/m4rw3r/php-libev
+ * @see https://gist.github.com/1688204
+ * @deprecated 1.2.0, use [`ExtEvLoop`](#extevloop) instead.
+ */
+final class ExtLibevLoop implements LoopInterface
+{
+ private $loop;
+ private $futureTickQueue;
+ private $timerEvents;
+ private $readEvents = array();
+ private $writeEvents = array();
+ private $running;
+ private $signals;
+ private $signalEvents = array();
+
+ public function __construct()
+ {
+ if (!\class_exists('libev\EventLoop', false)) {
+ throw new BadMethodCallException('Cannot create ExtLibevLoop, ext-libev extension missing');
+ }
+
+ $this->loop = new EventLoop();
+ $this->futureTickQueue = new FutureTickQueue();
+ $this->timerEvents = new SplObjectStorage();
+ $this->signals = new SignalsHandler();
+ }
+
+ public function addReadStream($stream, $listener)
+ {
+ if (isset($this->readEvents[(int) $stream])) {
+ return;
+ }
+
+ $callback = function () use ($stream, $listener) {
+ \call_user_func($listener, $stream);
+ };
+
+ $event = new IOEvent($callback, $stream, IOEvent::READ);
+ $this->loop->add($event);
+
+ $this->readEvents[(int) $stream] = $event;
+ }
+
+ public function addWriteStream($stream, $listener)
+ {
+ if (isset($this->writeEvents[(int) $stream])) {
+ return;
+ }
+
+ $callback = function () use ($stream, $listener) {
+ \call_user_func($listener, $stream);
+ };
+
+ $event = new IOEvent($callback, $stream, IOEvent::WRITE);
+ $this->loop->add($event);
+
+ $this->writeEvents[(int) $stream] = $event;
+ }
+
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->readEvents[$key])) {
+ $this->readEvents[$key]->stop();
+ $this->loop->remove($this->readEvents[$key]);
+ unset($this->readEvents[$key]);
+ }
+ }
+
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->writeEvents[$key])) {
+ $this->writeEvents[$key]->stop();
+ $this->loop->remove($this->writeEvents[$key]);
+ unset($this->writeEvents[$key]);
+ }
+ }
+
+ public function addTimer($interval, $callback)
+ {
+ $timer = new Timer( $interval, $callback, false);
+
+ $that = $this;
+ $timers = $this->timerEvents;
+ $callback = function () use ($timer, $timers, $that) {
+ \call_user_func($timer->getCallback(), $timer);
+
+ if ($timers->contains($timer)) {
+ $that->cancelTimer($timer);
+ }
+ };
+
+ $event = new TimerEvent($callback, $timer->getInterval());
+ $this->timerEvents->attach($timer, $event);
+ $this->loop->add($event);
+
+ return $timer;
+ }
+
+ public function addPeriodicTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, true);
+
+ $callback = function () use ($timer) {
+ \call_user_func($timer->getCallback(), $timer);
+ };
+
+ $event = new TimerEvent($callback, $timer->getInterval(), $timer->getInterval());
+ $this->timerEvents->attach($timer, $event);
+ $this->loop->add($event);
+
+ return $timer;
+ }
+
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if (isset($this->timerEvents[$timer])) {
+ $this->loop->remove($this->timerEvents[$timer]);
+ $this->timerEvents->detach($timer);
+ }
+ }
+
+ public function futureTick($listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ public function addSignal($signal, $listener)
+ {
+ $this->signals->add($signal, $listener);
+
+ if (!isset($this->signalEvents[$signal])) {
+ $signals = $this->signals;
+ $this->signalEvents[$signal] = new SignalEvent(function () use ($signals, $signal) {
+ $signals->call($signal);
+ }, $signal);
+ $this->loop->add($this->signalEvents[$signal]);
+ }
+ }
+
+ public function removeSignal($signal, $listener)
+ {
+ $this->signals->remove($signal, $listener);
+
+ if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) {
+ $this->signalEvents[$signal]->stop();
+ $this->loop->remove($this->signalEvents[$signal]);
+ unset($this->signalEvents[$signal]);
+ }
+ }
+
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->futureTickQueue->tick();
+
+ $flags = EventLoop::RUN_ONCE;
+ if (!$this->running || !$this->futureTickQueue->isEmpty()) {
+ $flags |= EventLoop::RUN_NOWAIT;
+ } elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) {
+ break;
+ }
+
+ $this->loop->run($flags);
+ }
+ }
+
+ public function stop()
+ {
+ $this->running = false;
+ }
+}
diff --git a/vendor/react/event-loop/src/ExtLibeventLoop.php b/vendor/react/event-loop/src/ExtLibeventLoop.php
new file mode 100644
index 0000000..099293a
--- /dev/null
+++ b/vendor/react/event-loop/src/ExtLibeventLoop.php
@@ -0,0 +1,285 @@
+<?php
+
+namespace React\EventLoop;
+
+use BadMethodCallException;
+use Event;
+use EventBase;
+use React\EventLoop\Tick\FutureTickQueue;
+use React\EventLoop\Timer\Timer;
+use SplObjectStorage;
+
+/**
+ * [Deprecated] An `ext-libevent` based event loop.
+ *
+ * This uses the [`libevent` PECL extension](https://pecl.php.net/package/libevent),
+ * that provides an interface to `libevent` library.
+ * `libevent` itself supports a number of system-specific backends (epoll, kqueue).
+ *
+ * This event loop does only work with PHP 5.
+ * An [unofficial update](https://github.com/php/pecl-event-libevent/pull/2) for
+ * PHP 7 does exist, but it is known to cause regular crashes due to `SEGFAULT`s.
+ * To reiterate: Using this event loop on PHP 7 is not recommended.
+ * Accordingly, neither the [`Loop` class](#loop) nor the deprecated
+ * [`Factory` class](#factory) will try to use this event loop on PHP 7.
+ *
+ * This event loop is known to trigger a readable listener only if
+ * the stream *becomes* readable (edge-triggered) and may not trigger if the
+ * stream has already been readable from the beginning.
+ * This also implies that a stream may not be recognized as readable when data
+ * is still left in PHP's internal stream buffers.
+ * As such, it's recommended to use `stream_set_read_buffer($stream, 0);`
+ * to disable PHP's internal read buffer in this case.
+ * See also [`addReadStream()`](#addreadstream) for more details.
+ *
+ * @link https://pecl.php.net/package/libevent
+ * @deprecated 1.2.0, use [`ExtEventLoop`](#exteventloop) instead.
+ */
+final class ExtLibeventLoop implements LoopInterface
+{
+ /** @internal */
+ const MICROSECONDS_PER_SECOND = 1000000;
+
+ private $eventBase;
+ private $futureTickQueue;
+ private $timerCallback;
+ private $timerEvents;
+ private $streamCallback;
+ private $readEvents = array();
+ private $writeEvents = array();
+ private $readListeners = array();
+ private $writeListeners = array();
+ private $running;
+ private $signals;
+ private $signalEvents = array();
+
+ public function __construct()
+ {
+ if (!\function_exists('event_base_new')) {
+ throw new BadMethodCallException('Cannot create ExtLibeventLoop, ext-libevent extension missing');
+ }
+
+ $this->eventBase = \event_base_new();
+ $this->futureTickQueue = new FutureTickQueue();
+ $this->timerEvents = new SplObjectStorage();
+ $this->signals = new SignalsHandler();
+
+ $this->createTimerCallback();
+ $this->createStreamCallback();
+ }
+
+ public function addReadStream($stream, $listener)
+ {
+ $key = (int) $stream;
+ if (isset($this->readListeners[$key])) {
+ return;
+ }
+
+ $event = \event_new();
+ \event_set($event, $stream, \EV_PERSIST | \EV_READ, $this->streamCallback);
+ \event_base_set($event, $this->eventBase);
+ \event_add($event);
+
+ $this->readEvents[$key] = $event;
+ $this->readListeners[$key] = $listener;
+ }
+
+ public function addWriteStream($stream, $listener)
+ {
+ $key = (int) $stream;
+ if (isset($this->writeListeners[$key])) {
+ return;
+ }
+
+ $event = \event_new();
+ \event_set($event, $stream, \EV_PERSIST | \EV_WRITE, $this->streamCallback);
+ \event_base_set($event, $this->eventBase);
+ \event_add($event);
+
+ $this->writeEvents[$key] = $event;
+ $this->writeListeners[$key] = $listener;
+ }
+
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->readListeners[$key])) {
+ $event = $this->readEvents[$key];
+ \event_del($event);
+ \event_free($event);
+
+ unset(
+ $this->readEvents[$key],
+ $this->readListeners[$key]
+ );
+ }
+ }
+
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->writeListeners[$key])) {
+ $event = $this->writeEvents[$key];
+ \event_del($event);
+ \event_free($event);
+
+ unset(
+ $this->writeEvents[$key],
+ $this->writeListeners[$key]
+ );
+ }
+ }
+
+ public function addTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, false);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ public function addPeriodicTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, true);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if ($this->timerEvents->contains($timer)) {
+ $event = $this->timerEvents[$timer];
+ \event_del($event);
+ \event_free($event);
+
+ $this->timerEvents->detach($timer);
+ }
+ }
+
+ public function futureTick($listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ public function addSignal($signal, $listener)
+ {
+ $this->signals->add($signal, $listener);
+
+ if (!isset($this->signalEvents[$signal])) {
+ $this->signalEvents[$signal] = \event_new();
+ \event_set($this->signalEvents[$signal], $signal, \EV_PERSIST | \EV_SIGNAL, array($this->signals, 'call'));
+ \event_base_set($this->signalEvents[$signal], $this->eventBase);
+ \event_add($this->signalEvents[$signal]);
+ }
+ }
+
+ public function removeSignal($signal, $listener)
+ {
+ $this->signals->remove($signal, $listener);
+
+ if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) {
+ \event_del($this->signalEvents[$signal]);
+ \event_free($this->signalEvents[$signal]);
+ unset($this->signalEvents[$signal]);
+ }
+ }
+
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->futureTickQueue->tick();
+
+ $flags = \EVLOOP_ONCE;
+ if (!$this->running || !$this->futureTickQueue->isEmpty()) {
+ $flags |= \EVLOOP_NONBLOCK;
+ } elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) {
+ break;
+ }
+
+ \event_base_loop($this->eventBase, $flags);
+ }
+ }
+
+ 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 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()
+ {
+ $that = $this;
+ $timers = $this->timerEvents;
+ $this->timerCallback = function ($_, $__, $timer) use ($timers, $that) {
+ \call_user_func($timer->getCallback(), $timer);
+
+ // Timer already cancelled ...
+ if (!$timers->contains($timer)) {
+ return;
+ }
+
+ // Reschedule periodic timers ...
+ if ($timer->isPeriodic()) {
+ \event_add(
+ $timers[$timer],
+ $timer->getInterval() * ExtLibeventLoop::MICROSECONDS_PER_SECOND
+ );
+
+ // Clean-up one shot timers ...
+ } else {
+ $that->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()
+ {
+ $read =& $this->readListeners;
+ $write =& $this->writeListeners;
+ $this->streamCallback = function ($stream, $flags) use (&$read, &$write) {
+ $key = (int) $stream;
+
+ if (\EV_READ === (\EV_READ & $flags) && isset($read[$key])) {
+ \call_user_func($read[$key], $stream);
+ }
+
+ if (\EV_WRITE === (\EV_WRITE & $flags) && isset($write[$key])) {
+ \call_user_func($write[$key], $stream);
+ }
+ };
+ }
+}
diff --git a/vendor/react/event-loop/src/ExtUvLoop.php b/vendor/react/event-loop/src/ExtUvLoop.php
new file mode 100644
index 0000000..4434720
--- /dev/null
+++ b/vendor/react/event-loop/src/ExtUvLoop.php
@@ -0,0 +1,342 @@
+<?php
+
+namespace React\EventLoop;
+
+use React\EventLoop\Tick\FutureTickQueue;
+use React\EventLoop\Timer\Timer;
+use SplObjectStorage;
+
+/**
+ * An `ext-uv` based event loop.
+ *
+ * This loop uses the [`uv` PECL extension](https://pecl.php.net/package/uv),
+ * that provides an interface to `libuv` library.
+ * `libuv` itself supports a number of system-specific backends (epoll, kqueue).
+ *
+ * This loop is known to work with PHP 7+.
+ *
+ * @see https://github.com/bwoebi/php-uv
+ */
+final class ExtUvLoop implements LoopInterface
+{
+ private $uv;
+ private $futureTickQueue;
+ private $timers;
+ private $streamEvents = array();
+ private $readStreams = array();
+ private $writeStreams = array();
+ private $running;
+ private $signals;
+ private $signalEvents = array();
+ private $streamListener;
+
+ public function __construct()
+ {
+ if (!\function_exists('uv_loop_new')) {
+ throw new \BadMethodCallException('Cannot create LibUvLoop, ext-uv extension missing');
+ }
+
+ $this->uv = \uv_loop_new();
+ $this->futureTickQueue = new FutureTickQueue();
+ $this->timers = new SplObjectStorage();
+ $this->streamListener = $this->createStreamListener();
+ $this->signals = new SignalsHandler();
+ }
+
+ /**
+ * Returns the underlying ext-uv event loop. (Internal ReactPHP use only.)
+ *
+ * @internal
+ *
+ * @return resource
+ */
+ public function getUvLoop()
+ {
+ return $this->uv;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addReadStream($stream, $listener)
+ {
+ if (isset($this->readStreams[(int) $stream])) {
+ return;
+ }
+
+ $this->readStreams[(int) $stream] = $listener;
+ $this->addStream($stream);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addWriteStream($stream, $listener)
+ {
+ if (isset($this->writeStreams[(int) $stream])) {
+ return;
+ }
+
+ $this->writeStreams[(int) $stream] = $listener;
+ $this->addStream($stream);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeReadStream($stream)
+ {
+ if (!isset($this->streamEvents[(int) $stream])) {
+ return;
+ }
+
+ unset($this->readStreams[(int) $stream]);
+ $this->removeStream($stream);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeWriteStream($stream)
+ {
+ if (!isset($this->streamEvents[(int) $stream])) {
+ return;
+ }
+
+ unset($this->writeStreams[(int) $stream]);
+ $this->removeStream($stream);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, false);
+
+ $that = $this;
+ $timers = $this->timers;
+ $callback = function () use ($timer, $timers, $that) {
+ \call_user_func($timer->getCallback(), $timer);
+
+ if ($timers->contains($timer)) {
+ $that->cancelTimer($timer);
+ }
+ };
+
+ $event = \uv_timer_init($this->uv);
+ $this->timers->attach($timer, $event);
+ \uv_timer_start(
+ $event,
+ $this->convertFloatSecondsToMilliseconds($interval),
+ 0,
+ $callback
+ );
+
+ return $timer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addPeriodicTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, true);
+
+ $callback = function () use ($timer) {
+ \call_user_func($timer->getCallback(), $timer);
+ };
+
+ $interval = $this->convertFloatSecondsToMilliseconds($interval);
+ $event = \uv_timer_init($this->uv);
+ $this->timers->attach($timer, $event);
+ \uv_timer_start(
+ $event,
+ $interval,
+ (int) $interval === 0 ? 1 : $interval,
+ $callback
+ );
+
+ return $timer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if (isset($this->timers[$timer])) {
+ @\uv_timer_stop($this->timers[$timer]);
+ $this->timers->detach($timer);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function futureTick($listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ public function addSignal($signal, $listener)
+ {
+ $this->signals->add($signal, $listener);
+
+ if (!isset($this->signalEvents[$signal])) {
+ $signals = $this->signals;
+ $this->signalEvents[$signal] = \uv_signal_init($this->uv);
+ \uv_signal_start($this->signalEvents[$signal], function () use ($signals, $signal) {
+ $signals->call($signal);
+ }, $signal);
+ }
+ }
+
+ public function removeSignal($signal, $listener)
+ {
+ $this->signals->remove($signal, $listener);
+
+ if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) {
+ \uv_signal_stop($this->signalEvents[$signal]);
+ unset($this->signalEvents[$signal]);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->futureTickQueue->tick();
+
+ $hasPendingCallbacks = !$this->futureTickQueue->isEmpty();
+ $wasJustStopped = !$this->running;
+ $nothingLeftToDo = !$this->readStreams
+ && !$this->writeStreams
+ && !$this->timers->count()
+ && $this->signals->isEmpty();
+
+ // Use UV::RUN_ONCE when there are only I/O events active in the loop and block until one of those triggers,
+ // otherwise use UV::RUN_NOWAIT.
+ // @link http://docs.libuv.org/en/v1.x/loop.html#c.uv_run
+ $flags = \UV::RUN_ONCE;
+ if ($wasJustStopped || $hasPendingCallbacks) {
+ $flags = \UV::RUN_NOWAIT;
+ } elseif ($nothingLeftToDo) {
+ break;
+ }
+
+ \uv_run($this->uv, $flags);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function stop()
+ {
+ $this->running = false;
+ }
+
+ private function addStream($stream)
+ {
+ if (!isset($this->streamEvents[(int) $stream])) {
+ $this->streamEvents[(int)$stream] = \uv_poll_init_socket($this->uv, $stream);
+ }
+
+ if ($this->streamEvents[(int) $stream] !== false) {
+ $this->pollStream($stream);
+ }
+ }
+
+ private function removeStream($stream)
+ {
+ if (!isset($this->streamEvents[(int) $stream])) {
+ return;
+ }
+
+ if (!isset($this->readStreams[(int) $stream])
+ && !isset($this->writeStreams[(int) $stream])) {
+ \uv_poll_stop($this->streamEvents[(int) $stream]);
+ \uv_close($this->streamEvents[(int) $stream]);
+ unset($this->streamEvents[(int) $stream]);
+ return;
+ }
+
+ $this->pollStream($stream);
+ }
+
+ private function pollStream($stream)
+ {
+ if (!isset($this->streamEvents[(int) $stream])) {
+ return;
+ }
+
+ $flags = 0;
+ if (isset($this->readStreams[(int) $stream])) {
+ $flags |= \UV::READABLE;
+ }
+
+ if (isset($this->writeStreams[(int) $stream])) {
+ $flags |= \UV::WRITABLE;
+ }
+
+ \uv_poll_start($this->streamEvents[(int) $stream], $flags, $this->streamListener);
+ }
+
+ /**
+ * Create a stream listener
+ *
+ * @return callable Returns a callback
+ */
+ private function createStreamListener()
+ {
+ $callback = function ($event, $status, $events, $stream) {
+ // libuv automatically stops polling on error, re-enable polling to match other loop implementations
+ if ($status !== 0) {
+ $this->pollStream($stream);
+
+ // libuv may report no events on error, but this should still invoke stream listeners to report closed connections
+ // re-enable both readable and writable, correct listeners will be checked below anyway
+ if ($events === 0) {
+ $events = \UV::READABLE | \UV::WRITABLE;
+ }
+ }
+
+ if (isset($this->readStreams[(int) $stream]) && ($events & \UV::READABLE)) {
+ \call_user_func($this->readStreams[(int) $stream], $stream);
+ }
+
+ if (isset($this->writeStreams[(int) $stream]) && ($events & \UV::WRITABLE)) {
+ \call_user_func($this->writeStreams[(int) $stream], $stream);
+ }
+ };
+
+ return $callback;
+ }
+
+ /**
+ * @param float $interval
+ * @return int
+ */
+ private function convertFloatSecondsToMilliseconds($interval)
+ {
+ if ($interval < 0) {
+ return 0;
+ }
+
+ $maxValue = (int) (\PHP_INT_MAX / 1000);
+ $intInterval = (int) $interval;
+
+ if (($intInterval <= 0 && $interval > 1) || $intInterval >= $maxValue) {
+ throw new \InvalidArgumentException(
+ "Interval overflow, value must be lower than '{$maxValue}', but '{$interval}' passed."
+ );
+ }
+
+ return (int) \floor($interval * 1000);
+ }
+}
diff --git a/vendor/react/event-loop/src/Factory.php b/vendor/react/event-loop/src/Factory.php
new file mode 100644
index 0000000..30bbfd7
--- /dev/null
+++ b/vendor/react/event-loop/src/Factory.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace React\EventLoop;
+
+/**
+ * [Deprecated] The `Factory` class exists as a convenient way to pick the best available event loop implementation.
+ *
+ * @deprecated 1.2.0 See Loop instead.
+ * @see Loop
+ */
+final class Factory
+{
+ /**
+ * [Deprecated] Creates a new event loop instance
+ *
+ * ```php
+ * // deprecated
+ * $loop = React\EventLoop\Factory::create();
+ *
+ * // new
+ * $loop = React\EventLoop\Loop::get();
+ * ```
+ *
+ * This method always returns an instance implementing `LoopInterface`,
+ * the actual event loop implementation is an implementation detail.
+ *
+ * This method should usually only be called once at the beginning of the program.
+ *
+ * @deprecated 1.2.0 See Loop::get() instead.
+ * @see Loop::get()
+ *
+ * @return LoopInterface
+ */
+ public static function create()
+ {
+ $loop = self::construct();
+
+ Loop::set($loop);
+
+ return $loop;
+ }
+
+ /**
+ * @internal
+ * @return LoopInterface
+ */
+ private static function construct()
+ {
+ // @codeCoverageIgnoreStart
+ if (\function_exists('uv_loop_new')) {
+ // only use ext-uv on PHP 7
+ return new ExtUvLoop();
+ }
+
+ if (\class_exists('libev\EventLoop', false)) {
+ return new ExtLibevLoop();
+ }
+
+ if (\class_exists('EvLoop', false)) {
+ return new ExtEvLoop();
+ }
+
+ if (\class_exists('EventBase', false)) {
+ return new ExtEventLoop();
+ }
+
+ if (\function_exists('event_base_new') && \PHP_MAJOR_VERSION === 5) {
+ // only use ext-libevent on PHP 5 for now
+ return new ExtLibeventLoop();
+ }
+
+ return new StreamSelectLoop();
+ // @codeCoverageIgnoreEnd
+ }
+}
diff --git a/vendor/react/event-loop/src/Loop.php b/vendor/react/event-loop/src/Loop.php
new file mode 100644
index 0000000..f74b9ef
--- /dev/null
+++ b/vendor/react/event-loop/src/Loop.php
@@ -0,0 +1,266 @@
+<?php
+
+namespace React\EventLoop;
+
+/**
+ * The `Loop` class exists as a convenient way to get the currently relevant loop
+ */
+final class Loop
+{
+ /**
+ * @var ?LoopInterface
+ */
+ private static $instance;
+
+ /** @var bool */
+ private static $stopped = false;
+
+ /**
+ * Returns the event loop.
+ * When no loop is set, it will call the factory to create one.
+ *
+ * This method always returns an instance implementing `LoopInterface`,
+ * the actual event loop implementation is an implementation detail.
+ *
+ * This method is the preferred way to get the event loop and using
+ * Factory::create has been deprecated.
+ *
+ * @return LoopInterface
+ */
+ public static function get()
+ {
+ if (self::$instance instanceof LoopInterface) {
+ return self::$instance;
+ }
+
+ self::$instance = $loop = Factory::create();
+
+ // Automatically run loop at end of program, unless already started or stopped explicitly.
+ // This is tested using child processes, so coverage is actually 100%, see BinTest.
+ // @codeCoverageIgnoreStart
+ $hasRun = false;
+ $loop->futureTick(function () use (&$hasRun) {
+ $hasRun = true;
+ });
+
+ $stopped =& self::$stopped;
+ register_shutdown_function(function () use ($loop, &$hasRun, &$stopped) {
+ // Don't run if we're coming from a fatal error (uncaught exception).
+ $error = error_get_last();
+ if ((isset($error['type']) ? $error['type'] : 0) & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR)) {
+ return;
+ }
+
+ if (!$hasRun && !$stopped) {
+ $loop->run();
+ }
+ });
+ // @codeCoverageIgnoreEnd
+
+ return self::$instance;
+ }
+
+ /**
+ * Internal undocumented method, behavior might change or throw in the
+ * future. Use with caution and at your own risk.
+ *
+ * @internal
+ * @return void
+ */
+ public static function set(LoopInterface $loop)
+ {
+ self::$instance = $loop;
+ }
+
+ /**
+ * [Advanced] Register a listener to be notified when a stream is ready to read.
+ *
+ * @param resource $stream
+ * @param callable $listener
+ * @return void
+ * @throws \Exception
+ * @see LoopInterface::addReadStream()
+ */
+ public static function addReadStream($stream, $listener)
+ {
+ // create loop instance on demand (legacy PHP < 7 doesn't like ternaries in method calls)
+ if (self::$instance === null) {
+ self::get();
+ }
+ self::$instance->addReadStream($stream, $listener);
+ }
+
+ /**
+ * [Advanced] Register a listener to be notified when a stream is ready to write.
+ *
+ * @param resource $stream
+ * @param callable $listener
+ * @return void
+ * @throws \Exception
+ * @see LoopInterface::addWriteStream()
+ */
+ public static function addWriteStream($stream, $listener)
+ {
+ // create loop instance on demand (legacy PHP < 7 doesn't like ternaries in method calls)
+ if (self::$instance === null) {
+ self::get();
+ }
+ self::$instance->addWriteStream($stream, $listener);
+ }
+
+ /**
+ * Remove the read event listener for the given stream.
+ *
+ * @param resource $stream
+ * @return void
+ * @see LoopInterface::removeReadStream()
+ */
+ public static function removeReadStream($stream)
+ {
+ if (self::$instance !== null) {
+ self::$instance->removeReadStream($stream);
+ }
+ }
+
+ /**
+ * Remove the write event listener for the given stream.
+ *
+ * @param resource $stream
+ * @return void
+ * @see LoopInterface::removeWriteStream()
+ */
+ public static function removeWriteStream($stream)
+ {
+ if (self::$instance !== null) {
+ self::$instance->removeWriteStream($stream);
+ }
+ }
+
+ /**
+ * Enqueue a callback to be invoked once after the given interval.
+ *
+ * @param float $interval
+ * @param callable $callback
+ * @return TimerInterface
+ * @see LoopInterface::addTimer()
+ */
+ public static function addTimer($interval, $callback)
+ {
+ // create loop instance on demand (legacy PHP < 7 doesn't like ternaries in method calls)
+ if (self::$instance === null) {
+ self::get();
+ }
+ return self::$instance->addTimer($interval, $callback);
+ }
+
+ /**
+ * Enqueue a callback to be invoked repeatedly after the given interval.
+ *
+ * @param float $interval
+ * @param callable $callback
+ * @return TimerInterface
+ * @see LoopInterface::addPeriodicTimer()
+ */
+ public static function addPeriodicTimer($interval, $callback)
+ {
+ // create loop instance on demand (legacy PHP < 7 doesn't like ternaries in method calls)
+ if (self::$instance === null) {
+ self::get();
+ }
+ return self::$instance->addPeriodicTimer($interval, $callback);
+ }
+
+ /**
+ * Cancel a pending timer.
+ *
+ * @param TimerInterface $timer
+ * @return void
+ * @see LoopInterface::cancelTimer()
+ */
+ public static function cancelTimer(TimerInterface $timer)
+ {
+ if (self::$instance !== null) {
+ self::$instance->cancelTimer($timer);
+ }
+ }
+
+ /**
+ * Schedule a callback to be invoked on a future tick of the event loop.
+ *
+ * @param callable $listener
+ * @return void
+ * @see LoopInterface::futureTick()
+ */
+ public static function futureTick($listener)
+ {
+ // create loop instance on demand (legacy PHP < 7 doesn't like ternaries in method calls)
+ if (self::$instance === null) {
+ self::get();
+ }
+
+ self::$instance->futureTick($listener);
+ }
+
+ /**
+ * Register a listener to be notified when a signal has been caught by this process.
+ *
+ * @param int $signal
+ * @param callable $listener
+ * @return void
+ * @see LoopInterface::addSignal()
+ */
+ public static function addSignal($signal, $listener)
+ {
+ // create loop instance on demand (legacy PHP < 7 doesn't like ternaries in method calls)
+ if (self::$instance === null) {
+ self::get();
+ }
+
+ self::$instance->addSignal($signal, $listener);
+ }
+
+ /**
+ * Removes a previously added signal listener.
+ *
+ * @param int $signal
+ * @param callable $listener
+ * @return void
+ * @see LoopInterface::removeSignal()
+ */
+ public static function removeSignal($signal, $listener)
+ {
+ if (self::$instance !== null) {
+ self::$instance->removeSignal($signal, $listener);
+ }
+ }
+
+ /**
+ * Run the event loop until there are no more tasks to perform.
+ *
+ * @return void
+ * @see LoopInterface::run()
+ */
+ public static function run()
+ {
+ // create loop instance on demand (legacy PHP < 7 doesn't like ternaries in method calls)
+ if (self::$instance === null) {
+ self::get();
+ }
+
+ self::$instance->run();
+ }
+
+ /**
+ * Instruct a running event loop to stop.
+ *
+ * @return void
+ * @see LoopInterface::stop()
+ */
+ public static function stop()
+ {
+ self::$stopped = true;
+ if (self::$instance !== null) {
+ self::$instance->stop();
+ }
+ }
+}
diff --git a/vendor/react/event-loop/src/LoopInterface.php b/vendor/react/event-loop/src/LoopInterface.php
new file mode 100644
index 0000000..9266f71
--- /dev/null
+++ b/vendor/react/event-loop/src/LoopInterface.php
@@ -0,0 +1,472 @@
+<?php
+
+namespace React\EventLoop;
+
+interface LoopInterface
+{
+ /**
+ * [Advanced] Register a listener to be notified when a stream is ready to read.
+ *
+ * Note that this low-level API is considered advanced usage.
+ * Most use cases should probably use the higher-level
+ * [readable Stream API](https://github.com/reactphp/stream#readablestreaminterface)
+ * instead.
+ *
+ * The first parameter MUST be a valid stream resource that supports
+ * checking whether it is ready to read by this loop implementation.
+ * A single stream resource MUST NOT be added more than once.
+ * Instead, either call [`removeReadStream()`](#removereadstream) first or
+ * react to this event with a single listener and then dispatch from this
+ * listener. This method MAY throw an `Exception` if the given resource type
+ * is not supported by this loop implementation.
+ *
+ * The second parameter MUST be a listener callback function that accepts
+ * the stream resource as its only parameter.
+ * If you don't use the stream resource inside your listener callback function
+ * you MAY use a function which has no parameters at all.
+ *
+ * The listener callback function MUST NOT throw an `Exception`.
+ * The return value of the listener callback function will be ignored and has
+ * no effect, so for performance reasons you're recommended to not return
+ * any excessive data structures.
+ *
+ * If you want to access any variables within your callback function, you
+ * can bind arbitrary data to a callback closure like this:
+ *
+ * ```php
+ * $loop->addReadStream($stream, function ($stream) use ($name) {
+ * echo $name . ' said: ' . fread($stream);
+ * });
+ * ```
+ *
+ * See also [example #11](examples).
+ *
+ * You can invoke [`removeReadStream()`](#removereadstream) to remove the
+ * read event listener for this stream.
+ *
+ * The execution order of listeners when multiple streams become ready at
+ * the same time is not guaranteed.
+ *
+ * @param resource $stream The PHP stream resource to check.
+ * @param callable $listener Invoked when the stream is ready.
+ * @throws \Exception if the given resource type is not supported by this loop implementation
+ * @see self::removeReadStream()
+ */
+ public function addReadStream($stream, $listener);
+
+ /**
+ * [Advanced] Register a listener to be notified when a stream is ready to write.
+ *
+ * Note that this low-level API is considered advanced usage.
+ * Most use cases should probably use the higher-level
+ * [writable Stream API](https://github.com/reactphp/stream#writablestreaminterface)
+ * instead.
+ *
+ * The first parameter MUST be a valid stream resource that supports
+ * checking whether it is ready to write by this loop implementation.
+ * A single stream resource MUST NOT be added more than once.
+ * Instead, either call [`removeWriteStream()`](#removewritestream) first or
+ * react to this event with a single listener and then dispatch from this
+ * listener. This method MAY throw an `Exception` if the given resource type
+ * is not supported by this loop implementation.
+ *
+ * The second parameter MUST be a listener callback function that accepts
+ * the stream resource as its only parameter.
+ * If you don't use the stream resource inside your listener callback function
+ * you MAY use a function which has no parameters at all.
+ *
+ * The listener callback function MUST NOT throw an `Exception`.
+ * The return value of the listener callback function will be ignored and has
+ * no effect, so for performance reasons you're recommended to not return
+ * any excessive data structures.
+ *
+ * If you want to access any variables within your callback function, you
+ * can bind arbitrary data to a callback closure like this:
+ *
+ * ```php
+ * $loop->addWriteStream($stream, function ($stream) use ($name) {
+ * fwrite($stream, 'Hello ' . $name);
+ * });
+ * ```
+ *
+ * See also [example #12](examples).
+ *
+ * You can invoke [`removeWriteStream()`](#removewritestream) to remove the
+ * write event listener for this stream.
+ *
+ * The execution order of listeners when multiple streams become ready at
+ * the same time is not guaranteed.
+ *
+ * Some event loop implementations are known to only trigger the listener if
+ * the stream *becomes* readable (edge-triggered) and may not trigger if the
+ * stream has already been readable from the beginning.
+ * This also implies that a stream may not be recognized as readable when data
+ * is still left in PHP's internal stream buffers.
+ * As such, it's recommended to use `stream_set_read_buffer($stream, 0);`
+ * to disable PHP's internal read buffer in this case.
+ *
+ * @param resource $stream The PHP stream resource to check.
+ * @param callable $listener Invoked when the stream is ready.
+ * @throws \Exception if the given resource type is not supported by this loop implementation
+ * @see self::removeWriteStream()
+ */
+ public function addWriteStream($stream, $listener);
+
+ /**
+ * Remove the read event listener for the given stream.
+ *
+ * Removing a stream from the loop that has already been removed or trying
+ * to remove a stream that was never added or is invalid has no effect.
+ *
+ * @param resource $stream The PHP stream resource.
+ */
+ public function removeReadStream($stream);
+
+ /**
+ * Remove the write event listener for the given stream.
+ *
+ * Removing a stream from the loop that has already been removed or trying
+ * to remove a stream that was never added or is invalid has no effect.
+ *
+ * @param resource $stream The PHP stream resource.
+ */
+ public function removeWriteStream($stream);
+
+ /**
+ * Enqueue a callback to be invoked once after the given interval.
+ *
+ * The second parameter MUST be a timer callback function that accepts
+ * the timer instance as its only parameter.
+ * If you don't use the timer instance inside your timer callback function
+ * you MAY use a function which has no parameters at all.
+ *
+ * The timer callback function MUST NOT throw an `Exception`.
+ * The return value of the timer callback function will be ignored and has
+ * no effect, so for performance reasons you're recommended to not return
+ * any excessive data structures.
+ *
+ * This method returns a timer instance. The same timer instance will also be
+ * passed into the timer callback function as described above.
+ * You can invoke [`cancelTimer`](#canceltimer) to cancel a pending timer.
+ * Unlike [`addPeriodicTimer()`](#addperiodictimer), this method will ensure
+ * the callback will be invoked only once after the given interval.
+ *
+ * ```php
+ * $loop->addTimer(0.8, function () {
+ * echo 'world!' . PHP_EOL;
+ * });
+ *
+ * $loop->addTimer(0.3, function () {
+ * echo 'hello ';
+ * });
+ * ```
+ *
+ * See also [example #1](examples).
+ *
+ * If you want to access any variables within your callback function, you
+ * can bind arbitrary data to a callback closure like this:
+ *
+ * ```php
+ * function hello($name, LoopInterface $loop)
+ * {
+ * $loop->addTimer(1.0, function () use ($name) {
+ * echo "hello $name\n";
+ * });
+ * }
+ *
+ * hello('Tester', $loop);
+ * ```
+ *
+ * This interface does not enforce any particular timer resolution, so
+ * special care may have to be taken if you rely on very high precision with
+ * millisecond accuracy or below. Event loop implementations SHOULD work on
+ * a best effort basis and SHOULD provide at least millisecond accuracy
+ * unless otherwise noted. Many existing event loop implementations are
+ * known to provide microsecond accuracy, but it's generally not recommended
+ * to rely on this high precision.
+ *
+ * Similarly, the execution order of timers scheduled to execute at the
+ * same time (within its possible accuracy) is not guaranteed.
+ *
+ * This interface suggests that event loop implementations SHOULD use a
+ * monotonic time source if available. Given that a monotonic time source is
+ * only available as of PHP 7.3 by default, event loop implementations MAY
+ * fall back to using wall-clock time.
+ * While this does not affect many common use cases, this is an important
+ * distinction for programs that rely on a high time precision or on systems
+ * that are subject to discontinuous time adjustments (time jumps).
+ * This means that if you schedule a timer to trigger in 30s and then adjust
+ * your system time forward by 20s, the timer SHOULD still trigger in 30s.
+ * See also [event loop implementations](#loop-implementations) for more details.
+ *
+ * @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, $callback);
+
+ /**
+ * Enqueue a callback to be invoked repeatedly after the given interval.
+ *
+ * The second parameter MUST be a timer callback function that accepts
+ * the timer instance as its only parameter.
+ * If you don't use the timer instance inside your timer callback function
+ * you MAY use a function which has no parameters at all.
+ *
+ * The timer callback function MUST NOT throw an `Exception`.
+ * The return value of the timer callback function will be ignored and has
+ * no effect, so for performance reasons you're recommended to not return
+ * any excessive data structures.
+ *
+ * This method returns a timer instance. The same timer instance will also be
+ * passed into the timer callback function as described above.
+ * Unlike [`addTimer()`](#addtimer), this method will ensure the callback
+ * will be invoked infinitely after the given interval or until you invoke
+ * [`cancelTimer`](#canceltimer).
+ *
+ * ```php
+ * $timer = $loop->addPeriodicTimer(0.1, function () {
+ * echo 'tick!' . PHP_EOL;
+ * });
+ *
+ * $loop->addTimer(1.0, function () use ($loop, $timer) {
+ * $loop->cancelTimer($timer);
+ * echo 'Done' . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also [example #2](examples).
+ *
+ * If you want to limit the number of executions, you can bind
+ * arbitrary data to a callback closure like this:
+ *
+ * ```php
+ * function hello($name, LoopInterface $loop)
+ * {
+ * $n = 3;
+ * $loop->addPeriodicTimer(1.0, function ($timer) use ($name, $loop, &$n) {
+ * if ($n > 0) {
+ * --$n;
+ * echo "hello $name\n";
+ * } else {
+ * $loop->cancelTimer($timer);
+ * }
+ * });
+ * }
+ *
+ * hello('Tester', $loop);
+ * ```
+ *
+ * This interface does not enforce any particular timer resolution, so
+ * special care may have to be taken if you rely on very high precision with
+ * millisecond accuracy or below. Event loop implementations SHOULD work on
+ * a best effort basis and SHOULD provide at least millisecond accuracy
+ * unless otherwise noted. Many existing event loop implementations are
+ * known to provide microsecond accuracy, but it's generally not recommended
+ * to rely on this high precision.
+ *
+ * Similarly, the execution order of timers scheduled to execute at the
+ * same time (within its possible accuracy) is not guaranteed.
+ *
+ * This interface suggests that event loop implementations SHOULD use a
+ * monotonic time source if available. Given that a monotonic time source is
+ * only available as of PHP 7.3 by default, event loop implementations MAY
+ * fall back to using wall-clock time.
+ * While this does not affect many common use cases, this is an important
+ * distinction for programs that rely on a high time precision or on systems
+ * that are subject to discontinuous time adjustments (time jumps).
+ * This means that if you schedule a timer to trigger in 30s and then adjust
+ * your system time forward by 20s, the timer SHOULD still trigger in 30s.
+ * See also [event loop implementations](#loop-implementations) for more details.
+ *
+ * Additionally, periodic timers may be subject to timer drift due to
+ * re-scheduling after each invocation. As such, it's generally not
+ * recommended to rely on this for high precision intervals with millisecond
+ * accuracy or below.
+ *
+ * @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, $callback);
+
+ /**
+ * Cancel a pending timer.
+ *
+ * See also [`addPeriodicTimer()`](#addperiodictimer) and [example #2](examples).
+ *
+ * Calling this method on a timer instance that has not been added to this
+ * loop instance or on a timer that has already been cancelled has no effect.
+ *
+ * @param TimerInterface $timer The timer to cancel.
+ *
+ * @return void
+ */
+ public function cancelTimer(TimerInterface $timer);
+
+ /**
+ * Schedule a callback to be invoked on a future tick of the event loop.
+ *
+ * This works very much similar to timers with an interval of zero seconds,
+ * but does not require the overhead of scheduling a timer queue.
+ *
+ * The tick callback function MUST be able to accept zero parameters.
+ *
+ * The tick callback function MUST NOT throw an `Exception`.
+ * The return value of the tick callback function will be ignored and has
+ * no effect, so for performance reasons you're recommended to not return
+ * any excessive data structures.
+ *
+ * If you want to access any variables within your callback function, you
+ * can bind arbitrary data to a callback closure like this:
+ *
+ * ```php
+ * function hello($name, LoopInterface $loop)
+ * {
+ * $loop->futureTick(function () use ($name) {
+ * echo "hello $name\n";
+ * });
+ * }
+ *
+ * hello('Tester', $loop);
+ * ```
+ *
+ * Unlike timers, tick callbacks are guaranteed to be executed in the order
+ * they are enqueued.
+ * Also, once a callback is enqueued, there's no way to cancel this operation.
+ *
+ * This is often used to break down bigger tasks into smaller steps (a form
+ * of cooperative multitasking).
+ *
+ * ```php
+ * $loop->futureTick(function () {
+ * echo 'b';
+ * });
+ * $loop->futureTick(function () {
+ * echo 'c';
+ * });
+ * echo 'a';
+ * ```
+ *
+ * See also [example #3](examples).
+ *
+ * @param callable $listener The callback to invoke.
+ *
+ * @return void
+ */
+ public function futureTick($listener);
+
+ /**
+ * Register a listener to be notified when a signal has been caught by this process.
+ *
+ * This is useful to catch user interrupt signals or shutdown signals from
+ * tools like `supervisor` or `systemd`.
+ *
+ * The second parameter MUST be a listener callback function that accepts
+ * the signal as its only parameter.
+ * If you don't use the signal inside your listener callback function
+ * you MAY use a function which has no parameters at all.
+ *
+ * The listener callback function MUST NOT throw an `Exception`.
+ * The return value of the listener callback function will be ignored and has
+ * no effect, so for performance reasons you're recommended to not return
+ * any excessive data structures.
+ *
+ * ```php
+ * $loop->addSignal(SIGINT, function (int $signal) {
+ * echo 'Caught user interrupt signal' . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also [example #4](examples).
+ *
+ * Signaling is only available on Unix-like platforms, Windows isn't
+ * supported due to operating system limitations.
+ * This method may throw a `BadMethodCallException` if signals aren't
+ * supported on this platform, for example when required extensions are
+ * missing.
+ *
+ * **Note: A listener can only be added once to the same signal, any
+ * attempts to add it more than once will be ignored.**
+ *
+ * @param int $signal
+ * @param callable $listener
+ *
+ * @throws \BadMethodCallException when signals aren't supported on this
+ * platform, for example when required extensions are missing.
+ *
+ * @return void
+ */
+ public function addSignal($signal, $listener);
+
+ /**
+ * Removes a previously added signal listener.
+ *
+ * ```php
+ * $loop->removeSignal(SIGINT, $listener);
+ * ```
+ *
+ * Any attempts to remove listeners that aren't registered will be ignored.
+ *
+ * @param int $signal
+ * @param callable $listener
+ *
+ * @return void
+ */
+ public function removeSignal($signal, $listener);
+
+ /**
+ * Run the event loop until there are no more tasks to perform.
+ *
+ * For many applications, this method is the only directly visible
+ * invocation on the event loop.
+ * As a rule of thumb, it is usually recommended to attach everything to the
+ * same loop instance and then run the loop once at the bottom end of the
+ * application.
+ *
+ * ```php
+ * $loop->run();
+ * ```
+ *
+ * This method will keep the loop running until there are no more tasks
+ * to perform. In other words: This method will block until the last
+ * timer, stream and/or signal has been removed.
+ *
+ * Likewise, it is imperative to ensure the application actually invokes
+ * this method once. Adding listeners to the loop and missing to actually
+ * run it will result in the application exiting without actually waiting
+ * for any of the attached listeners.
+ *
+ * This method MUST NOT be called while the loop is already running.
+ * This method MAY be called more than once after it has explicitly been
+ * [`stop()`ped](#stop) or after it automatically stopped because it
+ * previously did no longer have anything to do.
+ *
+ * @return void
+ */
+ public function run();
+
+ /**
+ * Instruct a running event loop to stop.
+ *
+ * This method is considered advanced usage and should be used with care.
+ * As a rule of thumb, it is usually recommended to let the loop stop
+ * only automatically when it no longer has anything to do.
+ *
+ * This method can be used to explicitly instruct the event loop to stop:
+ *
+ * ```php
+ * $loop->addTimer(3.0, function () use ($loop) {
+ * $loop->stop();
+ * });
+ * ```
+ *
+ * Calling this method on a loop instance that is not currently running or
+ * on a loop instance that has already been stopped has no effect.
+ *
+ * @return void
+ */
+ public function stop();
+}
diff --git a/vendor/react/event-loop/src/SignalsHandler.php b/vendor/react/event-loop/src/SignalsHandler.php
new file mode 100644
index 0000000..10d125d
--- /dev/null
+++ b/vendor/react/event-loop/src/SignalsHandler.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace React\EventLoop;
+
+/**
+ * @internal
+ */
+final class SignalsHandler
+{
+ private $signals = array();
+
+ public function add($signal, $listener)
+ {
+ if (!isset($this->signals[$signal])) {
+ $this->signals[$signal] = array();
+ }
+
+ if (\in_array($listener, $this->signals[$signal])) {
+ return;
+ }
+
+ $this->signals[$signal][] = $listener;
+ }
+
+ public function remove($signal, $listener)
+ {
+ if (!isset($this->signals[$signal])) {
+ return;
+ }
+
+ $index = \array_search($listener, $this->signals[$signal], true);
+ unset($this->signals[$signal][$index]);
+
+ if (isset($this->signals[$signal]) && \count($this->signals[$signal]) === 0) {
+ unset($this->signals[$signal]);
+ }
+ }
+
+ public function call($signal)
+ {
+ if (!isset($this->signals[$signal])) {
+ return;
+ }
+
+ foreach ($this->signals[$signal] as $listener) {
+ \call_user_func($listener, $signal);
+ }
+ }
+
+ public function count($signal)
+ {
+ if (!isset($this->signals[$signal])) {
+ return 0;
+ }
+
+ return \count($this->signals[$signal]);
+ }
+
+ public function isEmpty()
+ {
+ return !$this->signals;
+ }
+}
diff --git a/vendor/react/event-loop/src/StreamSelectLoop.php b/vendor/react/event-loop/src/StreamSelectLoop.php
new file mode 100644
index 0000000..1686fd7
--- /dev/null
+++ b/vendor/react/event-loop/src/StreamSelectLoop.php
@@ -0,0 +1,330 @@
+<?php
+
+namespace React\EventLoop;
+
+use React\EventLoop\Tick\FutureTickQueue;
+use React\EventLoop\Timer\Timer;
+use React\EventLoop\Timer\Timers;
+
+/**
+ * A `stream_select()` based event loop.
+ *
+ * This uses the [`stream_select()`](https://www.php.net/manual/en/function.stream-select.php)
+ * function and is the only implementation that works out of the box with PHP.
+ *
+ * This event loop works out of the box on PHP 5.4 through PHP 8+ and HHVM.
+ * This means that no installation is required and this library works on all
+ * platforms and supported PHP versions.
+ * Accordingly, the [`Loop` class](#loop) and the deprecated [`Factory`](#factory)
+ * will use this event loop by default if you do not install any of the event loop
+ * extensions listed below.
+ *
+ * Under the hood, it does a simple `select` system call.
+ * This system call is limited to the maximum file descriptor number of
+ * `FD_SETSIZE` (platform dependent, commonly 1024) and scales with `O(m)`
+ * (`m` being the maximum file descriptor number passed).
+ * This means that you may run into issues when handling thousands of streams
+ * concurrently and you may want to look into using one of the alternative
+ * event loop implementations listed below in this case.
+ * If your use case is among the many common use cases that involve handling only
+ * dozens or a few hundred streams at once, then this event loop implementation
+ * performs really well.
+ *
+ * If you want to use signal handling (see also [`addSignal()`](#addsignal) below),
+ * this event loop implementation requires `ext-pcntl`.
+ * This extension is only available for Unix-like platforms and does not support
+ * Windows.
+ * It is commonly installed as part of many PHP distributions.
+ * If this extension is missing (or you're running on Windows), signal handling is
+ * not supported and throws a `BadMethodCallException` instead.
+ *
+ * This event loop is known to rely on wall-clock time to schedule future timers
+ * when using any version before PHP 7.3, because a monotonic time source is
+ * only available as of PHP 7.3 (`hrtime()`).
+ * While this does not affect many common use cases, this is an important
+ * distinction for programs that rely on a high time precision or on systems
+ * that are subject to discontinuous time adjustments (time jumps).
+ * This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and
+ * then adjust your system time forward by 20s, the timer may trigger in 10s.
+ * See also [`addTimer()`](#addtimer) for more details.
+ *
+ * @link https://www.php.net/manual/en/function.stream-select.php
+ */
+final class StreamSelectLoop implements LoopInterface
+{
+ /** @internal */
+ const MICROSECONDS_PER_SECOND = 1000000;
+
+ private $futureTickQueue;
+ private $timers;
+ private $readStreams = array();
+ private $readListeners = array();
+ private $writeStreams = array();
+ private $writeListeners = array();
+ private $running;
+ private $pcntl = false;
+ private $pcntlPoll = false;
+ private $signals;
+
+ public function __construct()
+ {
+ $this->futureTickQueue = new FutureTickQueue();
+ $this->timers = new Timers();
+ $this->pcntl = \function_exists('pcntl_signal') && \function_exists('pcntl_signal_dispatch');
+ $this->pcntlPoll = $this->pcntl && !\function_exists('pcntl_async_signals');
+ $this->signals = new SignalsHandler();
+
+ // prefer async signals if available (PHP 7.1+) or fall back to dispatching on each tick
+ if ($this->pcntl && !$this->pcntlPoll) {
+ \pcntl_async_signals(true);
+ }
+ }
+
+ public function addReadStream($stream, $listener)
+ {
+ $key = (int) $stream;
+
+ if (!isset($this->readStreams[$key])) {
+ $this->readStreams[$key] = $stream;
+ $this->readListeners[$key] = $listener;
+ }
+ }
+
+ public function addWriteStream($stream, $listener)
+ {
+ $key = (int) $stream;
+
+ if (!isset($this->writeStreams[$key])) {
+ $this->writeStreams[$key] = $stream;
+ $this->writeListeners[$key] = $listener;
+ }
+ }
+
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ unset(
+ $this->readStreams[$key],
+ $this->readListeners[$key]
+ );
+ }
+
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ unset(
+ $this->writeStreams[$key],
+ $this->writeListeners[$key]
+ );
+ }
+
+ public function addTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, false);
+
+ $this->timers->add($timer);
+
+ return $timer;
+ }
+
+ public function addPeriodicTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, true);
+
+ $this->timers->add($timer);
+
+ return $timer;
+ }
+
+ public function cancelTimer(TimerInterface $timer)
+ {
+ $this->timers->cancel($timer);
+ }
+
+ public function futureTick($listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ public function addSignal($signal, $listener)
+ {
+ if ($this->pcntl === false) {
+ throw new \BadMethodCallException('Event loop feature "signals" isn\'t supported by the "StreamSelectLoop"');
+ }
+
+ $first = $this->signals->count($signal) === 0;
+ $this->signals->add($signal, $listener);
+
+ if ($first) {
+ \pcntl_signal($signal, array($this->signals, 'call'));
+ }
+ }
+
+ public function removeSignal($signal, $listener)
+ {
+ if (!$this->signals->count($signal)) {
+ return;
+ }
+
+ $this->signals->remove($signal, $listener);
+
+ if ($this->signals->count($signal) === 0) {
+ \pcntl_signal($signal, \SIG_DFL);
+ }
+ }
+
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->futureTickQueue->tick();
+
+ $this->timers->tick();
+
+ // Future-tick queue has pending callbacks ...
+ if (!$this->running || !$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 {
+ // Convert float seconds to int microseconds.
+ // Ensure we do not exceed maximum integer size, which may
+ // cause the loop to tick once every ~35min on 32bit systems.
+ $timeout *= self::MICROSECONDS_PER_SECOND;
+ $timeout = $timeout > \PHP_INT_MAX ? \PHP_INT_MAX : (int)$timeout;
+ }
+
+ // The only possible event is stream or signal activity, so wait forever ...
+ } elseif ($this->readStreams || $this->writeStreams || !$this->signals->isEmpty()) {
+ $timeout = null;
+
+ // There's nothing left to do ...
+ } else {
+ break;
+ }
+
+ $this->waitForStreamActivity($timeout);
+ }
+ }
+
+ public function stop()
+ {
+ $this->running = false;
+ }
+
+ /**
+ * Wait/check for stream activity, or until the next timer is due.
+ *
+ * @param integer|null $timeout Activity timeout in microseconds, or null to wait forever.
+ */
+ private function waitForStreamActivity($timeout)
+ {
+ $read = $this->readStreams;
+ $write = $this->writeStreams;
+
+ $available = $this->streamSelect($read, $write, $timeout);
+ if ($this->pcntlPoll) {
+ \pcntl_signal_dispatch();
+ }
+ if (false === $available) {
+ // if a system call has been interrupted,
+ // we cannot rely on it's outcome
+ return;
+ }
+
+ foreach ($read as $stream) {
+ $key = (int) $stream;
+
+ if (isset($this->readListeners[$key])) {
+ \call_user_func($this->readListeners[$key], $stream);
+ }
+ }
+
+ foreach ($write as $stream) {
+ $key = (int) $stream;
+
+ if (isset($this->writeListeners[$key])) {
+ \call_user_func($this->writeListeners[$key], $stream);
+ }
+ }
+ }
+
+ /**
+ * 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 int|null $timeout Activity timeout in microseconds, or null to wait forever.
+ *
+ * @return int|false The total number of streams that are ready for read/write.
+ * Can return false if stream_select() is interrupted by a signal.
+ */
+ private function streamSelect(array &$read, array &$write, $timeout)
+ {
+ if ($read || $write) {
+ // We do not usually use or expose the `exceptfds` parameter passed to the underlying `select`.
+ // However, Windows does not report failed connection attempts in `writefds` passed to `select` like most other platforms.
+ // Instead, it uses `writefds` only for successful connection attempts and `exceptfds` for failed connection attempts.
+ // We work around this by adding all sockets that look like a pending connection attempt to `exceptfds` automatically on Windows and merge it back later.
+ // This ensures the public API matches other loop implementations across all platforms (see also test suite or rather test matrix).
+ // Lacking better APIs, every write-only socket that has not yet read any data is assumed to be in a pending connection attempt state.
+ // @link https://docs.microsoft.com/de-de/windows/win32/api/winsock2/nf-winsock2-select
+ $except = null;
+ if (\DIRECTORY_SEPARATOR === '\\') {
+ $except = array();
+ foreach ($write as $key => $socket) {
+ if (!isset($read[$key]) && @\ftell($socket) === 0) {
+ $except[$key] = $socket;
+ }
+ }
+ }
+
+ /** @var ?callable $previous */
+ $previous = \set_error_handler(function ($errno, $errstr) use (&$previous) {
+ // suppress warnings that occur when `stream_select()` is interrupted by a signal
+ // PHP defines `EINTR` through `ext-sockets` or `ext-pcntl`, otherwise use common default (Linux & Mac)
+ $eintr = \defined('SOCKET_EINTR') ? \SOCKET_EINTR : (\defined('PCNTL_EINTR') ? \PCNTL_EINTR : 4);
+ if ($errno === \E_WARNING && \strpos($errstr, '[' . $eintr .']: ') !== false) {
+ return;
+ }
+
+ // forward any other error to registered error handler or print warning
+ return ($previous !== null) ? \call_user_func_array($previous, \func_get_args()) : false;
+ });
+
+ try {
+ $ret = \stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout);
+ \restore_error_handler();
+ } catch (\Throwable $e) { // @codeCoverageIgnoreStart
+ \restore_error_handler();
+ throw $e;
+ } catch (\Exception $e) {
+ \restore_error_handler();
+ throw $e;
+ } // @codeCoverageIgnoreEnd
+
+ if ($except) {
+ $write = \array_merge($write, $except);
+ }
+ return $ret;
+ }
+
+ if ($timeout > 0) {
+ \usleep($timeout);
+ } elseif ($timeout === null) {
+ // wait forever (we only reach this if we're only awaiting signals)
+ // this may be interrupted and return earlier when a signal is received
+ \sleep(PHP_INT_MAX);
+ }
+
+ return 0;
+ }
+}
diff --git a/vendor/react/event-loop/src/Tick/FutureTickQueue.php b/vendor/react/event-loop/src/Tick/FutureTickQueue.php
new file mode 100644
index 0000000..efabcbc
--- /dev/null
+++ b/vendor/react/event-loop/src/Tick/FutureTickQueue.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace React\EventLoop\Tick;
+
+use SplQueue;
+
+/**
+ * A tick queue implementation that can hold multiple callback functions
+ *
+ * This class should only be used internally, see LoopInterface instead.
+ *
+ * @see LoopInterface
+ * @internal
+ */
+final class FutureTickQueue
+{
+ private $queue;
+
+ public function __construct()
+ {
+ $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($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()
+ );
+ }
+ }
+
+ /**
+ * Check if the next tick queue is empty.
+ *
+ * @return boolean
+ */
+ public function isEmpty()
+ {
+ return $this->queue->isEmpty();
+ }
+}
diff --git a/vendor/react/event-loop/src/Timer/Timer.php b/vendor/react/event-loop/src/Timer/Timer.php
new file mode 100644
index 0000000..da3602a
--- /dev/null
+++ b/vendor/react/event-loop/src/Timer/Timer.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace React\EventLoop\Timer;
+
+use React\EventLoop\TimerInterface;
+
+/**
+ * The actual connection implementation for TimerInterface
+ *
+ * This class should only be used internally, see TimerInterface instead.
+ *
+ * @see TimerInterface
+ * @internal
+ */
+final class Timer implements TimerInterface
+{
+ const MIN_INTERVAL = 0.000001;
+
+ private $interval;
+ private $callback;
+ private $periodic;
+
+ /**
+ * Constructor initializes the fields of the Timer
+ *
+ * @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
+ */
+ public function __construct($interval, $callback, $periodic = false)
+ {
+ if ($interval < self::MIN_INTERVAL) {
+ $interval = self::MIN_INTERVAL;
+ }
+
+ $this->interval = (float) $interval;
+ $this->callback = $callback;
+ $this->periodic = (bool) $periodic;
+ }
+
+ public function getInterval()
+ {
+ return $this->interval;
+ }
+
+ public function getCallback()
+ {
+ return $this->callback;
+ }
+
+ public function isPeriodic()
+ {
+ return $this->periodic;
+ }
+}
diff --git a/vendor/react/event-loop/src/Timer/Timers.php b/vendor/react/event-loop/src/Timer/Timers.php
new file mode 100644
index 0000000..53c46d0
--- /dev/null
+++ b/vendor/react/event-loop/src/Timer/Timers.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace React\EventLoop\Timer;
+
+use React\EventLoop\TimerInterface;
+
+/**
+ * A scheduler implementation that can hold multiple timer instances
+ *
+ * This class should only be used internally, see TimerInterface instead.
+ *
+ * @see TimerInterface
+ * @internal
+ */
+final class Timers
+{
+ private $time;
+ private $timers = array();
+ private $schedule = array();
+ private $sorted = true;
+ private $useHighResolution;
+
+ public function __construct()
+ {
+ // prefer high-resolution timer, available as of PHP 7.3+
+ $this->useHighResolution = \function_exists('hrtime');
+ }
+
+ public function updateTime()
+ {
+ return $this->time = $this->useHighResolution ? \hrtime(true) * 1e-9 : \microtime(true);
+ }
+
+ public function getTime()
+ {
+ return $this->time ?: $this->updateTime();
+ }
+
+ public function add(TimerInterface $timer)
+ {
+ $id = \PHP_VERSION_ID < 70200 ? \spl_object_hash($timer) : \spl_object_id($timer);
+ $this->timers[$id] = $timer;
+ $this->schedule[$id] = $timer->getInterval() + $this->updateTime();
+ $this->sorted = false;
+ }
+
+ public function contains(TimerInterface $timer)
+ {
+ $id = \PHP_VERSION_ID < 70200 ? \spl_object_hash($timer) : \spl_object_id($timer);
+ return isset($this->timers[$id]);
+ }
+
+ public function cancel(TimerInterface $timer)
+ {
+ $id = \PHP_VERSION_ID < 70200 ? \spl_object_hash($timer) : \spl_object_id($timer);
+ unset($this->timers[$id], $this->schedule[$id]);
+ }
+
+ public function getFirst()
+ {
+ // ensure timers are sorted to simply accessing next (first) one
+ if (!$this->sorted) {
+ $this->sorted = true;
+ \asort($this->schedule);
+ }
+
+ return \reset($this->schedule);
+ }
+
+ public function isEmpty()
+ {
+ return \count($this->timers) === 0;
+ }
+
+ public function tick()
+ {
+ // hot path: skip timers if nothing is scheduled
+ if (!$this->schedule) {
+ return;
+ }
+
+ // ensure timers are sorted so we can execute in order
+ if (!$this->sorted) {
+ $this->sorted = true;
+ \asort($this->schedule);
+ }
+
+ $time = $this->updateTime();
+
+ foreach ($this->schedule as $id => $scheduled) {
+ // schedule is ordered, so loop until first timer that is not scheduled for execution now
+ if ($scheduled >= $time) {
+ break;
+ }
+
+ // skip any timers that are removed while we process the current schedule
+ if (!isset($this->schedule[$id]) || $this->schedule[$id] !== $scheduled) {
+ continue;
+ }
+
+ $timer = $this->timers[$id];
+ \call_user_func($timer->getCallback(), $timer);
+
+ // re-schedule if this is a periodic timer and it has not been cancelled explicitly already
+ if ($timer->isPeriodic() && isset($this->timers[$id])) {
+ $this->schedule[$id] = $timer->getInterval() + $time;
+ $this->sorted = false;
+ } else {
+ unset($this->timers[$id], $this->schedule[$id]);
+ }
+ }
+ }
+}
diff --git a/vendor/react/event-loop/src/TimerInterface.php b/vendor/react/event-loop/src/TimerInterface.php
new file mode 100644
index 0000000..cdcf773
--- /dev/null
+++ b/vendor/react/event-loop/src/TimerInterface.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace React\EventLoop;
+
+interface TimerInterface
+{
+ /**
+ * 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();
+
+ /**
+ * Determine whether the time is periodic
+ *
+ * @return bool
+ */
+ public function isPeriodic();
+}