diff --git a/.gitignore b/.gitignore index 987e2a25..81b92580 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ composer.lock +phpunit.xml vendor diff --git a/src/ExtEvLoop.php b/src/ExtEvLoop.php new file mode 100644 index 00000000..95f41880 --- /dev/null +++ b/src/ExtEvLoop.php @@ -0,0 +1,244 @@ +loop = new \EvLoop(); + $this->timers = new SplObjectStorage(); + $this->nextTickQueue = new NextTickQueue($this); + $this->futureTickQueue = new FutureTickQueue($this); + $this->timers = new SplObjectStorage(); + } + + /** + * {@inheritdoc} + */ + public function addReadStream($stream, callable $listener) + { + $this->addStream($stream, $listener, \Ev::READ); + } + + /** + * {@inheritdoc} + */ + public function addWriteStream($stream, callable $listener) + { + $this->addStream($stream, $listener, \Ev::WRITE); + } + + /** + * {@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); + } + + /** + * Wraps the listener in a callback which will pass the + * stream to the listener then registers the stream with + * the eventloop. + * + * @param resource $stream PHP Stream resource + * @param callable $listener stream callback + * @param int $flags flag bitmask + */ + private function addStream($stream, callable $listener, $flags) + { + $listener = function ($event) use ($stream, $listener) { + call_user_func($listener, $stream, $this); + }; + + $event = $this->loop->io($stream, $flags, $listener); + + if (($flags & \Ev::READ) === $flags) { + $this->readEvents[(int)$stream] = $event; + } elseif (($flags & \Ev::WRITE) === $flags) { + $this->writeEvents[(int)$stream] = $event; + } + } + + /** + * {@inheritdoc} + */ + public function addTimer($interval, callable $callback) + { + $timer = new Timer($this, $interval, $callback, false); + $this->setupTimer($timer); + + return $timer; + } + + /** + * {@inheritdoc} + */ + public function addPeriodicTimer($interval, callable $callback) + { + $timer = new Timer($this, $interval, $callback, true); + $this->setupTimer($timer); + + return $timer; + } + + /** + * {@inheritdoc} + */ + public function cancelTimer(TimerInterface $timer) + { + if (isset($this->timers[$timer])) { + /* stop EvTimer */ + $this->timers[$timer]->stop(); + + /* defer timer */ + $this->nextTick(function() use ($timer) { + $this->timers->detach($timer); + }); + } + } + + /** + * Add timer object as + * @param TimerInterface $timer [description] + * @return [type] [description] + */ + private function setupTimer(TimerInterface $timer) + { + $callback = function () use ($timer) { + call_user_func($timer->getCallback(), $timer); + + if (!$timer->isPeriodic()) { + $timer->cancel(); + } + }; + + $interval = $timer->getInterval(); + + $libevTimer = $this->loop->timer($interval, $interval, $callback); + + $this->timers->attach($timer, $libevTimer); + + return $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(); + + $flags = \Ev::RUN_ONCE; + if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) { + $flags |= \Ev::RUN_NOWAIT; + } elseif (!$this->readEvents && !$this->writeEvents && !$this->timers->count()) { + $this->running = false; + return; + } + $this->loop->run($flags); + } + + /** + * {@inheritdoc} + */ + public function run() + { + $this->running = true; + + while($this->running) { + $this->tick(); + } + } + + /** + * {@inheritdoc} + */ + public function stop() + { + $this->running = false; + } + + public function __destruct() + { + // mannually stop all watchers + foreach ($this->timers as $timer) { + $this->timers[$timer]->stop(); + } + + foreach ($this->readEvents as $event) { + $event->stop(); + } + + foreach ($this->writeEvents as $event) { + $event->stop(); + } + } +} diff --git a/src/ExtEventLoop.php b/src/ExtEventLoop.php index 40b860ae..48657f96 100644 --- a/src/ExtEventLoop.php +++ b/src/ExtEventLoop.php @@ -236,8 +236,8 @@ private function scheduleTimer(TimerInterface $timer) /** * Create a new ext-event Event object, or update the existing one. * - * @param stream $stream - * @param integer $flag Event::READ or Event::WRITE + * @param resource $stream + * @param integer $flag Event::READ or Event::WRITE */ private function subscribeStreamEvent($stream, $flag) { @@ -263,8 +263,8 @@ private function subscribeStreamEvent($stream, $flag) * 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 stream $stream - * @param integer $flag Event::READ or Event::WRITE + * @param resource $stream + * @param integer $flag Event::READ or Event::WRITE */ private function unsubscribeStreamEvent($stream, $flag) { diff --git a/src/Factory.php b/src/Factory.php index 207bc13c..9a481e35 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -9,9 +9,9 @@ public static function create() // @codeCoverageIgnoreStart if (function_exists('event_base_new')) { return new LibEventLoop(); - } else if (class_exists('libev\EventLoop', false)) { + } elseif (class_exists('libev\EventLoop', false)) { return new LibEvLoop; - } else if (class_exists('EventBase', false)) { + } elseif (class_exists('EventBase', false)) { return new ExtEventLoop; } diff --git a/src/LibEventLoop.php b/src/LibEventLoop.php index a55d6104..6fbc8269 100644 --- a/src/LibEventLoop.php +++ b/src/LibEventLoop.php @@ -237,8 +237,8 @@ private function scheduleTimer(TimerInterface $timer) /** * Create a new ext-libevent event resource, or update the existing one. * - * @param stream $stream - * @param integer $flag EV_READ or EV_WRITE + * @param resource $stream + * @param integer $flag EV_READ or EV_WRITE */ private function subscribeStreamEvent($stream, $flag) { @@ -267,8 +267,8 @@ private function subscribeStreamEvent($stream, $flag) * 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 stream $stream - * @param integer $flag EV_READ or EV_WRITE + * @param resource $stream + * @param integer $flag EV_READ or EV_WRITE */ private function unsubscribeStreamEvent($stream, $flag) { diff --git a/src/LoopInterface.php b/src/LoopInterface.php index 24a80a3d..d046526c 100644 --- a/src/LoopInterface.php +++ b/src/LoopInterface.php @@ -9,7 +9,7 @@ interface LoopInterface /** * Register a listener to be notified when a stream is ready to read. * - * @param stream $stream The PHP stream resource to check. + * @param resource $stream The PHP stream resource to check. * @param callable $listener Invoked when the stream is ready. */ public function addReadStream($stream, callable $listener); @@ -17,7 +17,7 @@ public function addReadStream($stream, callable $listener); /** * Register a listener to be notified when a stream is ready to write. * - * @param stream $stream The PHP stream resource to check. + * @param resource $stream The PHP stream resource to check. * @param callable $listener Invoked when the stream is ready. */ public function addWriteStream($stream, callable $listener); @@ -25,21 +25,21 @@ public function addWriteStream($stream, callable $listener); /** * Remove the read event listener for the given stream. * - * @param stream $stream The PHP stream resource. + * @param resource $stream The PHP stream resource. */ public function removeReadStream($stream); /** * Remove the write event listener for the given stream. * - * @param stream $stream The PHP stream resource. + * @param resource $stream The PHP stream resource. */ public function removeWriteStream($stream); /** * Remove all listeners for the given stream. * - * @param stream $stream The PHP stream resource. + * @param resource $stream The PHP stream resource. */ public function removeStream($stream); @@ -49,8 +49,8 @@ public function removeStream($stream); * The execution order of timers scheduled to execute at the same time is * not guaranteed. * - * @param numeric $interval The number of seconds to wait before execution. - * @param callable $callback The callback to invoke. + * @param int|float $interval The number of seconds to wait before execution. + * @param callable $callback The callback to invoke. * * @return TimerInterface */ @@ -62,8 +62,8 @@ public function addTimer($interval, callable $callback); * The execution order of timers scheduled to execute at the same time is * not guaranteed. * - * @param numeric $interval The number of seconds to wait before execution. - * @param callable $callback The callback to invoke. + * @param int|float $interval The number of seconds to wait before execution. + * @param callable $callback The callback to invoke. * * @return TimerInterface */ diff --git a/src/Timer/Timer.php b/src/Timer/Timer.php index ac64d2b0..f670ab3c 100644 --- a/src/Timer/Timer.php +++ b/src/Timer/Timer.php @@ -14,6 +14,15 @@ class Timer implements TimerInterface 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) { @@ -27,41 +36,65 @@ public function __construct(LoopInterface $loop, $interval, callable $callback, $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); diff --git a/src/Timer/TimerInterface.php b/src/Timer/TimerInterface.php index 5982b314..d066f369 100644 --- a/src/Timer/TimerInterface.php +++ b/src/Timer/TimerInterface.php @@ -2,14 +2,61 @@ 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(); } diff --git a/tests/ExtEvLoopTest.php b/tests/ExtEvLoopTest.php new file mode 100644 index 00000000..d05dbba3 --- /dev/null +++ b/tests/ExtEvLoopTest.php @@ -0,0 +1,40 @@ +markTestSkipped('ev tests skipped because pecl/ev is not installed.'); + } + + return new ExtEvLoop(); + } + + public function tearDown() + { + if (file_exists($this->file)) { + unlink($this->file); + } + } + + public function createStream() + { + $this->file = tempnam(sys_get_temp_dir(), 'react-'); + + $stream = fopen($this->file, 'r+'); + + return $stream; + } + + public function testEvConstructor() + { + $loop = new ExtEvLoop(); + } +} diff --git a/tests/ExtEventLoopTest.php b/tests/ExtEventLoopTest.php index 93408ad9..80daaf24 100644 --- a/tests/ExtEventLoopTest.php +++ b/tests/ExtEventLoopTest.php @@ -17,7 +17,7 @@ public function createLoop($readStreamCompatible = false) } $cfg = null; - if($readStreamCompatible) { + if ($readStreamCompatible) { $cfg = new \EventConfig(); $cfg->requireFeatures(\EventConfig::FEATURE_FDS); } diff --git a/tests/Timer/AbstractTimerTest.php b/tests/Timer/AbstractTimerTest.php index bb26f52e..57689658 100644 --- a/tests/Timer/AbstractTimerTest.php +++ b/tests/Timer/AbstractTimerTest.php @@ -3,7 +3,6 @@ namespace React\Tests\EventLoop\Timer; use React\Tests\EventLoop\TestCase; -use React\EventLoop\Timer\Timers; abstract class AbstractTimerTest extends TestCase { diff --git a/travis-init.sh b/travis-init.sh index 63deeb85..6aca97b5 100755 --- a/travis-init.sh +++ b/travis-init.sh @@ -21,6 +21,18 @@ if [[ "$TRAVIS_PHP_VERSION" != "hhvm" && popd echo "extension=libevent.so" >> "$(php -r 'echo php_ini_loaded_file();')" + # install 'pecl-ev' PHP extension + git clone http://bitbucket.org/osmanov/pecl-ev.git + # 0.2.12 + pushd pecl-ev + phpize + ./configure + make + # make test + make install + popd + echo "extension=ev.so" >> "$(php -r 'echo php_ini_loaded_file();')" + # install 'libev' PHP extension git clone --recursive https://github.com/m4rw3r/php-libev pushd php-libev
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: