init
This commit is contained in:
3
vendor/react/event-loop/.gitignore
vendored
Executable file
3
vendor/react/event-loop/.gitignore
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
vendor
|
||||
27
vendor/react/event-loop/.travis.yml
vendored
Executable file
27
vendor/react/event-loop/.travis.yml
vendored
Executable file
@@ -0,0 +1,27 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
- hhvm
|
||||
|
||||
sudo: false
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libevent-dev # Used by 'event' and 'libevent' PHP extensions
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache/files
|
||||
|
||||
install:
|
||||
- ./travis-init.sh
|
||||
- composer install
|
||||
|
||||
script:
|
||||
- ./vendor/bin/phpunit --coverage-text
|
||||
79
vendor/react/event-loop/CHANGELOG.md
vendored
Executable file
79
vendor/react/event-loop/CHANGELOG.md
vendored
Executable file
@@ -0,0 +1,79 @@
|
||||
# Changelog
|
||||
|
||||
## 0.4.3 (2017-04-27)
|
||||
|
||||
* Bug fix: Bugfix in the usage sample code #57 (@dandelionred)
|
||||
* Improvement: Remove branch-alias definition #53 (@WyriHaximus)
|
||||
* Improvement: StreamSelectLoop: Use fresh time so Timers added during stream events are accurate #51 (@andrewminerd)
|
||||
* Improvement: Avoid deprecation warnings in test suite due to deprecation of getMock() in PHPUnit #68 (@martinschroeder)
|
||||
* Improvement: Add PHPUnit 4.8 to require-dev #69 (@shaunbramley)
|
||||
* Improvement: Increase test timeouts for HHVM and unify timeout handling #70 (@clue)
|
||||
* Improvement: Travis improvements (backported from #74) #75 (@clue)
|
||||
* Improvement: Test suite now uses socket pairs instead of memory streams #66 (@martinschroeder)
|
||||
* Improvement: StreamSelectLoop: Test suite uses signal constant names in data provider #67 (@martinschroeder)
|
||||
* Improvement: ExtEventLoop: No longer suppress all errors #65 (@mamciek)
|
||||
* Improvement: Readme cleanup #89 (@jsor)
|
||||
* Improvement: Restructure and improve README #90 (@jsor)
|
||||
* Bug fix: StreamSelectLoop: Fix erroneous zero-time sleep (backport to 0.4) #94 (@jsor)
|
||||
|
||||
## 0.4.2 (2016-03-07)
|
||||
|
||||
* Bug fix: No longer error when signals sent to StreamSelectLoop
|
||||
* Support HHVM and PHP7 (@ondrejmirtes, @cebe)
|
||||
* Feature: Added support for EventConfig for ExtEventLoop (@steverhoades)
|
||||
* Bug fix: Fixed an issue loading loop extension libs via autoloader (@czarpino)
|
||||
|
||||
## 0.4.1 (2014-04-13)
|
||||
|
||||
* Bug fix: null timeout in StreamSelectLoop causing 100% CPU usage (@clue)
|
||||
* Bug fix: v0.3.4 changes merged for v0.4.1
|
||||
|
||||
## 0.3.4 (2014-03-30)
|
||||
|
||||
* Changed StreamSelectLoop to use non-blocking behavior on tick() (@astephens25)
|
||||
|
||||
## 0.4.0 (2014-02-02)
|
||||
|
||||
* Feature: Added `EventLoopInterface::nextTick()`, implemented in all event loops (@jmalloc)
|
||||
* Feature: Added `EventLoopInterface::futureTick()`, implemented in all event loops (@jmalloc)
|
||||
* Feature: Added `ExtEventLoop` implementation using pecl/event (@jmalloc)
|
||||
* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks
|
||||
* BC break: New method: `EventLoopInterface::nextTick()`
|
||||
* BC break: New method: `EventLoopInterface::futureTick()`
|
||||
* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0
|
||||
|
||||
## 0.3.3 (2013-07-08)
|
||||
|
||||
* Bug fix: No error on removing non-existent streams (@clue)
|
||||
* Bug fix: Do not silently remove feof listeners in `LibEvLoop`
|
||||
|
||||
## 0.3.0 (2013-04-14)
|
||||
|
||||
* BC break: New timers API (@nrk)
|
||||
* BC break: Remove check on return value from stream callbacks (@nrk)
|
||||
|
||||
## 0.2.7 (2013-01-05)
|
||||
|
||||
* Bug fix: Fix libevent timers with PHP 5.3
|
||||
* Bug fix: Fix libevent timer cancellation (@nrk)
|
||||
|
||||
## 0.2.6 (2012-12-26)
|
||||
|
||||
* Bug fix: Plug memory issue in libevent timers (@cameronjacobson)
|
||||
* Bug fix: Correctly pause LibEvLoop on stop()
|
||||
|
||||
## 0.2.3 (2012-11-14)
|
||||
|
||||
* Feature: LibEvLoop, integration of `php-libev`
|
||||
|
||||
## 0.2.0 (2012-09-10)
|
||||
|
||||
* Version bump
|
||||
|
||||
## 0.1.1 (2012-07-12)
|
||||
|
||||
* Version bump
|
||||
|
||||
## 0.1.0 (2012-07-11)
|
||||
|
||||
* First tagged release
|
||||
19
vendor/react/event-loop/LICENSE
vendored
Executable file
19
vendor/react/event-loop/LICENSE
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2012 Igor Wiedler, Chris Boden
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
143
vendor/react/event-loop/README.md
vendored
Executable file
143
vendor/react/event-loop/README.md
vendored
Executable file
@@ -0,0 +1,143 @@
|
||||
# EventLoop Component
|
||||
|
||||
[](https://travis-ci.org/reactphp/event-loop)
|
||||
[](https://codeclimate.com/github/reactphp/event-loop)
|
||||
|
||||
Event loop abstraction layer that libraries can use for evented I/O.
|
||||
|
||||
In order for async based libraries to be interoperable, they need to use the
|
||||
same event loop. This component provides a common `LoopInterface` that any
|
||||
library can target. This allows them to be used in the same loop, with one
|
||||
single `run()` call that is controlled by the user.
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
* [Quickstart example](#quickstart-example)
|
||||
* [Usage](#usage)
|
||||
* [Loop implementations](#loop-implementations)
|
||||
* [Install](#install)
|
||||
* [Tests](#tests)
|
||||
* [License](#license)
|
||||
|
||||
## Quickstart example
|
||||
|
||||
Here is an async HTTP server built with just the event loop.
|
||||
|
||||
```php
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
$server = stream_socket_server('tcp://127.0.0.1:8080');
|
||||
stream_set_blocking($server, 0);
|
||||
|
||||
$loop->addReadStream($server, function ($server) use ($loop) {
|
||||
$conn = stream_socket_accept($server);
|
||||
$data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n";
|
||||
$loop->addWriteStream($conn, function ($conn) use (&$data, $loop) {
|
||||
$written = fwrite($conn, $data);
|
||||
if ($written === strlen($data)) {
|
||||
fclose($conn);
|
||||
$loop->removeStream($conn);
|
||||
} else {
|
||||
$data = substr($data, $written);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$loop->addPeriodicTimer(5, function () {
|
||||
$memory = memory_get_usage() / 1024;
|
||||
$formatted = number_format($memory, 3).'K';
|
||||
echo "Current memory usage: {$formatted}\n";
|
||||
});
|
||||
|
||||
$loop->run();
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Typical applications use a single event loop which is created at the beginning
|
||||
and run at the end of the program.
|
||||
|
||||
```php
|
||||
// [1]
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
// [2]
|
||||
$loop->addPeriodicTimer(1, function () {
|
||||
echo "Tick\n";
|
||||
});
|
||||
|
||||
$stream = new React\Stream\ReadableResourceStream(
|
||||
fopen('file.txt', 'r'),
|
||||
$loop
|
||||
);
|
||||
|
||||
// [3]
|
||||
$loop->run();
|
||||
```
|
||||
|
||||
1. The loop instance is created at the beginning of the program. A convenience
|
||||
factory `React\EventLoop\Factory::create()` is provided by this library which
|
||||
picks the best available [loop implementation](#loop-implementations).
|
||||
2. The loop instance is used directly or passed to library and application code.
|
||||
In this example, a periodic timer is registered with the event loop which
|
||||
simply outputs `Tick` every second and a
|
||||
[readable stream](https://github.com/reactphp/stream#readableresourcestream)
|
||||
is created by using ReactPHP's
|
||||
[stream component](https://github.com/reactphp/stream) for demonstration
|
||||
purposes.
|
||||
3. The loop is run with a single `$loop->run()` call at the end of the program.
|
||||
|
||||
## Loop implementations
|
||||
|
||||
In addition to the interface there are the following implementations provided:
|
||||
|
||||
* `StreamSelectLoop`: This is the only implementation which works out of the
|
||||
box with PHP. It does a simple `select` system call. It's not the most
|
||||
performant of loops, but still does the job quite well.
|
||||
|
||||
* `LibEventLoop`: This uses the `libevent` pecl extension. `libevent` itself
|
||||
supports a number of system-specific backends (epoll, kqueue).
|
||||
|
||||
* `LibEvLoop`: This uses the `libev` pecl extension
|
||||
([github](https://github.com/m4rw3r/php-libev)). It supports the same
|
||||
backends as libevent.
|
||||
|
||||
* `ExtEventLoop`: This uses the `event` pecl extension. It supports the same
|
||||
backends as libevent.
|
||||
|
||||
All of the loops support these features:
|
||||
|
||||
* File descriptor polling
|
||||
* One-off timers
|
||||
* Periodic timers
|
||||
* Deferred execution of callbacks
|
||||
|
||||
## Install
|
||||
|
||||
The recommended way to install this library is [through Composer](http://getcomposer.org).
|
||||
[New to Composer?](http://getcomposer.org/doc/00-intro.md)
|
||||
|
||||
This will install the latest supported version:
|
||||
|
||||
```bash
|
||||
$ composer require react/event-loop
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
To run the test suite, you first need to clone this repo and then install all
|
||||
dependencies [through Composer](http://getcomposer.org):
|
||||
|
||||
```bash
|
||||
$ composer install
|
||||
```
|
||||
|
||||
To run the test suite, go to the project root and run:
|
||||
|
||||
```bash
|
||||
$ php vendor/bin/phpunit
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT, see [LICENSE file](LICENSE).
|
||||
22
vendor/react/event-loop/composer.json
vendored
Executable file
22
vendor/react/event-loop/composer.json
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "react/event-loop",
|
||||
"description": "Event loop abstraction layer that libraries can use for evented I/O.",
|
||||
"keywords": ["event-loop", "asynchronous"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.8"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libevent": ">=0.1.0",
|
||||
"ext-event": "~1.0",
|
||||
"ext-libev": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"React\\EventLoop\\": "src"
|
||||
}
|
||||
}
|
||||
}
|
||||
25
vendor/react/event-loop/phpunit.xml.dist
vendored
Executable file
25
vendor/react/event-loop/phpunit.xml.dist
vendored
Executable file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="React Test Suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
326
vendor/react/event-loop/src/ExtEventLoop.php
vendored
Executable file
326
vendor/react/event-loop/src/ExtEventLoop.php
vendored
Executable file
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use Event;
|
||||
use EventBase;
|
||||
use EventConfig as EventBaseConfig;
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Tick\NextTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use React\EventLoop\Timer\TimerInterface;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* An ext-event based event-loop.
|
||||
*/
|
||||
class ExtEventLoop implements LoopInterface
|
||||
{
|
||||
private $eventBase;
|
||||
private $nextTickQueue;
|
||||
private $futureTickQueue;
|
||||
private $timerCallback;
|
||||
private $timerEvents;
|
||||
private $streamCallback;
|
||||
private $streamEvents = [];
|
||||
private $streamFlags = [];
|
||||
private $readListeners = [];
|
||||
private $writeListeners = [];
|
||||
private $running;
|
||||
|
||||
public function __construct(EventBaseConfig $config = null)
|
||||
{
|
||||
$this->eventBase = new EventBase($config);
|
||||
$this->nextTickQueue = new NextTickQueue($this);
|
||||
$this->futureTickQueue = new FutureTickQueue($this);
|
||||
$this->timerEvents = new SplObjectStorage();
|
||||
|
||||
$this->createTimerCallback();
|
||||
$this->createStreamCallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addReadStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->readListeners[$key])) {
|
||||
$this->readListeners[$key] = $listener;
|
||||
$this->subscribeStreamEvent($stream, Event::READ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addWriteStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->writeListeners[$key])) {
|
||||
$this->writeListeners[$key] = $listener;
|
||||
$this->subscribeStreamEvent($stream, Event::WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->readListeners[$key])) {
|
||||
unset($this->readListeners[$key]);
|
||||
$this->unsubscribeStreamEvent($stream, Event::READ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeListeners[$key])) {
|
||||
unset($this->writeListeners[$key]);
|
||||
$this->unsubscribeStreamEvent($stream, Event::WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->streamEvents[$key])) {
|
||||
$this->streamEvents[$key]->free();
|
||||
|
||||
unset(
|
||||
$this->streamFlags[$key],
|
||||
$this->streamEvents[$key],
|
||||
$this->readListeners[$key],
|
||||
$this->writeListeners[$key]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, false);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addPeriodicTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, true);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
if ($this->isTimerActive($timer)) {
|
||||
$this->timerEvents[$timer]->free();
|
||||
$this->timerEvents->detach($timer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTimerActive(TimerInterface $timer)
|
||||
{
|
||||
return $this->timerEvents->contains($timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function nextTick(callable $listener)
|
||||
{
|
||||
$this->nextTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function futureTick(callable $listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
// @-suppression: https://github.com/reactphp/react/pull/234#discussion-diff-7759616R226
|
||||
@$this->eventBase->loop(EventBase::LOOP_ONCE | EventBase::LOOP_NONBLOCK);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$flags = EventBase::LOOP_ONCE;
|
||||
if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
|
||||
$flags |= EventBase::LOOP_NONBLOCK;
|
||||
} elseif (!$this->streamEvents && !$this->timerEvents->count()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->eventBase->loop($flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a timer for execution.
|
||||
*
|
||||
* @param TimerInterface $timer
|
||||
*/
|
||||
private function scheduleTimer(TimerInterface $timer)
|
||||
{
|
||||
$flags = Event::TIMEOUT;
|
||||
|
||||
if ($timer->isPeriodic()) {
|
||||
$flags |= Event::PERSIST;
|
||||
}
|
||||
|
||||
$event = new Event($this->eventBase, -1, $flags, $this->timerCallback, $timer);
|
||||
$this->timerEvents[$timer] = $event;
|
||||
|
||||
$event->add($timer->getInterval());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ext-event Event object, or update the existing one.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param integer $flag Event::READ or Event::WRITE
|
||||
*/
|
||||
private function subscribeStreamEvent($stream, $flag)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->streamEvents[$key])) {
|
||||
$event = $this->streamEvents[$key];
|
||||
$flags = ($this->streamFlags[$key] |= $flag);
|
||||
|
||||
$event->del();
|
||||
$event->set($this->eventBase, $stream, Event::PERSIST | $flags, $this->streamCallback);
|
||||
} else {
|
||||
$event = new Event($this->eventBase, $stream, Event::PERSIST | $flag, $this->streamCallback);
|
||||
|
||||
$this->streamEvents[$key] = $event;
|
||||
$this->streamFlags[$key] = $flag;
|
||||
}
|
||||
|
||||
$event->add();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the ext-event Event object for this stream to stop listening to
|
||||
* the given event type, or remove it entirely if it's no longer needed.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param integer $flag Event::READ or Event::WRITE
|
||||
*/
|
||||
private function unsubscribeStreamEvent($stream, $flag)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
$flags = $this->streamFlags[$key] &= ~$flag;
|
||||
|
||||
if (0 === $flags) {
|
||||
$this->removeStream($stream);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$event = $this->streamEvents[$key];
|
||||
|
||||
$event->del();
|
||||
$event->set($this->eventBase, $stream, Event::PERSIST | $flags, $this->streamCallback);
|
||||
$event->add();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of timer events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createTimerCallback()
|
||||
{
|
||||
$this->timerCallback = function ($_, $__, $timer) {
|
||||
call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
if (!$timer->isPeriodic() && $this->isTimerActive($timer)) {
|
||||
$this->cancelTimer($timer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of stream events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createStreamCallback()
|
||||
{
|
||||
$this->streamCallback = function ($stream, $flags) {
|
||||
$key = (int) $stream;
|
||||
|
||||
if (Event::READ === (Event::READ & $flags) && isset($this->readListeners[$key])) {
|
||||
call_user_func($this->readListeners[$key], $stream, $this);
|
||||
}
|
||||
|
||||
if (Event::WRITE === (Event::WRITE & $flags) && isset($this->writeListeners[$key])) {
|
||||
call_user_func($this->writeListeners[$key], $stream, $this);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
21
vendor/react/event-loop/src/Factory.php
vendored
Executable file
21
vendor/react/event-loop/src/Factory.php
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
class Factory
|
||||
{
|
||||
public static function create()
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if (function_exists('event_base_new')) {
|
||||
return new LibEventLoop();
|
||||
} elseif (class_exists('libev\EventLoop', false)) {
|
||||
return new LibEvLoop;
|
||||
} elseif (class_exists('EventBase', false)) {
|
||||
return new ExtEventLoop;
|
||||
}
|
||||
|
||||
return new StreamSelectLoop();
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
226
vendor/react/event-loop/src/LibEvLoop.php
vendored
Executable file
226
vendor/react/event-loop/src/LibEvLoop.php
vendored
Executable file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use libev\EventLoop;
|
||||
use libev\IOEvent;
|
||||
use libev\TimerEvent;
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Tick\NextTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use React\EventLoop\Timer\TimerInterface;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* @see https://github.com/m4rw3r/php-libev
|
||||
* @see https://gist.github.com/1688204
|
||||
*/
|
||||
class LibEvLoop implements LoopInterface
|
||||
{
|
||||
private $loop;
|
||||
private $nextTickQueue;
|
||||
private $futureTickQueue;
|
||||
private $timerEvents;
|
||||
private $readEvents = [];
|
||||
private $writeEvents = [];
|
||||
private $running;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->loop = new EventLoop();
|
||||
$this->nextTickQueue = new NextTickQueue($this);
|
||||
$this->futureTickQueue = new FutureTickQueue($this);
|
||||
$this->timerEvents = new SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addReadStream($stream, callable $listener)
|
||||
{
|
||||
if (isset($this->readEvents[(int) $stream])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$callback = function () use ($stream, $listener) {
|
||||
call_user_func($listener, $stream, $this);
|
||||
};
|
||||
|
||||
$event = new IOEvent($callback, $stream, IOEvent::READ);
|
||||
$this->loop->add($event);
|
||||
|
||||
$this->readEvents[(int) $stream] = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addWriteStream($stream, callable $listener)
|
||||
{
|
||||
if (isset($this->writeEvents[(int) $stream])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$callback = function () use ($stream, $listener) {
|
||||
call_user_func($listener, $stream, $this);
|
||||
};
|
||||
|
||||
$event = new IOEvent($callback, $stream, IOEvent::WRITE);
|
||||
$this->loop->add($event);
|
||||
|
||||
$this->writeEvents[(int) $stream] = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->readEvents[$key])) {
|
||||
$this->readEvents[$key]->stop();
|
||||
unset($this->readEvents[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeEvents[$key])) {
|
||||
$this->writeEvents[$key]->stop();
|
||||
unset($this->writeEvents[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeStream($stream)
|
||||
{
|
||||
$this->removeReadStream($stream);
|
||||
$this->removeWriteStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, false);
|
||||
|
||||
$callback = function () use ($timer) {
|
||||
call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
if ($this->isTimerActive($timer)) {
|
||||
$this->cancelTimer($timer);
|
||||
}
|
||||
};
|
||||
|
||||
$event = new TimerEvent($callback, $timer->getInterval());
|
||||
$this->timerEvents->attach($timer, $event);
|
||||
$this->loop->add($event);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addPeriodicTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, true);
|
||||
|
||||
$callback = function () use ($timer) {
|
||||
call_user_func($timer->getCallback(), $timer);
|
||||
};
|
||||
|
||||
$event = new TimerEvent($callback, $interval, $interval);
|
||||
$this->timerEvents->attach($timer, $event);
|
||||
$this->loop->add($event);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
if (isset($this->timerEvents[$timer])) {
|
||||
$this->loop->remove($this->timerEvents[$timer]);
|
||||
$this->timerEvents->detach($timer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTimerActive(TimerInterface $timer)
|
||||
{
|
||||
return $this->timerEvents->contains($timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function nextTick(callable $listener)
|
||||
{
|
||||
$this->nextTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function futureTick(callable $listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$this->loop->run(EventLoop::RUN_ONCE | EventLoop::RUN_NOWAIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$flags = EventLoop::RUN_ONCE;
|
||||
if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
|
||||
$flags |= EventLoop::RUN_NOWAIT;
|
||||
} elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->loop->run($flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
}
|
||||
343
vendor/react/event-loop/src/LibEventLoop.php
vendored
Executable file
343
vendor/react/event-loop/src/LibEventLoop.php
vendored
Executable file
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use Event;
|
||||
use EventBase;
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Tick\NextTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use React\EventLoop\Timer\TimerInterface;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* An ext-libevent based event-loop.
|
||||
*/
|
||||
class LibEventLoop implements LoopInterface
|
||||
{
|
||||
const MICROSECONDS_PER_SECOND = 1000000;
|
||||
|
||||
private $eventBase;
|
||||
private $nextTickQueue;
|
||||
private $futureTickQueue;
|
||||
private $timerCallback;
|
||||
private $timerEvents;
|
||||
private $streamCallback;
|
||||
private $streamEvents = [];
|
||||
private $streamFlags = [];
|
||||
private $readListeners = [];
|
||||
private $writeListeners = [];
|
||||
private $running;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->eventBase = event_base_new();
|
||||
$this->nextTickQueue = new NextTickQueue($this);
|
||||
$this->futureTickQueue = new FutureTickQueue($this);
|
||||
$this->timerEvents = new SplObjectStorage();
|
||||
|
||||
$this->createTimerCallback();
|
||||
$this->createStreamCallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addReadStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->readListeners[$key])) {
|
||||
$this->readListeners[$key] = $listener;
|
||||
$this->subscribeStreamEvent($stream, EV_READ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addWriteStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->writeListeners[$key])) {
|
||||
$this->writeListeners[$key] = $listener;
|
||||
$this->subscribeStreamEvent($stream, EV_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->readListeners[$key])) {
|
||||
unset($this->readListeners[$key]);
|
||||
$this->unsubscribeStreamEvent($stream, EV_READ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeListeners[$key])) {
|
||||
unset($this->writeListeners[$key]);
|
||||
$this->unsubscribeStreamEvent($stream, EV_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->streamEvents[$key])) {
|
||||
$event = $this->streamEvents[$key];
|
||||
|
||||
event_del($event);
|
||||
event_free($event);
|
||||
|
||||
unset(
|
||||
$this->streamFlags[$key],
|
||||
$this->streamEvents[$key],
|
||||
$this->readListeners[$key],
|
||||
$this->writeListeners[$key]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, false);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addPeriodicTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, true);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
if ($this->isTimerActive($timer)) {
|
||||
$event = $this->timerEvents[$timer];
|
||||
|
||||
event_del($event);
|
||||
event_free($event);
|
||||
|
||||
$this->timerEvents->detach($timer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTimerActive(TimerInterface $timer)
|
||||
{
|
||||
return $this->timerEvents->contains($timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function nextTick(callable $listener)
|
||||
{
|
||||
$this->nextTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function futureTick(callable $listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
event_base_loop($this->eventBase, EVLOOP_ONCE | EVLOOP_NONBLOCK);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$flags = EVLOOP_ONCE;
|
||||
if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
|
||||
$flags |= EVLOOP_NONBLOCK;
|
||||
} elseif (!$this->streamEvents && !$this->timerEvents->count()) {
|
||||
break;
|
||||
}
|
||||
|
||||
event_base_loop($this->eventBase, $flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a timer for execution.
|
||||
*
|
||||
* @param TimerInterface $timer
|
||||
*/
|
||||
private function scheduleTimer(TimerInterface $timer)
|
||||
{
|
||||
$this->timerEvents[$timer] = $event = event_timer_new();
|
||||
|
||||
event_timer_set($event, $this->timerCallback, $timer);
|
||||
event_base_set($event, $this->eventBase);
|
||||
event_add($event, $timer->getInterval() * self::MICROSECONDS_PER_SECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ext-libevent event resource, or update the existing one.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param integer $flag EV_READ or EV_WRITE
|
||||
*/
|
||||
private function subscribeStreamEvent($stream, $flag)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->streamEvents[$key])) {
|
||||
$event = $this->streamEvents[$key];
|
||||
$flags = $this->streamFlags[$key] |= $flag;
|
||||
|
||||
event_del($event);
|
||||
event_set($event, $stream, EV_PERSIST | $flags, $this->streamCallback);
|
||||
} else {
|
||||
$event = event_new();
|
||||
|
||||
event_set($event, $stream, EV_PERSIST | $flag, $this->streamCallback);
|
||||
event_base_set($event, $this->eventBase);
|
||||
|
||||
$this->streamEvents[$key] = $event;
|
||||
$this->streamFlags[$key] = $flag;
|
||||
}
|
||||
|
||||
event_add($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the ext-libevent event resource for this stream to stop listening to
|
||||
* the given event type, or remove it entirely if it's no longer needed.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param integer $flag EV_READ or EV_WRITE
|
||||
*/
|
||||
private function unsubscribeStreamEvent($stream, $flag)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
$flags = $this->streamFlags[$key] &= ~$flag;
|
||||
|
||||
if (0 === $flags) {
|
||||
$this->removeStream($stream);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$event = $this->streamEvents[$key];
|
||||
|
||||
event_del($event);
|
||||
event_set($event, $stream, EV_PERSIST | $flags, $this->streamCallback);
|
||||
event_add($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of timer events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createTimerCallback()
|
||||
{
|
||||
$this->timerCallback = function ($_, $__, $timer) {
|
||||
call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
// Timer already cancelled ...
|
||||
if (!$this->isTimerActive($timer)) {
|
||||
return;
|
||||
|
||||
// Reschedule periodic timers ...
|
||||
} elseif ($timer->isPeriodic()) {
|
||||
event_add(
|
||||
$this->timerEvents[$timer],
|
||||
$timer->getInterval() * self::MICROSECONDS_PER_SECOND
|
||||
);
|
||||
|
||||
// Clean-up one shot timers ...
|
||||
} else {
|
||||
$this->cancelTimer($timer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of stream events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createStreamCallback()
|
||||
{
|
||||
$this->streamCallback = function ($stream, $flags) {
|
||||
$key = (int) $stream;
|
||||
|
||||
if (EV_READ === (EV_READ & $flags) && isset($this->readListeners[$key])) {
|
||||
call_user_func($this->readListeners[$key], $stream, $this);
|
||||
}
|
||||
|
||||
if (EV_WRITE === (EV_WRITE & $flags) && isset($this->writeListeners[$key])) {
|
||||
call_user_func($this->writeListeners[$key], $stream, $this);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
121
vendor/react/event-loop/src/LoopInterface.php
vendored
Executable file
121
vendor/react/event-loop/src/LoopInterface.php
vendored
Executable file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use React\EventLoop\Timer\TimerInterface;
|
||||
|
||||
interface LoopInterface
|
||||
{
|
||||
/**
|
||||
* Register a listener to be notified when a stream is ready to read.
|
||||
*
|
||||
* @param resource $stream The PHP stream resource to check.
|
||||
* @param callable $listener Invoked when the stream is ready.
|
||||
*/
|
||||
public function addReadStream($stream, callable $listener);
|
||||
|
||||
/**
|
||||
* Register a listener to be notified when a stream is ready to write.
|
||||
*
|
||||
* @param resource $stream The PHP stream resource to check.
|
||||
* @param callable $listener Invoked when the stream is ready.
|
||||
*/
|
||||
public function addWriteStream($stream, callable $listener);
|
||||
|
||||
/**
|
||||
* Remove the read event listener for the given stream.
|
||||
*
|
||||
* @param resource $stream The PHP stream resource.
|
||||
*/
|
||||
public function removeReadStream($stream);
|
||||
|
||||
/**
|
||||
* Remove the write event listener for the given stream.
|
||||
*
|
||||
* @param resource $stream The PHP stream resource.
|
||||
*/
|
||||
public function removeWriteStream($stream);
|
||||
|
||||
/**
|
||||
* Remove all listeners for the given stream.
|
||||
*
|
||||
* @param resource $stream The PHP stream resource.
|
||||
*/
|
||||
public function removeStream($stream);
|
||||
|
||||
/**
|
||||
* Enqueue a callback to be invoked once after the given interval.
|
||||
*
|
||||
* The execution order of timers scheduled to execute at the same time is
|
||||
* not guaranteed.
|
||||
*
|
||||
* @param int|float $interval The number of seconds to wait before execution.
|
||||
* @param callable $callback The callback to invoke.
|
||||
*
|
||||
* @return TimerInterface
|
||||
*/
|
||||
public function addTimer($interval, callable $callback);
|
||||
|
||||
/**
|
||||
* Enqueue a callback to be invoked repeatedly after the given interval.
|
||||
*
|
||||
* The execution order of timers scheduled to execute at the same time is
|
||||
* not guaranteed.
|
||||
*
|
||||
* @param int|float $interval The number of seconds to wait before execution.
|
||||
* @param callable $callback The callback to invoke.
|
||||
*
|
||||
* @return TimerInterface
|
||||
*/
|
||||
public function addPeriodicTimer($interval, callable $callback);
|
||||
|
||||
/**
|
||||
* Cancel a pending timer.
|
||||
*
|
||||
* @param TimerInterface $timer The timer to cancel.
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer);
|
||||
|
||||
/**
|
||||
* Check if a given timer is active.
|
||||
*
|
||||
* @param TimerInterface $timer The timer to check.
|
||||
*
|
||||
* @return boolean True if the timer is still enqueued for execution.
|
||||
*/
|
||||
public function isTimerActive(TimerInterface $timer);
|
||||
|
||||
/**
|
||||
* Schedule a callback to be invoked on the next tick of the event loop.
|
||||
*
|
||||
* Callbacks are guaranteed to be executed in the order they are enqueued,
|
||||
* before any timer or stream events.
|
||||
*
|
||||
* @param callable $listener The callback to invoke.
|
||||
*/
|
||||
public function nextTick(callable $listener);
|
||||
|
||||
/**
|
||||
* Schedule a callback to be invoked on a future tick of the event loop.
|
||||
*
|
||||
* Callbacks are guaranteed to be executed in the order they are enqueued.
|
||||
*
|
||||
* @param callable $listener The callback to invoke.
|
||||
*/
|
||||
public function futureTick(callable $listener);
|
||||
|
||||
/**
|
||||
* Perform a single iteration of the event loop.
|
||||
*/
|
||||
public function tick();
|
||||
|
||||
/**
|
||||
* Run the event loop until there are no more tasks to perform.
|
||||
*/
|
||||
public function run();
|
||||
|
||||
/**
|
||||
* Instruct a running event loop to stop.
|
||||
*/
|
||||
public function stop();
|
||||
}
|
||||
273
vendor/react/event-loop/src/StreamSelectLoop.php
vendored
Executable file
273
vendor/react/event-loop/src/StreamSelectLoop.php
vendored
Executable file
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Tick\NextTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use React\EventLoop\Timer\TimerInterface;
|
||||
use React\EventLoop\Timer\Timers;
|
||||
|
||||
/**
|
||||
* A stream_select() based event-loop.
|
||||
*/
|
||||
class StreamSelectLoop implements LoopInterface
|
||||
{
|
||||
const MICROSECONDS_PER_SECOND = 1000000;
|
||||
|
||||
private $nextTickQueue;
|
||||
private $futureTickQueue;
|
||||
private $timers;
|
||||
private $readStreams = [];
|
||||
private $readListeners = [];
|
||||
private $writeStreams = [];
|
||||
private $writeListeners = [];
|
||||
private $running;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->nextTickQueue = new NextTickQueue($this);
|
||||
$this->futureTickQueue = new FutureTickQueue($this);
|
||||
$this->timers = new Timers();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addReadStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->readStreams[$key])) {
|
||||
$this->readStreams[$key] = $stream;
|
||||
$this->readListeners[$key] = $listener;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addWriteStream($stream, callable $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->writeStreams[$key])) {
|
||||
$this->writeStreams[$key] = $stream;
|
||||
$this->writeListeners[$key] = $listener;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
unset(
|
||||
$this->readStreams[$key],
|
||||
$this->readListeners[$key]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
unset(
|
||||
$this->writeStreams[$key],
|
||||
$this->writeListeners[$key]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeStream($stream)
|
||||
{
|
||||
$this->removeReadStream($stream);
|
||||
$this->removeWriteStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, false);
|
||||
|
||||
$this->timers->add($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addPeriodicTimer($interval, callable $callback)
|
||||
{
|
||||
$timer = new Timer($this, $interval, $callback, true);
|
||||
|
||||
$this->timers->add($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
$this->timers->cancel($timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTimerActive(TimerInterface $timer)
|
||||
{
|
||||
return $this->timers->contains($timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function nextTick(callable $listener)
|
||||
{
|
||||
$this->nextTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function futureTick(callable $listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$this->timers->tick();
|
||||
|
||||
$this->waitForStreamActivity(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->nextTickQueue->tick();
|
||||
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$this->timers->tick();
|
||||
|
||||
// Next-tick or future-tick queues have pending callbacks ...
|
||||
if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
|
||||
$timeout = 0;
|
||||
|
||||
// There is a pending timer, only block until it is due ...
|
||||
} elseif ($scheduledAt = $this->timers->getFirst()) {
|
||||
$timeout = $scheduledAt - $this->timers->getTime();
|
||||
if ($timeout < 0) {
|
||||
$timeout = 0;
|
||||
} else {
|
||||
/*
|
||||
* round() needed to correct float error:
|
||||
* https://github.com/reactphp/event-loop/issues/48
|
||||
*/
|
||||
$timeout = round($timeout * self::MICROSECONDS_PER_SECOND);
|
||||
}
|
||||
|
||||
// The only possible event is stream activity, so wait forever ...
|
||||
} elseif ($this->readStreams || $this->writeStreams) {
|
||||
$timeout = null;
|
||||
|
||||
// There's nothing left to do ...
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->waitForStreamActivity($timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait/check for stream activity, or until the next timer is due.
|
||||
*/
|
||||
private function waitForStreamActivity($timeout)
|
||||
{
|
||||
$read = $this->readStreams;
|
||||
$write = $this->writeStreams;
|
||||
|
||||
$available = $this->streamSelect($read, $write, $timeout);
|
||||
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, $this);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($write as $stream) {
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeListeners[$key])) {
|
||||
call_user_func($this->writeListeners[$key], $stream, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulate a stream_select() implementation that does not break when passed
|
||||
* empty stream arrays.
|
||||
*
|
||||
* @param array &$read An array of read streams to select upon.
|
||||
* @param array &$write An array of write streams to select upon.
|
||||
* @param integer|null $timeout Activity timeout in microseconds, or null to wait forever.
|
||||
*
|
||||
* @return integer|false The total number of streams that are ready for read/write.
|
||||
* Can return false if stream_select() is interrupted by a signal.
|
||||
*/
|
||||
protected function streamSelect(array &$read, array &$write, $timeout)
|
||||
{
|
||||
if ($read || $write) {
|
||||
$except = null;
|
||||
|
||||
// suppress warnings that occur, when stream_select is interrupted by a signal
|
||||
return @stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout);
|
||||
}
|
||||
|
||||
$timeout && usleep($timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
59
vendor/react/event-loop/src/Tick/FutureTickQueue.php
vendored
Executable file
59
vendor/react/event-loop/src/Tick/FutureTickQueue.php
vendored
Executable file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop\Tick;
|
||||
|
||||
use React\EventLoop\LoopInterface;
|
||||
use SplQueue;
|
||||
|
||||
class FutureTickQueue
|
||||
{
|
||||
private $eventLoop;
|
||||
private $queue;
|
||||
|
||||
/**
|
||||
* @param LoopInterface $eventLoop The event loop passed as the first parameter to callbacks.
|
||||
*/
|
||||
public function __construct(LoopInterface $eventLoop)
|
||||
{
|
||||
$this->eventLoop = $eventLoop;
|
||||
$this->queue = new SplQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback to be invoked on a future tick of the event loop.
|
||||
*
|
||||
* Callbacks are guaranteed to be executed in the order they are enqueued.
|
||||
*
|
||||
* @param callable $listener The callback to invoke.
|
||||
*/
|
||||
public function add(callable $listener)
|
||||
{
|
||||
$this->queue->enqueue($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the callback queue.
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
// Only invoke as many callbacks as were on the queue when tick() was called.
|
||||
$count = $this->queue->count();
|
||||
|
||||
while ($count--) {
|
||||
call_user_func(
|
||||
$this->queue->dequeue(),
|
||||
$this->eventLoop
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the next tick queue is empty.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->queue->isEmpty();
|
||||
}
|
||||
}
|
||||
57
vendor/react/event-loop/src/Tick/NextTickQueue.php
vendored
Executable file
57
vendor/react/event-loop/src/Tick/NextTickQueue.php
vendored
Executable file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop\Tick;
|
||||
|
||||
use React\EventLoop\LoopInterface;
|
||||
use SplQueue;
|
||||
|
||||
class NextTickQueue
|
||||
{
|
||||
private $eventLoop;
|
||||
private $queue;
|
||||
|
||||
/**
|
||||
* @param LoopInterface $eventLoop The event loop passed as the first parameter to callbacks.
|
||||
*/
|
||||
public function __construct(LoopInterface $eventLoop)
|
||||
{
|
||||
$this->eventLoop = $eventLoop;
|
||||
$this->queue = new SplQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback to be invoked on the next tick of the event loop.
|
||||
*
|
||||
* Callbacks are guaranteed to be executed in the order they are enqueued,
|
||||
* before any timer or stream events.
|
||||
*
|
||||
* @param callable $listener The callback to invoke.
|
||||
*/
|
||||
public function add(callable $listener)
|
||||
{
|
||||
$this->queue->enqueue($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the callback queue.
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
while (!$this->queue->isEmpty()) {
|
||||
call_user_func(
|
||||
$this->queue->dequeue(),
|
||||
$this->eventLoop
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the next tick queue is empty.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->queue->isEmpty();
|
||||
}
|
||||
}
|
||||
102
vendor/react/event-loop/src/Timer/Timer.php
vendored
Executable file
102
vendor/react/event-loop/src/Timer/Timer.php
vendored
Executable file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\LoopInterface;
|
||||
|
||||
class Timer implements TimerInterface
|
||||
{
|
||||
const MIN_INTERVAL = 0.000001;
|
||||
|
||||
protected $loop;
|
||||
protected $interval;
|
||||
protected $callback;
|
||||
protected $periodic;
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Constructor initializes the fields of the Timer
|
||||
*
|
||||
* @param LoopInterface $loop The loop with which this timer is associated
|
||||
* @param float $interval The interval after which this timer will execute, in seconds
|
||||
* @param callable $callback The callback that will be executed when this timer elapses
|
||||
* @param bool $periodic Whether the time is periodic
|
||||
* @param mixed $data Arbitrary data associated with timer
|
||||
*/
|
||||
public function __construct(LoopInterface $loop, $interval, callable $callback, $periodic = false, $data = null)
|
||||
{
|
||||
if ($interval < self::MIN_INTERVAL) {
|
||||
$interval = self::MIN_INTERVAL;
|
||||
}
|
||||
|
||||
$this->loop = $loop;
|
||||
$this->interval = (float) $interval;
|
||||
$this->callback = $callback;
|
||||
$this->periodic = (bool) $periodic;
|
||||
$this->data = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLoop()
|
||||
{
|
||||
return $this->loop;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInterval()
|
||||
{
|
||||
return $this->interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCallback()
|
||||
{
|
||||
return $this->callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPeriodic()
|
||||
{
|
||||
return $this->periodic;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isActive()
|
||||
{
|
||||
return $this->loop->isTimerActive($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cancel()
|
||||
{
|
||||
$this->loop->cancelTimer($this);
|
||||
}
|
||||
}
|
||||
62
vendor/react/event-loop/src/Timer/TimerInterface.php
vendored
Executable file
62
vendor/react/event-loop/src/Timer/TimerInterface.php
vendored
Executable file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\LoopInterface;
|
||||
|
||||
interface TimerInterface
|
||||
{
|
||||
/**
|
||||
* Get the loop with which this timer is associated
|
||||
*
|
||||
* @return LoopInterface
|
||||
*/
|
||||
public function getLoop();
|
||||
|
||||
/**
|
||||
* Get the interval after which this timer will execute, in seconds
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getInterval();
|
||||
|
||||
/**
|
||||
* Get the callback that will be executed when this timer elapses
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function getCallback();
|
||||
|
||||
/**
|
||||
* Set arbitrary data associated with timer
|
||||
*
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function setData($data);
|
||||
|
||||
/**
|
||||
* Get arbitrary data associated with timer
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData();
|
||||
|
||||
/**
|
||||
* Determine whether the time is periodic
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPeriodic();
|
||||
|
||||
/**
|
||||
* Determine whether the time is active
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isActive();
|
||||
|
||||
/**
|
||||
* Cancel this timer
|
||||
*/
|
||||
public function cancel();
|
||||
}
|
||||
100
vendor/react/event-loop/src/Timer/Timers.php
vendored
Executable file
100
vendor/react/event-loop/src/Timer/Timers.php
vendored
Executable file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace React\EventLoop\Timer;
|
||||
|
||||
use SplObjectStorage;
|
||||
use SplPriorityQueue;
|
||||
|
||||
class Timers
|
||||
{
|
||||
private $time;
|
||||
private $timers;
|
||||
private $scheduler;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->timers = new SplObjectStorage();
|
||||
$this->scheduler = new SplPriorityQueue();
|
||||
}
|
||||
|
||||
public function updateTime()
|
||||
{
|
||||
return $this->time = microtime(true);
|
||||
}
|
||||
|
||||
public function getTime()
|
||||
{
|
||||
return $this->time ?: $this->updateTime();
|
||||
}
|
||||
|
||||
public function add(TimerInterface $timer)
|
||||
{
|
||||
$interval = $timer->getInterval();
|
||||
$scheduledAt = $interval + microtime(true);
|
||||
|
||||
$this->timers->attach($timer, $scheduledAt);
|
||||
$this->scheduler->insert($timer, -$scheduledAt);
|
||||
}
|
||||
|
||||
public function contains(TimerInterface $timer)
|
||||
{
|
||||
return $this->timers->contains($timer);
|
||||
}
|
||||
|
||||
public function cancel(TimerInterface $timer)
|
||||
{
|
||||
$this->timers->detach($timer);
|
||||
}
|
||||
|
||||
public function getFirst()
|
||||
{
|
||||
while ($this->scheduler->count()) {
|
||||
$timer = $this->scheduler->top();
|
||||
|
||||
if ($this->timers->contains($timer)) {
|
||||
return $this->timers[$timer];
|
||||
}
|
||||
|
||||
$this->scheduler->extract();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
{
|
||||
return count($this->timers) === 0;
|
||||
}
|
||||
|
||||
public function tick()
|
||||
{
|
||||
$time = $this->updateTime();
|
||||
$timers = $this->timers;
|
||||
$scheduler = $this->scheduler;
|
||||
|
||||
while (!$scheduler->isEmpty()) {
|
||||
$timer = $scheduler->top();
|
||||
|
||||
if (!isset($timers[$timer])) {
|
||||
$scheduler->extract();
|
||||
$timers->detach($timer);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($timers[$timer] >= $time) {
|
||||
break;
|
||||
}
|
||||
|
||||
$scheduler->extract();
|
||||
call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
if ($timer->isPeriodic() && isset($timers[$timer])) {
|
||||
$timers[$timer] = $scheduledAt = $timer->getInterval() + $time;
|
||||
$scheduler->insert($timer, -$scheduledAt);
|
||||
} else {
|
||||
$timers->detach($timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
539
vendor/react/event-loop/tests/AbstractLoopTest.php
vendored
Executable file
539
vendor/react/event-loop/tests/AbstractLoopTest.php
vendored
Executable file
@@ -0,0 +1,539 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
abstract class AbstractLoopTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \React\EventLoop\LoopInterface
|
||||
*/
|
||||
protected $loop;
|
||||
|
||||
private $tickTimeout;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
// HHVM is a bit slow, so give it more time
|
||||
$this->tickTimeout = defined('HHVM_VERSION') ? 0.02 : 0.005;
|
||||
$this->loop = $this->createLoop();
|
||||
}
|
||||
|
||||
abstract public function createLoop();
|
||||
|
||||
public function createSocketPair()
|
||||
{
|
||||
$domain = (DIRECTORY_SEPARATOR === '\\') ? STREAM_PF_INET : STREAM_PF_UNIX;
|
||||
$sockets = stream_socket_pair($domain, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
|
||||
|
||||
foreach ($sockets as $socket) {
|
||||
if (function_exists('stream_set_read_buffer')) {
|
||||
stream_set_read_buffer($socket, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return $sockets;
|
||||
}
|
||||
|
||||
public function testAddReadStream()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addReadStream($input, $this->expectCallableExactly(2));
|
||||
|
||||
fwrite($output, "foo\n");
|
||||
$this->loop->tick();
|
||||
|
||||
fwrite($output, "bar\n");
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testAddReadStreamIgnoresSecondCallable()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addReadStream($input, $this->expectCallableExactly(2));
|
||||
$this->loop->addReadStream($input, $this->expectCallableNever());
|
||||
|
||||
fwrite($output, "foo\n");
|
||||
$this->loop->tick();
|
||||
|
||||
fwrite($output, "bar\n");
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testAddWriteStream()
|
||||
{
|
||||
list ($input) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream($input, $this->expectCallableExactly(2));
|
||||
$this->loop->tick();
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testAddWriteStreamIgnoresSecondCallable()
|
||||
{
|
||||
list ($input) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream($input, $this->expectCallableExactly(2));
|
||||
$this->loop->addWriteStream($input, $this->expectCallableNever());
|
||||
$this->loop->tick();
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRemoveReadStreamInstantly()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addReadStream($input, $this->expectCallableNever());
|
||||
$this->loop->removeReadStream($input);
|
||||
|
||||
fwrite($output, "bar\n");
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRemoveReadStreamAfterReading()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addReadStream($input, $this->expectCallableOnce());
|
||||
|
||||
fwrite($output, "foo\n");
|
||||
$this->loop->tick();
|
||||
|
||||
$this->loop->removeReadStream($input);
|
||||
|
||||
fwrite($output, "bar\n");
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRemoveWriteStreamInstantly()
|
||||
{
|
||||
list ($input) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream($input, $this->expectCallableNever());
|
||||
$this->loop->removeWriteStream($input);
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRemoveWriteStreamAfterWriting()
|
||||
{
|
||||
list ($input) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream($input, $this->expectCallableOnce());
|
||||
$this->loop->tick();
|
||||
|
||||
$this->loop->removeWriteStream($input);
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRemoveStreamInstantly()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addReadStream($input, $this->expectCallableNever());
|
||||
$this->loop->addWriteStream($input, $this->expectCallableNever());
|
||||
$this->loop->removeStream($input);
|
||||
|
||||
fwrite($output, "bar\n");
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRemoveStreamForReadOnly()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addReadStream($input, $this->expectCallableNever());
|
||||
$this->loop->addWriteStream($output, $this->expectCallableOnce());
|
||||
$this->loop->removeReadStream($input);
|
||||
|
||||
fwrite($output, "foo\n");
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRemoveStreamForWriteOnly()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
fwrite($output, "foo\n");
|
||||
|
||||
$this->loop->addReadStream($input, $this->expectCallableOnce());
|
||||
$this->loop->addWriteStream($output, $this->expectCallableNever());
|
||||
$this->loop->removeWriteStream($output);
|
||||
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRemoveStream()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addReadStream($input, $this->expectCallableOnce());
|
||||
$this->loop->addWriteStream($input, $this->expectCallableOnce());
|
||||
|
||||
fwrite($output, "bar\n");
|
||||
$this->loop->tick();
|
||||
|
||||
$this->loop->removeStream($input);
|
||||
|
||||
fwrite($output, "bar\n");
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRemoveInvalid()
|
||||
{
|
||||
list ($stream) = $this->createSocketPair();
|
||||
|
||||
// remove a valid stream from the event loop that was never added in the first place
|
||||
$this->loop->removeReadStream($stream);
|
||||
$this->loop->removeWriteStream($stream);
|
||||
$this->loop->removeStream($stream);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function emptyRunShouldSimplyReturn()
|
||||
{
|
||||
$this->assertRunFasterThan($this->tickTimeout);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function runShouldReturnWhenNoMoreFds()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
$loop = $this->loop;
|
||||
$this->loop->addReadStream($input, function ($stream) use ($loop) {
|
||||
$loop->removeStream($stream);
|
||||
});
|
||||
|
||||
fwrite($output, "foo\n");
|
||||
|
||||
$this->assertRunFasterThan($this->tickTimeout * 2);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function stopShouldStopRunningLoop()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
$loop = $this->loop;
|
||||
$this->loop->addReadStream($input, function ($stream) use ($loop) {
|
||||
$loop->stop();
|
||||
});
|
||||
|
||||
fwrite($output, "foo\n");
|
||||
|
||||
$this->assertRunFasterThan($this->tickTimeout * 2);
|
||||
}
|
||||
|
||||
public function testStopShouldPreventRunFromBlocking()
|
||||
{
|
||||
$this->loop->addTimer(
|
||||
1,
|
||||
function () {
|
||||
$this->fail('Timer was executed.');
|
||||
}
|
||||
);
|
||||
|
||||
$this->loop->nextTick(
|
||||
function () {
|
||||
$this->loop->stop();
|
||||
}
|
||||
);
|
||||
|
||||
$this->assertRunFasterThan($this->tickTimeout * 2);
|
||||
}
|
||||
|
||||
public function testIgnoreRemovedCallback()
|
||||
{
|
||||
// two independent streams, both should be readable right away
|
||||
list ($input1, $output1) = $this->createSocketPair();
|
||||
list ($input2, $output2) = $this->createSocketPair();
|
||||
|
||||
$called = false;
|
||||
|
||||
$loop = $this->loop;
|
||||
$loop->addReadStream($input1, function ($stream) use (& $called, $loop, $input2) {
|
||||
// stream1 is readable, remove stream2 as well => this will invalidate its callback
|
||||
$loop->removeReadStream($stream);
|
||||
$loop->removeReadStream($input2);
|
||||
|
||||
$called = true;
|
||||
});
|
||||
|
||||
// this callback would have to be called as well, but the first stream already removed us
|
||||
$loop->addReadStream($input2, function () use (& $called) {
|
||||
if ($called) {
|
||||
$this->fail('Callback 2 must not be called after callback 1 was called');
|
||||
}
|
||||
});
|
||||
|
||||
fwrite($output1, "foo\n");
|
||||
fwrite($output2, "foo\n");
|
||||
|
||||
$loop->run();
|
||||
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
public function testNextTick()
|
||||
{
|
||||
$called = false;
|
||||
|
||||
$callback = function ($loop) use (&$called) {
|
||||
$this->assertSame($this->loop, $loop);
|
||||
$called = true;
|
||||
};
|
||||
|
||||
$this->loop->nextTick($callback);
|
||||
|
||||
$this->assertFalse($called);
|
||||
|
||||
$this->loop->tick();
|
||||
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
public function testNextTickFiresBeforeIO()
|
||||
{
|
||||
list ($stream) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream(
|
||||
$stream,
|
||||
function () {
|
||||
echo 'stream' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
|
||||
$this->loop->nextTick(
|
||||
function () {
|
||||
echo 'next-tick' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
|
||||
$this->expectOutputString('next-tick' . PHP_EOL . 'stream' . PHP_EOL);
|
||||
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRecursiveNextTick()
|
||||
{
|
||||
list ($stream) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream(
|
||||
$stream,
|
||||
function () {
|
||||
echo 'stream' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
|
||||
$this->loop->nextTick(
|
||||
function () {
|
||||
$this->loop->nextTick(
|
||||
function () {
|
||||
echo 'next-tick' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$this->expectOutputString('next-tick' . PHP_EOL . 'stream' . PHP_EOL);
|
||||
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRunWaitsForNextTickEvents()
|
||||
{
|
||||
list ($stream) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream(
|
||||
$stream,
|
||||
function () use ($stream) {
|
||||
$this->loop->removeStream($stream);
|
||||
$this->loop->nextTick(
|
||||
function () {
|
||||
echo 'next-tick' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$this->expectOutputString('next-tick' . PHP_EOL);
|
||||
|
||||
$this->loop->run();
|
||||
}
|
||||
|
||||
public function testNextTickEventGeneratedByFutureTick()
|
||||
{
|
||||
list ($stream) = $this->createSocketPair();
|
||||
|
||||
$this->loop->futureTick(
|
||||
function () {
|
||||
$this->loop->nextTick(
|
||||
function () {
|
||||
echo 'next-tick' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$this->expectOutputString('next-tick' . PHP_EOL);
|
||||
|
||||
$this->loop->run();
|
||||
}
|
||||
|
||||
public function testNextTickEventGeneratedByTimer()
|
||||
{
|
||||
$this->loop->addTimer(
|
||||
0.001,
|
||||
function () {
|
||||
$this->loop->nextTick(
|
||||
function () {
|
||||
echo 'next-tick' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$this->expectOutputString('next-tick' . PHP_EOL);
|
||||
|
||||
$this->loop->run();
|
||||
}
|
||||
|
||||
public function testFutureTick()
|
||||
{
|
||||
$called = false;
|
||||
|
||||
$callback = function ($loop) use (&$called) {
|
||||
$this->assertSame($this->loop, $loop);
|
||||
$called = true;
|
||||
};
|
||||
|
||||
$this->loop->futureTick($callback);
|
||||
|
||||
$this->assertFalse($called);
|
||||
|
||||
$this->loop->tick();
|
||||
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
public function testFutureTickFiresBeforeIO()
|
||||
{
|
||||
list ($stream) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream(
|
||||
$stream,
|
||||
function () {
|
||||
echo 'stream' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
|
||||
$this->loop->futureTick(
|
||||
function () {
|
||||
echo 'future-tick' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
|
||||
$this->expectOutputString('future-tick' . PHP_EOL . 'stream' . PHP_EOL);
|
||||
|
||||
$this->loop->tick();
|
||||
}
|
||||
|
||||
public function testRecursiveFutureTick()
|
||||
{
|
||||
list ($stream) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream(
|
||||
$stream,
|
||||
function () use ($stream) {
|
||||
echo 'stream' . PHP_EOL;
|
||||
$this->loop->removeWriteStream($stream);
|
||||
}
|
||||
);
|
||||
|
||||
$this->loop->futureTick(
|
||||
function () {
|
||||
echo 'future-tick-1' . PHP_EOL;
|
||||
$this->loop->futureTick(
|
||||
function () {
|
||||
echo 'future-tick-2' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$this->expectOutputString('future-tick-1' . PHP_EOL . 'stream' . PHP_EOL . 'future-tick-2' . PHP_EOL);
|
||||
|
||||
$this->loop->run();
|
||||
}
|
||||
|
||||
public function testRunWaitsForFutureTickEvents()
|
||||
{
|
||||
list ($stream) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream(
|
||||
$stream,
|
||||
function () use ($stream) {
|
||||
$this->loop->removeStream($stream);
|
||||
$this->loop->futureTick(
|
||||
function () {
|
||||
echo 'future-tick' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$this->expectOutputString('future-tick' . PHP_EOL);
|
||||
|
||||
$this->loop->run();
|
||||
}
|
||||
|
||||
public function testFutureTickEventGeneratedByNextTick()
|
||||
{
|
||||
list ($stream) = $this->createSocketPair();
|
||||
|
||||
$this->loop->nextTick(
|
||||
function () {
|
||||
$this->loop->futureTick(
|
||||
function () {
|
||||
echo 'future-tick' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$this->expectOutputString('future-tick' . PHP_EOL);
|
||||
|
||||
$this->loop->run();
|
||||
}
|
||||
|
||||
public function testFutureTickEventGeneratedByTimer()
|
||||
{
|
||||
$this->loop->addTimer(
|
||||
0.001,
|
||||
function () {
|
||||
$this->loop->futureTick(
|
||||
function () {
|
||||
echo 'future-tick' . PHP_EOL;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$this->expectOutputString('future-tick' . PHP_EOL);
|
||||
|
||||
$this->loop->run();
|
||||
}
|
||||
|
||||
private function assertRunFasterThan($maxInterval)
|
||||
{
|
||||
$start = microtime(true);
|
||||
|
||||
$this->loop->run();
|
||||
|
||||
$end = microtime(true);
|
||||
$interval = $end - $start;
|
||||
|
||||
$this->assertLessThan($maxInterval, $interval);
|
||||
}
|
||||
}
|
||||
10
vendor/react/event-loop/tests/CallableStub.php
vendored
Executable file
10
vendor/react/event-loop/tests/CallableStub.php
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
class CallableStub
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
}
|
||||
}
|
||||
90
vendor/react/event-loop/tests/ExtEventLoopTest.php
vendored
Executable file
90
vendor/react/event-loop/tests/ExtEventLoopTest.php
vendored
Executable file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
use React\EventLoop\ExtEventLoop;
|
||||
|
||||
class ExtEventLoopTest extends AbstractLoopTest
|
||||
{
|
||||
public function createLoop($readStreamCompatible = false)
|
||||
{
|
||||
if ('Linux' === PHP_OS && !extension_loaded('posix')) {
|
||||
$this->markTestSkipped('libevent tests skipped on linux due to linux epoll issues.');
|
||||
}
|
||||
|
||||
if (!extension_loaded('event')) {
|
||||
$this->markTestSkipped('ext-event tests skipped because ext-event is not installed.');
|
||||
}
|
||||
|
||||
$cfg = null;
|
||||
if ($readStreamCompatible) {
|
||||
$cfg = new \EventConfig();
|
||||
$cfg->requireFeatures(\EventConfig::FEATURE_FDS);
|
||||
}
|
||||
|
||||
return new ExtEventLoop($cfg);
|
||||
}
|
||||
|
||||
public function createStream()
|
||||
{
|
||||
// Use a FIFO on linux to get around lack of support for disk-based file
|
||||
// descriptors when using the EPOLL back-end.
|
||||
if ('Linux' === PHP_OS) {
|
||||
$this->fifoPath = tempnam(sys_get_temp_dir(), 'react-');
|
||||
|
||||
unlink($this->fifoPath);
|
||||
|
||||
posix_mkfifo($this->fifoPath, 0600);
|
||||
|
||||
$stream = fopen($this->fifoPath, 'r+');
|
||||
|
||||
// ext-event (as of 1.8.1) does not yet support in-memory temporary
|
||||
// streams. Setting maxmemory:0 and performing a write forces PHP to
|
||||
// back this temporary stream with a real file.
|
||||
//
|
||||
// This problem is mentioned at https://bugs.php.net/bug.php?id=64652&edit=3
|
||||
// but remains unresolved (despite that issue being closed).
|
||||
} else {
|
||||
$stream = fopen('php://temp/maxmemory:0', 'r+');
|
||||
|
||||
fwrite($stream, 'x');
|
||||
ftruncate($stream, 0);
|
||||
}
|
||||
|
||||
return $stream;
|
||||
}
|
||||
|
||||
public function writeToStream($stream, $content)
|
||||
{
|
||||
if ('Linux' !== PHP_OS) {
|
||||
return parent::writeToStream($stream, $content);
|
||||
}
|
||||
|
||||
fwrite($stream, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group epoll-readable-error
|
||||
*/
|
||||
public function testCanUseReadableStreamWithFeatureFds()
|
||||
{
|
||||
if (PHP_VERSION_ID > 70000) {
|
||||
$this->markTestSkipped('Memory stream not supported');
|
||||
}
|
||||
|
||||
$this->loop = $this->createLoop(true);
|
||||
|
||||
$input = fopen('php://temp/maxmemory:0', 'r+');
|
||||
|
||||
fwrite($input, 'x');
|
||||
ftruncate($input, 0);
|
||||
|
||||
$this->loop->addReadStream($input, $this->expectCallableExactly(2));
|
||||
|
||||
fwrite($input, "foo\n");
|
||||
$this->loop->tick();
|
||||
|
||||
fwrite($input, "bar\n");
|
||||
$this->loop->tick();
|
||||
}
|
||||
}
|
||||
22
vendor/react/event-loop/tests/LibEvLoopTest.php
vendored
Executable file
22
vendor/react/event-loop/tests/LibEvLoopTest.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
use React\EventLoop\LibEvLoop;
|
||||
|
||||
class LibEvLoopTest extends AbstractLoopTest
|
||||
{
|
||||
public function createLoop()
|
||||
{
|
||||
if (!class_exists('libev\EventLoop')) {
|
||||
$this->markTestSkipped('libev tests skipped because ext-libev is not installed.');
|
||||
}
|
||||
|
||||
return new LibEvLoop();
|
||||
}
|
||||
|
||||
public function testLibEvConstructor()
|
||||
{
|
||||
$loop = new LibEvLoop();
|
||||
}
|
||||
}
|
||||
58
vendor/react/event-loop/tests/LibEventLoopTest.php
vendored
Executable file
58
vendor/react/event-loop/tests/LibEventLoopTest.php
vendored
Executable file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
use React\EventLoop\LibEventLoop;
|
||||
|
||||
class LibEventLoopTest extends AbstractLoopTest
|
||||
{
|
||||
private $fifoPath;
|
||||
|
||||
public function createLoop()
|
||||
{
|
||||
if ('Linux' === PHP_OS && !extension_loaded('posix')) {
|
||||
$this->markTestSkipped('libevent tests skipped on linux due to linux epoll issues.');
|
||||
}
|
||||
|
||||
if (!function_exists('event_base_new')) {
|
||||
$this->markTestSkipped('libevent tests skipped because ext-libevent is not installed.');
|
||||
}
|
||||
|
||||
return new LibEventLoop();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
if (file_exists($this->fifoPath)) {
|
||||
unlink($this->fifoPath);
|
||||
}
|
||||
}
|
||||
|
||||
public function createStream()
|
||||
{
|
||||
if ('Linux' !== PHP_OS) {
|
||||
return parent::createStream();
|
||||
}
|
||||
|
||||
$this->fifoPath = tempnam(sys_get_temp_dir(), 'react-');
|
||||
|
||||
unlink($this->fifoPath);
|
||||
|
||||
// Use a FIFO on linux to get around lack of support for disk-based file
|
||||
// descriptors when using the EPOLL back-end.
|
||||
posix_mkfifo($this->fifoPath, 0600);
|
||||
|
||||
$stream = fopen($this->fifoPath, 'r+');
|
||||
|
||||
return $stream;
|
||||
}
|
||||
|
||||
public function writeToStream($stream, $content)
|
||||
{
|
||||
if ('Linux' !== PHP_OS) {
|
||||
return parent::writeToStream($stream, $content);
|
||||
}
|
||||
|
||||
fwrite($stream, $content);
|
||||
}
|
||||
}
|
||||
179
vendor/react/event-loop/tests/StreamSelectLoopTest.php
vendored
Executable file
179
vendor/react/event-loop/tests/StreamSelectLoopTest.php
vendored
Executable file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\EventLoop\StreamSelectLoop;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
|
||||
class StreamSelectLoopTest extends AbstractLoopTest
|
||||
{
|
||||
protected function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
if (strncmp($this->getName(false), 'testSignal', 10) === 0 && extension_loaded('pcntl')) {
|
||||
$this->resetSignalHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
public function createLoop()
|
||||
{
|
||||
return new StreamSelectLoop();
|
||||
}
|
||||
|
||||
public function testStreamSelectTimeoutEmulation()
|
||||
{
|
||||
$this->loop->addTimer(
|
||||
0.05,
|
||||
$this->expectCallableOnce()
|
||||
);
|
||||
|
||||
$start = microtime(true);
|
||||
|
||||
$this->loop->run();
|
||||
|
||||
$end = microtime(true);
|
||||
$interval = $end - $start;
|
||||
|
||||
$this->assertGreaterThan(0.04, $interval);
|
||||
}
|
||||
|
||||
public function signalProvider()
|
||||
{
|
||||
return [
|
||||
['SIGUSR1'],
|
||||
['SIGHUP'],
|
||||
['SIGTERM'],
|
||||
];
|
||||
}
|
||||
|
||||
private $_signalHandled = false;
|
||||
|
||||
/**
|
||||
* Test signal interrupt when no stream is attached to the loop
|
||||
* @dataProvider signalProvider
|
||||
*/
|
||||
public function testSignalInterruptNoStream($signal)
|
||||
{
|
||||
if (!extension_loaded('pcntl')) {
|
||||
$this->markTestSkipped('"pcntl" extension is required to run this test.');
|
||||
}
|
||||
|
||||
// dispatch signal handler once before signal is sent and once after
|
||||
$this->loop->addTimer(0.01, function() { pcntl_signal_dispatch(); });
|
||||
$this->loop->addTimer(0.03, function() { pcntl_signal_dispatch(); });
|
||||
if (defined('HHVM_VERSION')) {
|
||||
// hhvm startup is slow so we need to add another handler much later
|
||||
$this->loop->addTimer(0.5, function() { pcntl_signal_dispatch(); });
|
||||
}
|
||||
|
||||
$this->setUpSignalHandler($signal);
|
||||
|
||||
// spawn external process to send signal to current process id
|
||||
$this->forkSendSignal($signal);
|
||||
$this->loop->run();
|
||||
$this->assertTrue($this->_signalHandled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test signal interrupt when a stream is attached to the loop
|
||||
* @dataProvider signalProvider
|
||||
*/
|
||||
public function testSignalInterruptWithStream($signal)
|
||||
{
|
||||
if (!extension_loaded('pcntl')) {
|
||||
$this->markTestSkipped('"pcntl" extension is required to run this test.');
|
||||
}
|
||||
|
||||
// dispatch signal handler every 10ms
|
||||
$this->loop->addPeriodicTimer(0.01, function() { pcntl_signal_dispatch(); });
|
||||
|
||||
// add stream to the loop
|
||||
list($writeStream, $readStream) = $this->createSocketPair();
|
||||
$this->loop->addReadStream($readStream, function($stream, $loop) {
|
||||
/** @var $loop LoopInterface */
|
||||
$read = fgets($stream);
|
||||
if ($read === "end loop\n") {
|
||||
$loop->stop();
|
||||
}
|
||||
});
|
||||
$this->loop->addTimer(0.05, function() use ($writeStream) {
|
||||
fwrite($writeStream, "end loop\n");
|
||||
});
|
||||
|
||||
$this->setUpSignalHandler($signal);
|
||||
|
||||
// spawn external process to send signal to current process id
|
||||
$this->forkSendSignal($signal);
|
||||
|
||||
$this->loop->run();
|
||||
|
||||
$this->assertTrue($this->_signalHandled);
|
||||
}
|
||||
|
||||
/**
|
||||
* add signal handler for signal
|
||||
*/
|
||||
protected function setUpSignalHandler($signal)
|
||||
{
|
||||
$this->_signalHandled = false;
|
||||
$this->assertTrue(pcntl_signal(constant($signal), function() { $this->_signalHandled = true; }));
|
||||
}
|
||||
|
||||
/**
|
||||
* reset all signal handlers to default
|
||||
*/
|
||||
protected function resetSignalHandlers()
|
||||
{
|
||||
foreach($this->signalProvider() as $signal) {
|
||||
pcntl_signal(constant($signal[0]), SIG_DFL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fork child process to send signal to current process id
|
||||
*/
|
||||
protected function forkSendSignal($signal)
|
||||
{
|
||||
$currentPid = posix_getpid();
|
||||
$childPid = pcntl_fork();
|
||||
if ($childPid == -1) {
|
||||
$this->fail("Failed to fork child process!");
|
||||
} else if ($childPid === 0) {
|
||||
// this is executed in the child process
|
||||
usleep(20000);
|
||||
posix_kill($currentPid, constant($signal));
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/reactphp/event-loop/issues/48
|
||||
*
|
||||
* Tests that timer with very small interval uses at least 1 microsecond
|
||||
* timeout.
|
||||
*/
|
||||
public function testSmallTimerInterval()
|
||||
{
|
||||
/** @var StreamSelectLoop|\PHPUnit_Framework_MockObject_MockObject $loop */
|
||||
$loop = $this->getMock('React\EventLoop\StreamSelectLoop', ['streamSelect']);
|
||||
$loop
|
||||
->expects($this->at(0))
|
||||
->method('streamSelect')
|
||||
->with([], [], 1);
|
||||
$loop
|
||||
->expects($this->at(1))
|
||||
->method('streamSelect')
|
||||
->with([], [], 0);
|
||||
|
||||
$callsCount = 0;
|
||||
$loop->addPeriodicTimer(Timer::MIN_INTERVAL, function() use (&$loop, &$callsCount) {
|
||||
$callsCount++;
|
||||
if ($callsCount == 2) {
|
||||
$loop->stop();
|
||||
}
|
||||
});
|
||||
|
||||
$loop->run();
|
||||
}
|
||||
}
|
||||
41
vendor/react/event-loop/tests/TestCase.php
vendored
Executable file
41
vendor/react/event-loop/tests/TestCase.php
vendored
Executable file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
class TestCase extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected function expectCallableExactly($amount)
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
$mock
|
||||
->expects($this->exactly($amount))
|
||||
->method('__invoke');
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function expectCallableOnce()
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
$mock
|
||||
->expects($this->once())
|
||||
->method('__invoke');
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function expectCallableNever()
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
$mock
|
||||
->expects($this->never())
|
||||
->method('__invoke');
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function createCallableMock()
|
||||
{
|
||||
return $this->getMockBuilder('React\Tests\EventLoop\CallableStub')->getMock();
|
||||
}
|
||||
}
|
||||
97
vendor/react/event-loop/tests/Timer/AbstractTimerTest.php
vendored
Executable file
97
vendor/react/event-loop/tests/Timer/AbstractTimerTest.php
vendored
Executable file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop\Timer;
|
||||
|
||||
use React\Tests\EventLoop\TestCase;
|
||||
|
||||
abstract class AbstractTimerTest extends TestCase
|
||||
{
|
||||
abstract public function createLoop();
|
||||
|
||||
public function testAddTimer()
|
||||
{
|
||||
// usleep is intentionally high
|
||||
|
||||
$loop = $this->createLoop();
|
||||
|
||||
$loop->addTimer(0.001, $this->expectCallableOnce());
|
||||
usleep(1000);
|
||||
$loop->tick();
|
||||
}
|
||||
|
||||
public function testAddPeriodicTimer()
|
||||
{
|
||||
$loop = $this->createLoop();
|
||||
|
||||
$loop->addPeriodicTimer(0.001, $this->expectCallableExactly(3));
|
||||
usleep(1000);
|
||||
$loop->tick();
|
||||
usleep(1000);
|
||||
$loop->tick();
|
||||
usleep(1000);
|
||||
$loop->tick();
|
||||
}
|
||||
|
||||
public function testAddPeriodicTimerWithCancel()
|
||||
{
|
||||
$loop = $this->createLoop();
|
||||
|
||||
$timer = $loop->addPeriodicTimer(0.001, $this->expectCallableExactly(2));
|
||||
|
||||
usleep(1000);
|
||||
$loop->tick();
|
||||
usleep(1000);
|
||||
$loop->tick();
|
||||
|
||||
$timer->cancel();
|
||||
|
||||
usleep(1000);
|
||||
$loop->tick();
|
||||
}
|
||||
|
||||
public function testAddPeriodicTimerCancelsItself()
|
||||
{
|
||||
$i = 0;
|
||||
|
||||
$loop = $this->createLoop();
|
||||
|
||||
$loop->addPeriodicTimer(0.001, function ($timer) use (&$i) {
|
||||
$i++;
|
||||
|
||||
if ($i == 2) {
|
||||
$timer->cancel();
|
||||
}
|
||||
});
|
||||
|
||||
usleep(1000);
|
||||
$loop->tick();
|
||||
usleep(1000);
|
||||
$loop->tick();
|
||||
usleep(1000);
|
||||
$loop->tick();
|
||||
|
||||
$this->assertSame(2, $i);
|
||||
}
|
||||
|
||||
public function testIsTimerActive()
|
||||
{
|
||||
$loop = $this->createLoop();
|
||||
|
||||
$timer = $loop->addPeriodicTimer(0.001, function () {});
|
||||
|
||||
$this->assertTrue($loop->isTimerActive($timer));
|
||||
|
||||
$timer->cancel();
|
||||
|
||||
$this->assertFalse($loop->isTimerActive($timer));
|
||||
}
|
||||
|
||||
public function testMinimumIntervalOneMicrosecond()
|
||||
{
|
||||
$loop = $this->createLoop();
|
||||
|
||||
$timer = $loop->addTimer(0, function () {});
|
||||
|
||||
$this->assertEquals(0.000001, $timer->getInterval());
|
||||
}
|
||||
}
|
||||
17
vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php
vendored
Executable file
17
vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php
vendored
Executable file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\ExtEventLoop;
|
||||
|
||||
class ExtEventTimerTest extends AbstractTimerTest
|
||||
{
|
||||
public function createLoop()
|
||||
{
|
||||
if (!extension_loaded('event')) {
|
||||
$this->markTestSkipped('ext-event tests skipped because ext-event is not installed.');
|
||||
}
|
||||
|
||||
return new ExtEventLoop();
|
||||
}
|
||||
}
|
||||
17
vendor/react/event-loop/tests/Timer/LibEvTimerTest.php
vendored
Executable file
17
vendor/react/event-loop/tests/Timer/LibEvTimerTest.php
vendored
Executable file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\LibEvLoop;
|
||||
|
||||
class LibEvTimerTest extends AbstractTimerTest
|
||||
{
|
||||
public function createLoop()
|
||||
{
|
||||
if (!class_exists('libev\EventLoop')) {
|
||||
$this->markTestSkipped('libev tests skipped because ext-libev is not installed.');
|
||||
}
|
||||
|
||||
return new LibEvLoop();
|
||||
}
|
||||
}
|
||||
17
vendor/react/event-loop/tests/Timer/LibEventTimerTest.php
vendored
Executable file
17
vendor/react/event-loop/tests/Timer/LibEventTimerTest.php
vendored
Executable file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\LibEventLoop;
|
||||
|
||||
class LibEventTimerTest extends AbstractTimerTest
|
||||
{
|
||||
public function createLoop()
|
||||
{
|
||||
if (!function_exists('event_base_new')) {
|
||||
$this->markTestSkipped('libevent tests skipped because ext-libevent is not installed.');
|
||||
}
|
||||
|
||||
return new LibEventLoop();
|
||||
}
|
||||
}
|
||||
13
vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php
vendored
Executable file
13
vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\StreamSelectLoop;
|
||||
|
||||
class StreamSelectTimerTest extends AbstractTimerTest
|
||||
{
|
||||
public function createLoop()
|
||||
{
|
||||
return new StreamSelectLoop();
|
||||
}
|
||||
}
|
||||
29
vendor/react/event-loop/tests/Timer/TimersTest.php
vendored
Executable file
29
vendor/react/event-loop/tests/Timer/TimersTest.php
vendored
Executable file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop\Timer;
|
||||
|
||||
use React\Tests\EventLoop\TestCase;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use React\EventLoop\Timer\Timers;
|
||||
|
||||
class TimersTest extends TestCase
|
||||
{
|
||||
public function testBlockedTimer()
|
||||
{
|
||||
$loop = $this
|
||||
->getMockBuilder('React\EventLoop\LoopInterface')
|
||||
->getMock();
|
||||
|
||||
$timers = new Timers();
|
||||
$timers->tick();
|
||||
|
||||
// simulate a bunch of processing on stream events,
|
||||
// part of which schedules a future timer...
|
||||
sleep(1);
|
||||
$timers->add(new Timer($loop, 0.5, function () {
|
||||
$this->fail("Timer shouldn't be called");
|
||||
}));
|
||||
|
||||
$timers->tick();
|
||||
}
|
||||
}
|
||||
7
vendor/react/event-loop/tests/bootstrap.php
vendored
Executable file
7
vendor/react/event-loop/tests/bootstrap.php
vendored
Executable file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
$loader = @include __DIR__ . '/../vendor/autoload.php';
|
||||
if (!$loader) {
|
||||
$loader = require __DIR__ . '/../../../../vendor/autoload.php';
|
||||
}
|
||||
$loader->addPsr4('React\\Tests\\EventLoop\\', __DIR__);
|
||||
37
vendor/react/event-loop/travis-init.sh
vendored
Executable file
37
vendor/react/event-loop/travis-init.sh
vendored
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
if [[ "$TRAVIS_PHP_VERSION" != "hhvm" &&
|
||||
"$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]]; then
|
||||
|
||||
# install 'event' PHP extension
|
||||
echo "yes" | pecl install event
|
||||
|
||||
# install 'libevent' PHP extension (does not support php 7)
|
||||
if [[ "$TRAVIS_PHP_VERSION" != "7.0" &&
|
||||
"$TRAVIS_PHP_VERSION" != "7.1" ]]; then
|
||||
curl http://pecl.php.net/get/libevent-0.1.0.tgz | tar -xz
|
||||
pushd libevent-0.1.0
|
||||
phpize
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
popd
|
||||
echo "extension=libevent.so" >> "$(php -r 'echo php_ini_loaded_file();')"
|
||||
fi
|
||||
|
||||
# install 'libev' PHP extension (does not support php 7)
|
||||
if [[ "$TRAVIS_PHP_VERSION" != "7.0" &&
|
||||
"$TRAVIS_PHP_VERSION" != "7.1" ]]; then
|
||||
git clone --recursive https://github.com/m4rw3r/php-libev
|
||||
pushd php-libev
|
||||
phpize
|
||||
./configure --with-libev
|
||||
make
|
||||
make install
|
||||
popd
|
||||
echo "extension=libev.so" >> "$(php -r 'echo php_ini_loaded_file();')"
|
||||
fi
|
||||
|
||||
fi
|
||||
Reference in New Issue
Block a user