diff options
Diffstat (limited to 'assets/php/vendor/react/event-loop')
51 files changed, 4977 insertions, 0 deletions
diff --git a/assets/php/vendor/react/event-loop/.gitignore b/assets/php/vendor/react/event-loop/.gitignore new file mode 100644 index 0000000..81b9258 --- /dev/null +++ b/assets/php/vendor/react/event-loop/.gitignore @@ -0,0 +1,3 @@ +composer.lock +phpunit.xml +vendor diff --git a/assets/php/vendor/react/event-loop/.travis.yml b/assets/php/vendor/react/event-loop/.travis.yml new file mode 100644 index 0000000..7af713a --- /dev/null +++ b/assets/php/vendor/react/event-loop/.travis.yml @@ -0,0 +1,39 @@ +language: php + +php: +# - 5.3 # requires old distro, see below + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - 7.1 + - 7.2 + - hhvm # ignore errors, see below + +# lock distro so new future defaults will not break the build +dist: trusty + +matrix: + include: + - php: 5.3 + dist: precise + allow_failures: + - php: 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 diff --git a/assets/php/vendor/react/event-loop/CHANGELOG.md b/assets/php/vendor/react/event-loop/CHANGELOG.md new file mode 100644 index 0000000..c291840 --- /dev/null +++ b/assets/php/vendor/react/event-loop/CHANGELOG.md @@ -0,0 +1,316 @@ +# Changelog + +## 0.5.1 (2018-04-09) + +* New loop: ExtEvLoop (PECL ext-ev) (#148 by @kaduev13) + +## 0.5.0 (2018-04-05) + +A major feature release with a significant documentation overhaul and long overdue API cleanup! + +This update involves a number of BC breaks due to dropped support for deprecated +functionality. We've tried hard to avoid BC breaks where possible and minimize +impact otherwise. We expect that most consumers of this package will actually +not be affected by any BC breaks, see below for more details. + +We realize that the changes listed below may seem overwhelming, but we've tried +to be very clear about any possible BC breaks. Don't worry: In fact, all ReactPHP +components are already compatible and support both this new release as well as +providing backwards compatibility with the last release. + +* Feature / BC break: Add support for signal handling via new + `LoopInterface::addSignal()` and `LoopInterface::removeSignal()` methods. + (#104 by @WyriHaximus and #111 and #150 by @clue) + + ```php + $loop->addSignal(SIGINT, function () { + echo 'CTRL-C'; + }); + ``` + +* Feature: Significant documentation updates for `LoopInterface` and `Factory`. + (#100, #119, #126, #127, #159 and #160 by @clue, #113 by @WyriHaximus and #81 and #91 by @jsor) + +* Feature: Add examples to ease getting started + (#99, #100 and #125 by @clue, #59 by @WyriHaximus and #143 by @jsor) + +* Feature: Documentation for advanced timer concepts, such as monotonic time source vs wall-clock time + and high precision timers with millisecond accuracy or below. + (#130 and #157 by @clue) + +* Feature: Documentation for advanced stream concepts, such as edge-triggered event listeners + and stream buffers and allow throwing Exception if stream resource is not supported. + (#129 and #158 by @clue) + +* Feature: Throw `BadMethodCallException` on manual loop creation when required extension isn't installed. + (#153 by @WyriHaximus) + +* Feature / BC break: First class support for legacy PHP 5.3 through PHP 7.2 and HHVM + and remove all `callable` type hints for consistency reasons. + (#141 and #151 by @clue) + +* BC break: Documentation for timer API and clean up unneeded timer API. + (#102 by @clue) + + Remove `TimerInterface::cancel()`, use `LoopInterface::cancelTimer()` instead: + + ```php + // old (method invoked on timer instance) + $timer->cancel(); + + // already supported before: invoke method on loop instance + $loop->cancelTimer($timer); + ``` + + Remove unneeded `TimerInterface::setData()` and `TimerInterface::getData()`, + use closure binding to add arbitrary data to timer instead: + + ```php + // old (limited setData() and getData() only allows single variable) + $name = 'Tester'; + $timer = $loop->addTimer(1.0, function ($timer) { + echo 'Hello ' . $timer->getData() . PHP_EOL; + }); + $timer->setData($name); + + // already supported before: closure binding allows any number of variables + $name = 'Tester'; + $loop->addTimer(1.0, function () use ($name) { + echo 'Hello ' . $name . PHP_EOL; + }); + ``` + + Remove unneeded `TimerInterface::getLoop()`, use closure binding instead: + + ```php + // old (getLoop() called on timer instance) + $loop->addTimer(0.1, function ($timer) { + $timer->getLoop()->stop(); + }); + + // already supported before: use closure binding as usual + $loop->addTimer(0.1, function () use ($loop) { + $loop->stop(); + }); + ``` + +* BC break: Remove unneeded `LoopInterface::isTimerActive()` and + `TimerInterface::isActive()` to reduce API surface. + (#133 by @clue) + + ```php + // old (method on timer instance or on loop instance) + $timer->isActive(); + $loop->isTimerActive($timer); + ``` + +* BC break: Move `TimerInterface` one level up to `React\EventLoop\TimerInterface`. + (#138 by @WyriHaximus) + + ```php + // old (notice obsolete "Timer" namespace) + assert($timer instanceof React\EventLoop\Timer\TimerInterface); + + // new + assert($timer instanceof React\EventLoop\TimerInterface); + ``` + +* BC break: Remove unneeded `LoopInterface::nextTick()` (and internal `NextTickQueue`), + use `LoopInterface::futureTick()` instead. + (#30 by @clue) + + ```php + // old (removed) + $loop->nextTick(function () { + echo 'tick'; + }); + + // already supported before + $loop->futureTick(function () { + echo 'tick'; + }); + ``` + +* BC break: Remove unneeded `$loop` argument for `LoopInterface::futureTick()` + (and fix internal cyclic dependency). + (#103 by @clue) + + ```php + // old ($loop gets passed by default) + $loop->futureTick(function ($loop) { + $loop->stop(); + }); + + // already supported before: use closure binding as usual + $loop->futureTick(function () use ($loop) { + $loop->stop(); + }); + ``` + +* BC break: Remove unneeded `LoopInterface::tick()`. + (#72 by @jsor) + + ```php + // old (removed) + $loop->tick(); + + // suggested work around for testing purposes only + $loop->futureTick(function () use ($loop) { + $loop->stop(); + }); + ``` + +* BC break: Documentation for advanced stream API and clean up unneeded stream API. + (#110 by @clue) + + Remove unneeded `$loop` argument for `LoopInterface::addReadStream()` + and `LoopInterface::addWriteStream()`, use closure binding instead: + + ```php + // old ($loop gets passed by default) + $loop->addReadStream($stream, function ($stream, $loop) { + $loop->removeReadStream($stream); + }); + + // already supported before: use closure binding as usual + $loop->addReadStream($stream, function ($stream) use ($loop) { + $loop->removeReadStream($stream); + }); + ``` + +* BC break: Remove unneeded `LoopInterface::removeStream()` method, + use `LoopInterface::removeReadStream()` and `LoopInterface::removeWriteStream()` instead. + (#118 by @clue) + + ```php + // old + $loop->removeStream($stream); + + // already supported before + $loop->removeReadStream($stream); + $loop->removeWriteStream($stream); + ``` + +* BC break: Rename `LibEventLoop` to `ExtLibeventLoop` and `LibEvLoop` to `ExtLibevLoop` + for consistent naming for event loop implementations. + (#128 by @clue) + +* BC break: Remove optional `EventBaseConfig` argument from `ExtEventLoop` + and make its `FEATURE_FDS` enabled by default. + (#156 by @WyriHaximus) + +* BC break: Mark all classes as final to discourage inheritance. + (#131 by @clue) + +* Fix: Fix `ExtEventLoop` to keep track of stream resources (refcount) + (#123 by @clue) + +* Fix: Ensure large timer interval does not overflow on 32bit systems + (#132 by @clue) + +* Fix: Fix separately removing readable and writable side of stream when closing + (#139 by @clue) + +* Fix: Properly clean up event watchers for `ext-event` and `ext-libev` + (#149 by @clue) + +* Fix: Minor code cleanup and remove unneeded references + (#145 by @seregazhuk) + +* Fix: Discourage outdated `ext-libevent` on PHP 7 + (#62 by @cboden) + +* Improve test suite by adding forward compatibility with PHPUnit 6 and PHPUnit 5, + lock Travis distro so new defaults will not break the build, + improve test suite to be less fragile and increase test timeouts, + test against PHP 7.2 and reduce fwrite() call length to one chunk. + (#106 and #144 by @clue, #120 and #124 by @carusogabriel, #147 by nawarian and #92 by @kelunik) + +* A number of changes were originally planned for this release but have been backported + to the last `v0.4.3` already: #74, #76, #79, #81 (refs #65, #66, #67), #88 and #93 + +## 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.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.5 (2016-12-28) + +This is a compatibility release that eases upgrading to the v0.4 release branch. +You should consider upgrading to the v0.4 release branch. + +* Feature: Cap min timer interval at 1µs, thus improving compatibility with v0.4 + (#47 by @clue) + +## 0.3.4 (2014-03-30) + +* Bug fix: Changed StreamSelectLoop to use non-blocking behavior on tick() (@astephens25) + +## 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 diff --git a/assets/php/vendor/react/event-loop/LICENSE b/assets/php/vendor/react/event-loop/LICENSE new file mode 100644 index 0000000..a808108 --- /dev/null +++ b/assets/php/vendor/react/event-loop/LICENSE @@ -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. diff --git a/assets/php/vendor/react/event-loop/README.md b/assets/php/vendor/react/event-loop/README.md new file mode 100644 index 0000000..207e7f4 --- /dev/null +++ b/assets/php/vendor/react/event-loop/README.md @@ -0,0 +1,702 @@ +# EventLoop Component + +[](https://travis-ci.org/reactphp/event-loop) + +[ReactPHP](https://reactphp.org/)'s core reactor event loop 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()`](#run) call that is controlled by the user. + +**Table of Contents** + +* [Quickstart example](#quickstart-example) +* [Usage](#usage) + * [Factory](#factory) + * [create()](#create) + * [Loop implementations](#loop-implementations) + * [StreamSelectLoop](#streamselectloop) + * [ExtEventLoop](#exteventloop) + * [ExtLibeventLoop](#extlibeventloop) + * [ExtLibevLoop](#extlibevloop) + * [ExtEvLoop](#extevloop) + * [LoopInterface](#loopinterface) + * [run()](#run) + * [stop()](#stop) + * [addTimer()](#addtimer) + * [addPeriodicTimer()](#addperiodictimer) + * [cancelTimer()](#canceltimer) + * [futureTick()](#futuretick) + * [addSignal()](#addsignal) + * [removeSignal()](#removesignal) + * [addReadStream()](#addreadstream) + * [addWriteStream()](#addwritestream) + * [removeReadStream()](#removereadstream) + * [removeWriteStream()](#removewritestream) +* [Install](#install) +* [Tests](#tests) +* [License](#license) +* [More](#more) + +## 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, false); + +$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->removeWriteStream($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(); +``` + +See also the [examples](examples). + +## 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()`](#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()`](#run) call at the end of the program. + +### Factory + +The `Factory` class exists as a convenient way to pick the best available +[event loop implementation](#loop-implementations). + +#### create() + +The `create(): LoopInterface` method can be used to create a new event loop +instance: + +```php +$loop = React\EventLoop\Factory::create(); +``` + +This method always returns an instance implementing [`LoopInterface`](#loopinterface), +the actual [event loop implementation](#loop-implementations) is an implementation detail. + +This method should usually only be called once at the beginning of the program. + +### Loop implementations + +In addition to the [`LoopInterface`](#loopinterface), there are a number of +event loop implementations provided. + +All of the event loops support these features: + +* File descriptor polling +* One-off timers +* Periodic timers +* Deferred execution on future loop tick + +For most consumers of this package, the underlying event loop implementation is +an implementation detail. +You should use the [`Factory`](#factory) to automatically create a new instance. + +Advanced! If you explicitly need a certain event loop implementation, you can +manually instantiate one of the following classes. +Note that you may have to install the required PHP extensions for the respective +event loop implementation first or they will throw a `BadMethodCallException` on creation. + +#### StreamSelectLoop + +A `stream_select()` based event loop. + +This uses the [`stream_select()`](http://php.net/manual/en/function.stream-select.php) +function and is the only implementation which works out of the box with PHP. + +This event loop works out of the box on PHP 5.3 through PHP 7+ and HHVM. +This means that no installation is required and this library works on all +platforms and supported PHP versions. +Accordingly, the [`Factory`](#factory) will use this event loop by default if +you do not install any of the event loop extensions listed below. + +Under the hood, it does a simple `select` system call. +This system call is limited to the maximum file descriptor number of +`FD_SETSIZE` (platform dependent, commonly 1024) and scales with `O(m)` +(`m` being the maximum file descriptor number passed). +This means that you may run into issues when handling thousands of streams +concurrently and you may want to look into using one of the alternative +event loop implementations listed below in this case. +If your use case is among the many common use cases that involve handling only +dozens or a few hundred streams at once, then this event loop implementation +performs really well. + +If you want to use signal handling (see also [`addSignal()`](#addsignal) below), +this event loop implementation requires `ext-pcntl`. +This extension is only available for Unix-like platforms and does not support +Windows. +It is commonly installed as part of many PHP distributions. +If this extension is missing (or you're running on Windows), signal handling is +not supported and throws a `BadMethodCallException` instead. + +This event loop is known to rely on wall-clock time to schedule future +timers, because a monotonic time source is not available in PHP by default. +While this does not affect many common use cases, this is an important +distinction for programs that rely on a high time precision or on systems +that are subject to discontinuous time adjustments (time jumps). +This means that if you schedule a timer to trigger in 30s and then adjust +your system time forward by 20s, the timer may trigger in 10s. +See also [`addTimer()`](#addtimer) for more details. + +#### ExtEventLoop + +An `ext-event` based event loop. + +This uses the [`event` PECL extension](https://pecl.php.net/package/event). +It supports the same backends as libevent. + +This loop is known to work with PHP 5.4 through PHP 7+. + +#### ExtEvLoop + +An `ext-ev` based event loop. + +This loop uses the [`ev` PECL extension](https://pecl.php.net/package/ev), that +provides an interface to `libev` library. + +This loop is known to work with PHP 5.4 through PHP 7+. + + +#### ExtLibeventLoop + +An `ext-libevent` based event loop. + +This uses the [`libevent` PECL extension](https://pecl.php.net/package/libevent). +`libevent` itself supports a number of system-specific backends (epoll, kqueue). + +This event loop does only work with PHP 5. +An [unofficial update](https://github.com/php/pecl-event-libevent/pull/2) for +PHP 7 does exist, but it is known to cause regular crashes due to `SEGFAULT`s. +To reiterate: Using this event loop on PHP 7 is not recommended. +Accordingly, the [`Factory`](#factory) will not try to use this event loop on +PHP 7. + +This event loop is known to trigger a readable listener only if +the stream *becomes* readable (edge-triggered) and may not trigger if the +stream has already been readable from the beginning. +This also implies that a stream may not be recognized as readable when data +is still left in PHP's internal stream buffers. +As such, it's recommended to use `stream_set_read_buffer($stream, 0);` +to disable PHP's internal read buffer in this case. +See also [`addReadStream()`](#addreadstream) for more details. + +#### ExtLibevLoop + +An `ext-libev` based event loop. + +This uses an [unofficial `libev` extension](https://github.com/m4rw3r/php-libev). +It supports the same backends as libevent. + +This loop does only work with PHP 5. +An update for PHP 7 is [unlikely](https://github.com/m4rw3r/php-libev/issues/8) +to happen any time soon. + +### LoopInterface + +#### run() + +The `run(): void` method can be used to +run the event loop until there are no more tasks to perform. + +For many applications, this method is the only directly visible +invocation on the event loop. +As a rule of thumb, it is usally recommended to attach everything to the +same loop instance and then run the loop once at the bottom end of the +application. + +```php +$loop->run(); +``` + +This method will keep the loop running until there are no more tasks +to perform. In other words: This method will block until the last +timer, stream and/or signal has been removed. + +Likewise, it is imperative to ensure the application actually invokes +this method once. Adding listeners to the loop and missing to actually +run it will result in the application exiting without actually waiting +for any of the attached listeners. + +This method MUST NOT be called while the loop is already running. +This method MAY be called more than once after it has explicity been +[`stop()`ped](#stop) or after it automatically stopped because it +previously did no longer have anything to do. + +#### stop() + +The `stop(): void` method can be used to +instruct a running event loop to stop. + +This method is considered advanced usage and should be used with care. +As a rule of thumb, it is usually recommended to let the loop stop +only automatically when it no longer has anything to do. + +This method can be used to explicitly instruct the event loop to stop: + +```php +$loop->addTimer(3.0, function () use ($loop) { + $loop->stop(); +}); +``` + +Calling this method on a loop instance that is not currently running or +on a loop instance that has already been stopped has no effect. + +#### addTimer() + +The `addTimer(float $interval, callable $callback): TimerInterface` method can be used to +enqueue a callback to be invoked once after the given interval. + +The timer callback function MUST be able to accept a single parameter, +the timer instance as also returned by this method or you MAY use a +function which has no parameters at all. + +The timer callback function MUST NOT throw an `Exception`. +The return value of the timer callback function will be ignored and has +no effect, so for performance reasons you're recommended to not return +any excessive data structures. + +Unlike [`addPeriodicTimer()`](#addperiodictimer), this method will ensure +the callback will be invoked only once after the given interval. +You can invoke [`cancelTimer`](#canceltimer) to cancel a pending timer. + +```php +$loop->addTimer(0.8, function () { + echo 'world!' . PHP_EOL; +}); + +$loop->addTimer(0.3, function () { + echo 'hello '; +}); +``` + +See also [example #1](examples). + +If you want to access any variables within your callback function, you +can bind arbitrary data to a callback closure like this: + +```php +function hello($name, LoopInterface $loop) +{ + $loop->addTimer(1.0, function () use ($name) { + echo "hello $name\n"; + }); +} + +hello('Tester', $loop); +``` + +This interface does not enforce any particular timer resolution, so +special care may have to be taken if you rely on very high precision with +millisecond accuracy or below. Event loop implementations SHOULD work on +a best effort basis and SHOULD provide at least millisecond accuracy +unless otherwise noted. Many existing event loop implementations are +known to provide microsecond accuracy, but it's generally not recommended +to rely on this high precision. + +Similarly, the execution order of timers scheduled to execute at the +same time (within its possible accuracy) is not guaranteed. + +This interface suggests that event loop implementations SHOULD use a +monotonic time source if available. Given that a monotonic time source is +not available on PHP by default, event loop implementations MAY fall back +to using wall-clock time. +While this does not affect many common use cases, this is an important +distinction for programs that rely on a high time precision or on systems +that are subject to discontinuous time adjustments (time jumps). +This means that if you schedule a timer to trigger in 30s and then adjust +your system time forward by 20s, the timer SHOULD still trigger in 30s. +See also [event loop implementations](#loop-implementations) for more details. + +#### addPeriodicTimer() + +The `addPeriodicTimer(float $interval, callable $callback): TimerInterface` method can be used to +enqueue a callback to be invoked repeatedly after the given interval. + +The timer callback function MUST be able to accept a single parameter, +the timer instance as also returned by this method or you MAY use a +function which has no parameters at all. + +The timer callback function MUST NOT throw an `Exception`. +The return value of the timer callback function will be ignored and has +no effect, so for performance reasons you're recommended to not return +any excessive data structures. + +Unlike [`addTimer()`](#addtimer), this method will ensure the the +callback will be invoked infinitely after the given interval or until you +invoke [`cancelTimer`](#canceltimer). + +```php +$timer = $loop->addPeriodicTimer(0.1, function () { + echo 'tick!' . PHP_EOL; +}); + +$loop->addTimer(1.0, function () use ($loop, $timer) { + $loop->cancelTimer($timer); + echo 'Done' . PHP_EOL; +}); +``` + +See also [example #2](examples). + +If you want to limit the number of executions, you can bind +arbitrary data to a callback closure like this: + +```php +function hello($name, LoopInterface $loop) +{ + $n = 3; + $loop->addPeriodicTimer(1.0, function ($timer) use ($name, $loop, &$n) { + if ($n > 0) { + --$n; + echo "hello $name\n"; + } else { + $loop->cancelTimer($timer); + } + }); +} + +hello('Tester', $loop); +``` + +This interface does not enforce any particular timer resolution, so +special care may have to be taken if you rely on very high precision with +millisecond accuracy or below. Event loop implementations SHOULD work on +a best effort basis and SHOULD provide at least millisecond accuracy +unless otherwise noted. Many existing event loop implementations are +known to provide microsecond accuracy, but it's generally not recommended +to rely on this high precision. + +Similarly, the execution order of timers scheduled to execute at the +same time (within its possible accuracy) is not guaranteed. + +This interface suggests that event loop implementations SHOULD use a +monotonic time source if available. Given that a monotonic time source is +not available on PHP by default, event loop implementations MAY fall back +to using wall-clock time. +While this does not affect many common use cases, this is an important +distinction for programs that rely on a high time precision or on systems +that are subject to discontinuous time adjustments (time jumps). +This means that if you schedule a timer to trigger in 30s and then adjust +your system time forward by 20s, the timer SHOULD still trigger in 30s. +See also [event loop implementations](#loop-implementations) for more details. + +Additionally, periodic timers may be subject to timer drift due to +re-scheduling after each invocation. As such, it's generally not +recommended to rely on this for high precision intervals with millisecond +accuracy or below. + +#### cancelTimer() + +The `cancelTimer(TimerInterface $timer): void` method can be used to +cancel a pending timer. + +See also [`addPeriodicTimer()`](#addperiodictimer) and [example #2](examples). + +Calling this method on a timer instance that has not been added to this +loop instance or on a timer that has already been cancelled has no effect. + +#### futureTick() + +The `futureTick(callable $listener): void` method can be used to +schedule a callback to be invoked on a future tick of the event loop. + +This works very much similar to timers with an interval of zero seconds, +but does not require the overhead of scheduling a timer queue. + +The tick callback function MUST be able to accept zero parameters. + +The tick callback function MUST NOT throw an `Exception`. +The return value of the tick callback function will be ignored and has +no effect, so for performance reasons you're recommended to not return +any excessive data structures. + +If you want to access any variables within your callback function, you +can bind arbitrary data to a callback closure like this: + +```php +function hello($name, LoopInterface $loop) +{ + $loop->futureTick(function () use ($name) { + echo "hello $name\n"; + }); +} + +hello('Tester', $loop); +``` + +Unlike timers, tick callbacks are guaranteed to be executed in the order +they are enqueued. +Also, once a callback is enqueued, there's no way to cancel this operation. + +This is often used to break down bigger tasks into smaller steps (a form +of cooperative multitasking). + +```php +$loop->futureTick(function () { + echo 'b'; +}); +$loop->futureTick(function () { + echo 'c'; +}); +echo 'a'; +``` + +See also [example #3](examples). + +#### addSignal() + +The `addSignal(int $signal, callable $listener): void` method can be used to +register a listener to be notified when a signal has been caught by this process. + +This is useful to catch user interrupt signals or shutdown signals from +tools like `supervisor` or `systemd`. + +The listener callback function MUST be able to accept a single parameter, +the signal added by this method or you MAY use a function which +has no parameters at all. + +The listener callback function MUST NOT throw an `Exception`. +The return value of the listener callback function will be ignored and has +no effect, so for performance reasons you're recommended to not return +any excessive data structures. + +```php +$loop->addSignal(SIGINT, function (int $signal) { + echo 'Caught user interrupt signal' . PHP_EOL; +}); +``` + +See also [example #4](examples). + +Signaling is only available on Unix-like platform, Windows isn't +supported due to operating system limitations. +This method may throw a `BadMethodCallException` if signals aren't +supported on this platform, for example when required extensions are +missing. + +**Note: A listener can only be added once to the same signal, any +attempts to add it more then once will be ignored.** + +#### removeSignal() + +The `removeSignal(int $signal, callable $listener): void` method can be used to +remove a previously added signal listener. + +```php +$loop->removeSignal(SIGINT, $listener); +``` + +Any attempts to remove listeners that aren't registered will be ignored. + +#### addReadStream() + +> Advanced! Note that this low-level API is considered advanced usage. + Most use cases should probably use the higher-level + [readable Stream API](https://github.com/reactphp/stream#readablestreaminterface) + instead. + +The `addReadStream(resource $stream, callable $callback): void` method can be used to +register a listener to be notified when a stream is ready to read. + +The first parameter MUST be a valid stream resource that supports +checking whether it is ready to read by this loop implementation. +A single stream resource MUST NOT be added more than once. +Instead, either call [`removeReadStream()`](#removereadstream) first or +react to this event with a single listener and then dispatch from this +listener. This method MAY throw an `Exception` if the given resource type +is not supported by this loop implementation. + +The listener callback function MUST be able to accept a single parameter, +the stream resource added by this method or you MAY use a function which +has no parameters at all. + +The listener callback function MUST NOT throw an `Exception`. +The return value of the listener callback function will be ignored and has +no effect, so for performance reasons you're recommended to not return +any excessive data structures. + +If you want to access any variables within your callback function, you +can bind arbitrary data to a callback closure like this: + +```php +$loop->addReadStream($stream, function ($stream) use ($name) { + echo $name . ' said: ' . fread($stream); +}); +``` + +See also [example #11](examples). + +You can invoke [`removeReadStream()`](#removereadstream) to remove the +read event listener for this stream. + +The execution order of listeners when multiple streams become ready at +the same time is not guaranteed. + +Some event loop implementations are known to only trigger the listener if +the stream *becomes* readable (edge-triggered) and may not trigger if the +stream has already been readable from the beginning. +This also implies that a stream may not be recognized as readable when data +is still left in PHP's internal stream buffers. +As such, it's recommended to use `stream_set_read_buffer($stream, 0);` +to disable PHP's internal read buffer in this case. + +#### addWriteStream() + +> Advanced! Note that this low-level API is considered advanced usage. + Most use cases should probably use the higher-level + [writable Stream API](https://github.com/reactphp/stream#writablestreaminterface) + instead. + +The `addWriteStream(resource $stream, callable $callback): void` method can be used to +register a listener to be notified when a stream is ready to write. + +The first parameter MUST be a valid stream resource that supports +checking whether it is ready to write by this loop implementation. +A single stream resource MUST NOT be added more than once. +Instead, either call [`removeWriteStream()`](#removewritestream) first or +react to this event with a single listener and then dispatch from this +listener. This method MAY throw an `Exception` if the given resource type +is not supported by this loop implementation. + +The listener callback function MUST be able to accept a single parameter, +the stream resource added by this method or you MAY use a function which +has no parameters at all. + +The listener callback function MUST NOT throw an `Exception`. +The return value of the listener callback function will be ignored and has +no effect, so for performance reasons you're recommended to not return +any excessive data structures. + +If you want to access any variables within your callback function, you +can bind arbitrary data to a callback closure like this: + +```php +$loop->addWriteStream($stream, function ($stream) use ($name) { + fwrite($stream, 'Hello ' . $name); +}); +``` + +See also [example #12](examples). + +You can invoke [`removeWriteStream()`](#removewritestream) to remove the +write event listener for this stream. + +The execution order of listeners when multiple streams become ready at +the same time is not guaranteed. + +#### removeReadStream() + +The `removeReadStream(resource $stream): void` method can be used to +remove the read event listener for the given stream. + +Removing a stream from the loop that has already been removed or trying +to remove a stream that was never added or is invalid has no effect. + +#### removeWriteStream() + +The `removeWriteStream(resource $stream): void` method can be used to +remove the write event listener for the given stream. + +Removing a stream from the loop that has already been removed or trying +to remove a stream that was never added or is invalid has no effect. + +## Install + +The recommended way to install this library is [through Composer](https://getcomposer.org). +[New to Composer?](https://getcomposer.org/doc/00-intro.md) + +This will install the latest supported version: + +```bash +$ composer require react/event-loop:^0.5.1 +``` + +See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. + +This project aims to run on any platform and thus does not require any PHP +extensions and supports running on legacy PHP 5.3 through current PHP 7+ and +HHVM. +It's *highly recommended to use PHP 7+* for this project. + +Installing any of the event loop extensions is suggested, but entirely optional. +See also [event loop implementations](#loop-implementations) for more details. + +## Tests + +To run the test suite, you first need to clone this repo and then install all +dependencies [through Composer](https://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). + +## More + +* See our [Stream component](https://github.com/reactphp/stream) for more + information on how streams are used in real-world applications. +* See our [users wiki](https://github.com/reactphp/react/wiki/Users) and the + [dependents on Packagist](https://packagist.org/packages/react/event-loop/dependents) + for a list of packages that use the EventLoop in real-world applications. diff --git a/assets/php/vendor/react/event-loop/composer.json b/assets/php/vendor/react/event-loop/composer.json new file mode 100644 index 0000000..24974ec --- /dev/null +++ b/assets/php/vendor/react/event-loop/composer.json @@ -0,0 +1,21 @@ +{ + "name": "react/event-loop", + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": ["event-loop", "asynchronous"], + "license": "MIT", + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8.35 || ^5.7 || ^6.4" + }, + "suggest": { + "ext-event": "~1.0 for ExtEventLoop", + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + }, + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src" + } + } +} diff --git a/assets/php/vendor/react/event-loop/examples/01-timers.php b/assets/php/vendor/react/event-loop/examples/01-timers.php new file mode 100644 index 0000000..e6107e4 --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/01-timers.php @@ -0,0 +1,15 @@ +<?php + +require __DIR__ . '/../vendor/autoload.php'; + +$loop = React\EventLoop\Factory::create(); + +$loop->addTimer(0.8, function () { + echo 'world!' . PHP_EOL; +}); + +$loop->addTimer(0.3, function () { + echo 'hello '; +}); + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/02-periodic.php b/assets/php/vendor/react/event-loop/examples/02-periodic.php new file mode 100644 index 0000000..5e138a6 --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/02-periodic.php @@ -0,0 +1,16 @@ +<?php + +require __DIR__ . '/../vendor/autoload.php'; + +$loop = React\EventLoop\Factory::create(); + +$timer = $loop->addPeriodicTimer(0.1, function () { + echo 'tick!' . PHP_EOL; +}); + +$loop->addTimer(1.0, function () use ($loop, $timer) { + $loop->cancelTimer($timer); + echo 'Done' . PHP_EOL; +}); + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/03-ticks.php b/assets/php/vendor/react/event-loop/examples/03-ticks.php new file mode 100644 index 0000000..3f36c6d --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/03-ticks.php @@ -0,0 +1,15 @@ +<?php + +require __DIR__ . '/../vendor/autoload.php'; + +$loop = React\EventLoop\Factory::create(); + +$loop->futureTick(function () { + echo 'b'; +}); +$loop->futureTick(function () { + echo 'c'; +}); +echo 'a'; + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/04-signals.php b/assets/php/vendor/react/event-loop/examples/04-signals.php new file mode 100644 index 0000000..90b6898 --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/04-signals.php @@ -0,0 +1,19 @@ +<?php + +require __DIR__ . '/../vendor/autoload.php'; + +if (!defined('SIGINT')) { + fwrite(STDERR, 'Not supported on your platform (ext-pcntl missing or Windows?)' . PHP_EOL); + exit(1); +} + +$loop = React\EventLoop\Factory::create(); + +$loop->addSignal(SIGINT, $func = function ($signal) use ($loop, &$func) { + echo 'Signal: ', (string)$signal, PHP_EOL; + $loop->removeSignal(SIGINT, $func); +}); + +echo 'Listening for SIGINT. Use "kill -SIGINT ' . getmypid() . '" or CTRL+C' . PHP_EOL; + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/11-consume-stdin.php b/assets/php/vendor/react/event-loop/examples/11-consume-stdin.php new file mode 100644 index 0000000..2a77245 --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/11-consume-stdin.php @@ -0,0 +1,30 @@ +<?php + +use React\EventLoop\Factory; + +require __DIR__ . '/../vendor/autoload.php'; + +if (!defined('STDIN') || stream_set_blocking(STDIN, false) !== true) { + fwrite(STDERR, 'ERROR: Unable to set STDIN non-blocking (not CLI or Windows?)' . PHP_EOL); + exit(1); +} + +$loop = Factory::create(); + +// read everything from STDIN and report number of bytes +// for illustration purposes only, should use react/stream instead +$loop->addReadStream(STDIN, function ($stream) use ($loop) { + $chunk = fread($stream, 64 * 1024); + + // reading nothing means we reached EOF + if ($chunk === '') { + $loop->removeReadStream($stream); + stream_set_blocking($stream, true); + fclose($stream); + return; + } + + echo strlen($chunk) . ' bytes' . PHP_EOL; +}); + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/12-generate-yes.php b/assets/php/vendor/react/event-loop/examples/12-generate-yes.php new file mode 100644 index 0000000..ebc2beb --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/12-generate-yes.php @@ -0,0 +1,41 @@ +<?php + +require __DIR__ . '/../vendor/autoload.php'; + +// data can be given as first argument or defaults to "y" +$data = (isset($argv[1]) ? $argv[1] : 'y') . "\n"; + +// repeat data X times in order to fill around 200 KB +$data = str_repeat($data, round(200000 / strlen($data))); + +$loop = React\EventLoop\Factory::create(); + +if (!defined('STDOUT') || stream_set_blocking(STDOUT, false) !== true) { + fwrite(STDERR, 'ERROR: Unable to set STDOUT non-blocking (not CLI or Windows?)' . PHP_EOL); + exit(1); +} + +// write data to STDOUT whenever its write buffer accepts data +// for illustrations purpose only, should use react/stream instead +$loop->addWriteStream(STDOUT, function ($stdout) use ($loop, &$data) { + // try to write data + $r = fwrite($stdout, $data); + + // nothing could be written despite being writable => closed + if ($r === 0) { + $loop->removeWriteStream($stdout); + fclose($stdout); + stream_set_blocking($stdout, true); + fwrite(STDERR, 'Stopped because STDOUT closed' . PHP_EOL); + + return; + } + + // implement a very simple ring buffer, unless everything has been written at once: + // everything written in this iteration will be appended for next iteration + if (isset($data[$r])) { + $data = substr($data, $r) . substr($data, 0, $r); + } +}); + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/13-http-client-blocking.php b/assets/php/vendor/react/event-loop/examples/13-http-client-blocking.php new file mode 100644 index 0000000..a2dde55 --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/13-http-client-blocking.php @@ -0,0 +1,35 @@ +<?php + +use React\EventLoop\Factory; + +require __DIR__ . '/../vendor/autoload.php'; + +$loop = Factory::create(); + +// connect to www.google.com:80 (blocking call!) +// for illustration purposes only, should use react/socket instead +$stream = stream_socket_client('tcp://www.google.com:80'); +if (!$stream) { + exit(1); +} +stream_set_blocking($stream, false); + +// send HTTP request +fwrite($stream, "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n"); + +// wait for HTTP response +$loop->addReadStream($stream, function ($stream) use ($loop) { + $chunk = fread($stream, 64 * 1024); + + // reading nothing means we reached EOF + if ($chunk === '') { + echo '[END]' . PHP_EOL; + $loop->removeReadStream($stream); + fclose($stream); + return; + } + + echo $chunk; +}); + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/14-http-client-async.php b/assets/php/vendor/react/event-loop/examples/14-http-client-async.php new file mode 100644 index 0000000..c82c988 --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/14-http-client-async.php @@ -0,0 +1,63 @@ +<?php + +use React\EventLoop\Factory; + +require __DIR__ . '/../vendor/autoload.php'; + +$loop = Factory::create(); + +// resolve hostname before establishing TCP/IP connection (resolving DNS is still blocking here) +// for illustration purposes only, should use react/socket or react/dns instead! +$ip = gethostbyname('www.google.com'); +if (ip2long($ip) === false) { + echo 'Unable to resolve hostname' . PHP_EOL; + exit(1); +} + +// establish TCP/IP connection (non-blocking) +// for illustraction purposes only, should use react/socket instead! +$stream = stream_socket_client('tcp://' . $ip . ':80', $errno, $errstr, null, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT); +if (!$stream) { + exit(1); +} +stream_set_blocking($stream, false); + +// print progress every 10ms +echo 'Connecting'; +$timer = $loop->addPeriodicTimer(0.01, function () { + echo '.'; +}); + +// wait for connection success/error +$loop->addWriteStream($stream, function ($stream) use ($loop, $timer) { + $loop->removeWriteStream($stream); + $loop->cancelTimer($timer); + + // check for socket error (connection rejected) + if (stream_socket_get_name($stream, true) === false) { + echo '[unable to connect]' . PHP_EOL; + exit(1); + } else { + echo '[connected]' . PHP_EOL; + } + + // send HTTP request + fwrite($stream, "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n"); + + // wait for HTTP response + $loop->addReadStream($stream, function ($stream) use ($loop) { + $chunk = fread($stream, 64 * 1024); + + // reading nothing means we reached EOF + if ($chunk === '') { + echo '[END]' . PHP_EOL; + $loop->removeReadStream($stream); + fclose($stream); + return; + } + + echo $chunk; + }); +}); + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/21-http-server.php b/assets/php/vendor/react/event-loop/examples/21-http-server.php new file mode 100644 index 0000000..89520ce --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/21-http-server.php @@ -0,0 +1,36 @@ +<?php + +require __DIR__ . '/../vendor/autoload.php'; + +$loop = React\EventLoop\Factory::create(); + +// start TCP/IP server on localhost:8080 +// for illustration purposes only, should use react/socket instead +$server = stream_socket_server('tcp://127.0.0.1:8080'); +if (!$server) { + exit(1); +} +stream_set_blocking($server, false); + +// wait for incoming connections on server socket +$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->removeWriteStream($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(); diff --git a/assets/php/vendor/react/event-loop/examples/91-benchmark-ticks.php b/assets/php/vendor/react/event-loop/examples/91-benchmark-ticks.php new file mode 100644 index 0000000..3f4690b --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/91-benchmark-ticks.php @@ -0,0 +1,15 @@ +<?php + +use React\EventLoop\Factory; + +require __DIR__ . '/../vendor/autoload.php'; + +$loop = Factory::create(); + +$n = isset($argv[1]) ? (int)$argv[1] : 1000 * 100; + +for ($i = 0; $i < $n; ++$i) { + $loop->futureTick(function () { }); +} + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/92-benchmark-timers.php b/assets/php/vendor/react/event-loop/examples/92-benchmark-timers.php new file mode 100644 index 0000000..e2e02e4 --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/92-benchmark-timers.php @@ -0,0 +1,15 @@ +<?php + +use React\EventLoop\Factory; + +require __DIR__ . '/../vendor/autoload.php'; + +$loop = Factory::create(); + +$n = isset($argv[1]) ? (int)$argv[1] : 1000 * 100; + +for ($i = 0; $i < $n; ++$i) { + $loop->addTimer(0, function () { }); +} + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/93-benchmark-ticks-delay.php b/assets/php/vendor/react/event-loop/examples/93-benchmark-ticks-delay.php new file mode 100644 index 0000000..95ee78c --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/93-benchmark-ticks-delay.php @@ -0,0 +1,22 @@ +<?php + +use React\EventLoop\Factory; + +require __DIR__ . '/../vendor/autoload.php'; + +$loop = Factory::create(); + +$ticks = isset($argv[1]) ? (int)$argv[1] : 1000 * 100; +$tick = function () use (&$tick, &$ticks, $loop) { + if ($ticks > 0) { + --$ticks; + //$loop->addTimer(0, $tick); + $loop->futureTick($tick); + } else { + echo 'done'; + } +}; + +$tick(); + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/94-benchmark-timers-delay.php b/assets/php/vendor/react/event-loop/examples/94-benchmark-timers-delay.php new file mode 100644 index 0000000..2d6cfa2 --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/94-benchmark-timers-delay.php @@ -0,0 +1,22 @@ +<?php + +use React\EventLoop\Factory; + +require __DIR__ . '/../vendor/autoload.php'; + +$loop = Factory::create(); + +$ticks = isset($argv[1]) ? (int)$argv[1] : 1000 * 100; +$tick = function () use (&$tick, &$ticks, $loop) { + if ($ticks > 0) { + --$ticks; + //$loop->futureTick($tick); + $loop->addTimer(0, $tick); + } else { + echo 'done'; + } +}; + +$tick(); + +$loop->run(); diff --git a/assets/php/vendor/react/event-loop/examples/95-benchmark-memory.php b/assets/php/vendor/react/event-loop/examples/95-benchmark-memory.php new file mode 100644 index 0000000..084c404 --- /dev/null +++ b/assets/php/vendor/react/event-loop/examples/95-benchmark-memory.php @@ -0,0 +1,67 @@ +<?php + +/** + * Run the script indefinitely seconds with the loop from the factory and report every 2 seconds: + * php 95-benchmark-memory.php + * Run the script for 30 seconds with the stream_select loop and report every 10 seconds: + * php 95-benchmark-memory.php -t 30 -l StreamSelect -r 10 + */ + +use React\EventLoop\Factory; +use React\EventLoop\LoopInterface; +use React\EventLoop\TimerInterface; + +require __DIR__ . '/../vendor/autoload.php'; + +$args = getopt('t:l:r:'); +$t = isset($args['t']) ? (int)$args['t'] : 0; +$loop = isset($args['l']) && class_exists('React\EventLoop\\' . $args['l'] . 'Loop') ? 'React\EventLoop\\' . $args['l'] . 'Loop' : Factory::create(); + +if (!($loop instanceof LoopInterface)) { + $loop = new $loop(); +} + +$r = isset($args['r']) ? (int)$args['r'] : 2; + +$runs = 0; + +if (5 < $t) { + $loop->addTimer($t, function () use ($loop) { + $loop->stop(); + }); + +} + +$loop->addPeriodicTimer(0.001, function () use (&$runs, $loop) { + $runs++; + + $loop->addPeriodicTimer(1, function (TimerInterface $timer) use ($loop) { + $loop->cancelTimer($timer); + }); +}); + +$loop->addPeriodicTimer($r, function () use (&$runs) { + $kmem = round(memory_get_usage() / 1024); + $kmemReal = round(memory_get_usage(true) / 1024); + echo "Runs:\t\t\t$runs\n"; + echo "Memory (internal):\t$kmem KiB\n"; + echo "Memory (real):\t\t$kmemReal KiB\n"; + echo str_repeat('-', 50), "\n"; +}); + +echo "PHP Version:\t\t", phpversion(), "\n"; +echo "Loop\t\t\t", get_class($loop), "\n"; +echo "Time\t\t\t", date('r'), "\n"; + +echo str_repeat('-', 50), "\n"; + +$beginTime = time(); +$loop->run(); +$endTime = time(); +$timeTaken = $endTime - $beginTime; + +echo "PHP Version:\t\t", phpversion(), "\n"; +echo "Loop\t\t\t", get_class($loop), "\n"; +echo "Time\t\t\t", date('r'), "\n"; +echo "Time taken\t\t", $timeTaken, " seconds\n"; +echo "Runs per second\t\t", round($runs / $timeTaken), "\n"; diff --git a/assets/php/vendor/react/event-loop/phpunit.xml.dist b/assets/php/vendor/react/event-loop/phpunit.xml.dist new file mode 100644 index 0000000..cba6d4d --- /dev/null +++ b/assets/php/vendor/react/event-loop/phpunit.xml.dist @@ -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> diff --git a/assets/php/vendor/react/event-loop/src/ExtEvLoop.php b/assets/php/vendor/react/event-loop/src/ExtEvLoop.php new file mode 100644 index 0000000..74db6d0 --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/ExtEvLoop.php @@ -0,0 +1,252 @@ +<?php + +namespace React\EventLoop; + +use Ev; +use EvIo; +use EvLoop; +use React\EventLoop\Tick\FutureTickQueue; +use React\EventLoop\Timer\Timer; +use SplObjectStorage; + +/** + * An `ext-ev` based event loop. + * + * This loop uses the [`ev` PECL extension](https://pecl.php.net/package/ev), + * that provides an interface to `libev` library. + * + * This loop is known to work with PHP 5.4 through PHP 7+. + * + * @see http://php.net/manual/en/book.ev.php + * @see https://bitbucket.org/osmanov/pecl-ev/overview + */ +class ExtEvLoop implements LoopInterface +{ + /** + * @var EvLoop + */ + private $loop; + + /** + * @var FutureTickQueue + */ + private $futureTickQueue; + + /** + * @var SplObjectStorage + */ + private $timers; + + /** + * @var EvIo[] + */ + private $readStreams = []; + + /** + * @var EvIo[] + */ + private $writeStreams = []; + + /** + * @var bool + */ + private $running; + + /** + * @var SignalsHandler + */ + private $signals; + + /** + * @var \EvSignal[] + */ + private $signalEvents = []; + + public function __construct() + { + $this->loop = new EvLoop(); + $this->futureTickQueue = new FutureTickQueue(); + $this->timers = new SplObjectStorage(); + $this->signals = new SignalsHandler(); + } + + public function addReadStream($stream, $listener) + { + $key = (int)$stream; + + if (isset($this->readStreams[$key])) { + return; + } + + $callback = $this->getStreamListenerClosure($stream, $listener); + $event = $this->loop->io($stream, Ev::READ, $callback); + $this->readStreams[$key] = $event; + } + + /** + * @param resource $stream + * @param callable $listener + * + * @return \Closure + */ + private function getStreamListenerClosure($stream, $listener) + { + return function () use ($stream, $listener) { + call_user_func($listener, $stream); + }; + } + + public function addWriteStream($stream, $listener) + { + $key = (int)$stream; + + if (isset($this->writeStreams[$key])) { + return; + } + + $callback = $this->getStreamListenerClosure($stream, $listener); + $event = $this->loop->io($stream, Ev::WRITE, $callback); + $this->writeStreams[$key] = $event; + } + + public function removeReadStream($stream) + { + $key = (int)$stream; + + if (!isset($this->readStreams[$key])) { + return; + } + + $this->readStreams[$key]->stop(); + unset($this->readStreams[$key]); + } + + public function removeWriteStream($stream) + { + $key = (int)$stream; + + if (!isset($this->writeStreams[$key])) { + return; + } + + $this->writeStreams[$key]->stop(); + unset($this->writeStreams[$key]); + } + + public function addTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, false); + + $that = $this; + $timers = $this->timers; + $callback = function () use ($timer, $timers, $that) { + call_user_func($timer->getCallback(), $timer); + + if ($timers->contains($timer)) { + $that->cancelTimer($timer); + } + }; + + $event = $this->loop->timer($timer->getInterval(), 0.0, $callback); + $this->timers->attach($timer, $event); + + return $timer; + } + + public function addPeriodicTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, true); + + $callback = function () use ($timer) { + call_user_func($timer->getCallback(), $timer); + }; + + $event = $this->loop->timer($interval, $interval, $callback); + $this->timers->attach($timer, $event); + + return $timer; + } + + public function cancelTimer(TimerInterface $timer) + { + if (!isset($this->timers[$timer])) { + return; + } + + $event = $this->timers[$timer]; + $event->stop(); + $this->timers->detach($timer); + } + + public function futureTick($listener) + { + $this->futureTickQueue->add($listener); + } + + public function run() + { + $this->running = true; + + while ($this->running) { + $this->futureTickQueue->tick(); + + $hasPendingCallbacks = !$this->futureTickQueue->isEmpty(); + $wasJustStopped = !$this->running; + $nothingLeftToDo = !$this->readStreams + && !$this->writeStreams + && !$this->timers->count() + && $this->signals->isEmpty(); + + $flags = Ev::RUN_ONCE; + if ($wasJustStopped || $hasPendingCallbacks) { + $flags |= Ev::RUN_NOWAIT; + } elseif ($nothingLeftToDo) { + break; + } + + $this->loop->run($flags); + } + } + + public function stop() + { + $this->running = false; + } + + public function __destruct() + { + /** @var TimerInterface $timer */ + foreach ($this->timers as $timer) { + $this->cancelTimer($timer); + } + + foreach ($this->readStreams as $key => $stream) { + $this->removeReadStream($key); + } + + foreach ($this->writeStreams as $key => $stream) { + $this->removeWriteStream($key); + } + } + + public function addSignal($signal, $listener) + { + $this->signals->add($signal, $listener); + + if (!isset($this->signalEvents[$signal])) { + $this->signalEvents[$signal] = $this->loop->signal($signal, function() use ($signal) { + $this->signals->call($signal); + }); + } + } + + public function removeSignal($signal, $listener) + { + $this->signals->remove($signal, $listener); + + if (isset($this->signalEvents[$signal])) { + $this->signalEvents[$signal]->stop(); + unset($this->signalEvents[$signal]); + } + } +} diff --git a/assets/php/vendor/react/event-loop/src/ExtEventLoop.php b/assets/php/vendor/react/event-loop/src/ExtEventLoop.php new file mode 100644 index 0000000..622dd47 --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/ExtEventLoop.php @@ -0,0 +1,259 @@ +<?php + +namespace React\EventLoop; + +use BadMethodCallException; +use Event; +use EventBase; +use EventConfig as EventBaseConfig; +use React\EventLoop\Tick\FutureTickQueue; +use React\EventLoop\Timer\Timer; +use SplObjectStorage; + +/** + * An `ext-event` based event loop. + * + * This uses the [`event` PECL extension](https://pecl.php.net/package/event). + * It supports the same backends as libevent. + * + * This loop is known to work with PHP 5.4 through PHP 7+. + * + * @link https://pecl.php.net/package/event + */ +final class ExtEventLoop implements LoopInterface +{ + private $eventBase; + private $futureTickQueue; + private $timerCallback; + private $timerEvents; + private $streamCallback; + private $readEvents = array(); + private $writeEvents = array(); + private $readListeners = array(); + private $writeListeners = array(); + private $readRefs = array(); + private $writeRefs = array(); + private $running; + private $signals; + private $signalEvents = array(); + + public function __construct() + { + if (!class_exists('EventBase', false)) { + throw new BadMethodCallException('Cannot create ExtEventLoop, ext-event extension missing'); + } + + $config = new EventBaseConfig(); + $config->requireFeatures(EventBaseConfig::FEATURE_FDS); + + $this->eventBase = new EventBase($config); + $this->futureTickQueue = new FutureTickQueue(); + $this->timerEvents = new SplObjectStorage(); + $this->signals = new SignalsHandler(); + + $this->createTimerCallback(); + $this->createStreamCallback(); + } + + public function addReadStream($stream, $listener) + { + $key = (int) $stream; + if (isset($this->readListeners[$key])) { + return; + } + + $event = new Event($this->eventBase, $stream, Event::PERSIST | Event::READ, $this->streamCallback); + $event->add(); + $this->readEvents[$key] = $event; + $this->readListeners[$key] = $listener; + + // ext-event does not increase refcount on stream resources for PHP 7+ + // manually keep track of stream resource to prevent premature garbage collection + if (PHP_VERSION_ID >= 70000) { + $this->readRefs[$key] = $stream; + } + } + + public function addWriteStream($stream, $listener) + { + $key = (int) $stream; + if (isset($this->writeListeners[$key])) { + return; + } + + $event = new Event($this->eventBase, $stream, Event::PERSIST | Event::WRITE, $this->streamCallback); + $event->add(); + $this->writeEvents[$key] = $event; + $this->writeListeners[$key] = $listener; + + // ext-event does not increase refcount on stream resources for PHP 7+ + // manually keep track of stream resource to prevent premature garbage collection + if (PHP_VERSION_ID >= 70000) { + $this->writeRefs[$key] = $stream; + } + } + + public function removeReadStream($stream) + { + $key = (int) $stream; + + if (isset($this->readEvents[$key])) { + $this->readEvents[$key]->free(); + unset( + $this->readEvents[$key], + $this->readListeners[$key], + $this->readRefs[$key] + ); + } + } + + public function removeWriteStream($stream) + { + $key = (int) $stream; + + if (isset($this->writeEvents[$key])) { + $this->writeEvents[$key]->free(); + unset( + $this->writeEvents[$key], + $this->writeListeners[$key], + $this->writeRefs[$key] + ); + } + } + + public function addTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, false); + + $this->scheduleTimer($timer); + + return $timer; + } + + public function addPeriodicTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, true); + + $this->scheduleTimer($timer); + + return $timer; + } + + public function cancelTimer(TimerInterface $timer) + { + if ($this->timerEvents->contains($timer)) { + $this->timerEvents[$timer]->free(); + $this->timerEvents->detach($timer); + } + } + + public function futureTick($listener) + { + $this->futureTickQueue->add($listener); + } + + public function addSignal($signal, $listener) + { + $this->signals->add($signal, $listener); + + if (!isset($this->signalEvents[$signal])) { + $this->signalEvents[$signal] = Event::signal($this->eventBase, $signal, array($this->signals, 'call')); + $this->signalEvents[$signal]->add(); + } + } + + public function removeSignal($signal, $listener) + { + $this->signals->remove($signal, $listener); + + if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) { + $this->signalEvents[$signal]->free(); + unset($this->signalEvents[$signal]); + } + } + + public function run() + { + $this->running = true; + + while ($this->running) { + $this->futureTickQueue->tick(); + + $flags = EventBase::LOOP_ONCE; + if (!$this->running || !$this->futureTickQueue->isEmpty()) { + $flags |= EventBase::LOOP_NONBLOCK; + } elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) { + break; + } + + $this->eventBase->loop($flags); + } + } + + public function stop() + { + $this->running = false; + } + + /** + * Schedule a timer for execution. + * + * @param TimerInterface $timer + */ + private function scheduleTimer(TimerInterface $timer) + { + $flags = Event::TIMEOUT; + + if ($timer->isPeriodic()) { + $flags |= Event::PERSIST; + } + + $event = new Event($this->eventBase, -1, $flags, $this->timerCallback, $timer); + $this->timerEvents[$timer] = $event; + + $event->add($timer->getInterval()); + } + + /** + * Create a callback used as the target of timer events. + * + * A reference is kept to the callback for the lifetime of the loop + * to prevent "Cannot destroy active lambda function" fatal error from + * the event extension. + */ + private function createTimerCallback() + { + $timers = $this->timerEvents; + $this->timerCallback = function ($_, $__, $timer) use ($timers) { + call_user_func($timer->getCallback(), $timer); + + if (!$timer->isPeriodic() && $timers->contains($timer)) { + $this->cancelTimer($timer); + } + }; + } + + /** + * Create a callback used as the target of stream events. + * + * A reference is kept to the callback for the lifetime of the loop + * to prevent "Cannot destroy active lambda function" fatal error from + * the event extension. + */ + private function createStreamCallback() + { + $read =& $this->readListeners; + $write =& $this->writeListeners; + $this->streamCallback = function ($stream, $flags) use (&$read, &$write) { + $key = (int) $stream; + + if (Event::READ === (Event::READ & $flags) && isset($read[$key])) { + call_user_func($read[$key], $stream); + } + + if (Event::WRITE === (Event::WRITE & $flags) && isset($write[$key])) { + call_user_func($write[$key], $stream); + } + }; + } +} diff --git a/assets/php/vendor/react/event-loop/src/ExtLibevLoop.php b/assets/php/vendor/react/event-loop/src/ExtLibevLoop.php new file mode 100644 index 0000000..d3b0df8 --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/ExtLibevLoop.php @@ -0,0 +1,199 @@ +<?php + +namespace React\EventLoop; + +use BadMethodCallException; +use libev\EventLoop; +use libev\IOEvent; +use libev\SignalEvent; +use libev\TimerEvent; +use React\EventLoop\Tick\FutureTickQueue; +use React\EventLoop\Timer\Timer; +use SplObjectStorage; + +/** + * An `ext-libev` based event loop. + * + * This uses an [unofficial `libev` extension](https://github.com/m4rw3r/php-libev). + * It supports the same backends as libevent. + * + * This loop does only work with PHP 5. + * An update for PHP 7 is [unlikely](https://github.com/m4rw3r/php-libev/issues/8) + * to happen any time soon. + * + * @see https://github.com/m4rw3r/php-libev + * @see https://gist.github.com/1688204 + */ +final class ExtLibevLoop implements LoopInterface +{ + private $loop; + private $futureTickQueue; + private $timerEvents; + private $readEvents = array(); + private $writeEvents = array(); + private $running; + private $signals; + private $signalEvents = array(); + + public function __construct() + { + if (!class_exists('libev\EventLoop', false)) { + throw new BadMethodCallException('Cannot create ExtLibevLoop, ext-libev extension missing'); + } + + $this->loop = new EventLoop(); + $this->futureTickQueue = new FutureTickQueue(); + $this->timerEvents = new SplObjectStorage(); + $this->signals = new SignalsHandler(); + } + + public function addReadStream($stream, $listener) + { + if (isset($this->readEvents[(int) $stream])) { + return; + } + + $callback = function () use ($stream, $listener) { + call_user_func($listener, $stream); + }; + + $event = new IOEvent($callback, $stream, IOEvent::READ); + $this->loop->add($event); + + $this->readEvents[(int) $stream] = $event; + } + + public function addWriteStream($stream, $listener) + { + if (isset($this->writeEvents[(int) $stream])) { + return; + } + + $callback = function () use ($stream, $listener) { + call_user_func($listener, $stream); + }; + + $event = new IOEvent($callback, $stream, IOEvent::WRITE); + $this->loop->add($event); + + $this->writeEvents[(int) $stream] = $event; + } + + public function removeReadStream($stream) + { + $key = (int) $stream; + + if (isset($this->readEvents[$key])) { + $this->readEvents[$key]->stop(); + $this->loop->remove($this->readEvents[$key]); + unset($this->readEvents[$key]); + } + } + + public function removeWriteStream($stream) + { + $key = (int) $stream; + + if (isset($this->writeEvents[$key])) { + $this->writeEvents[$key]->stop(); + $this->loop->remove($this->writeEvents[$key]); + unset($this->writeEvents[$key]); + } + } + + public function addTimer($interval, $callback) + { + $timer = new Timer( $interval, $callback, false); + + $that = $this; + $timers = $this->timerEvents; + $callback = function () use ($timer, $timers, $that) { + call_user_func($timer->getCallback(), $timer); + + if ($timers->contains($timer)) { + $that->cancelTimer($timer); + } + }; + + $event = new TimerEvent($callback, $timer->getInterval()); + $this->timerEvents->attach($timer, $event); + $this->loop->add($event); + + return $timer; + } + + public function addPeriodicTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, true); + + $callback = function () use ($timer) { + call_user_func($timer->getCallback(), $timer); + }; + + $event = new TimerEvent($callback, $interval, $interval); + $this->timerEvents->attach($timer, $event); + $this->loop->add($event); + + return $timer; + } + + public function cancelTimer(TimerInterface $timer) + { + if (isset($this->timerEvents[$timer])) { + $this->loop->remove($this->timerEvents[$timer]); + $this->timerEvents->detach($timer); + } + } + + public function futureTick($listener) + { + $this->futureTickQueue->add($listener); + } + + public function addSignal($signal, $listener) + { + $this->signals->add($signal, $listener); + + if (!isset($this->signalEvents[$signal])) { + $signals = $this->signals; + $this->signalEvents[$signal] = new SignalEvent(function () use ($signals, $signal) { + $signals->call($signal); + }, $signal); + $this->loop->add($this->signalEvents[$signal]); + } + } + + public function removeSignal($signal, $listener) + { + $this->signals->remove($signal, $listener); + + if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) { + $this->signalEvents[$signal]->stop(); + $this->loop->remove($this->signalEvents[$signal]); + unset($this->signalEvents[$signal]); + } + } + + public function run() + { + $this->running = true; + + while ($this->running) { + $this->futureTickQueue->tick(); + + $flags = EventLoop::RUN_ONCE; + if (!$this->running || !$this->futureTickQueue->isEmpty()) { + $flags |= EventLoop::RUN_NOWAIT; + } elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) { + break; + } + + $this->loop->run($flags); + } + } + + public function stop() + { + $this->running = false; + } +} diff --git a/assets/php/vendor/react/event-loop/src/ExtLibeventLoop.php b/assets/php/vendor/react/event-loop/src/ExtLibeventLoop.php new file mode 100644 index 0000000..427f8db --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/ExtLibeventLoop.php @@ -0,0 +1,283 @@ +<?php + +namespace React\EventLoop; + +use BadMethodCallException; +use Event; +use EventBase; +use React\EventLoop\Tick\FutureTickQueue; +use React\EventLoop\Timer\Timer; +use SplObjectStorage; + +/** + * An `ext-libevent` based event loop. + * + * This uses the [`libevent` PECL extension](https://pecl.php.net/package/libevent). + * `libevent` itself supports a number of system-specific backends (epoll, kqueue). + * + * This event loop does only work with PHP 5. + * An [unofficial update](https://github.com/php/pecl-event-libevent/pull/2) for + * PHP 7 does exist, but it is known to cause regular crashes due to `SEGFAULT`s. + * To reiterate: Using this event loop on PHP 7 is not recommended. + * Accordingly, the [`Factory`](#factory) will not try to use this event loop on + * PHP 7. + * + * This event loop is known to trigger a readable listener only if + * the stream *becomes* readable (edge-triggered) and may not trigger if the + * stream has already been readable from the beginning. + * This also implies that a stream may not be recognized as readable when data + * is still left in PHP's internal stream buffers. + * As such, it's recommended to use `stream_set_read_buffer($stream, 0);` + * to disable PHP's internal read buffer in this case. + * See also [`addReadStream()`](#addreadstream) for more details. + * + * @link https://pecl.php.net/package/libevent + */ +final class ExtLibeventLoop implements LoopInterface +{ + /** @internal */ + const MICROSECONDS_PER_SECOND = 1000000; + + private $eventBase; + private $futureTickQueue; + private $timerCallback; + private $timerEvents; + private $streamCallback; + private $readEvents = array(); + private $writeEvents = array(); + private $readListeners = array(); + private $writeListeners = array(); + private $running; + private $signals; + private $signalEvents = array(); + + public function __construct() + { + if (!function_exists('event_base_new')) { + throw new BadMethodCallException('Cannot create ExtLibeventLoop, ext-libevent extension missing'); + } + + $this->eventBase = event_base_new(); + $this->futureTickQueue = new FutureTickQueue(); + $this->timerEvents = new SplObjectStorage(); + $this->signals = new SignalsHandler(); + + $this->createTimerCallback(); + $this->createStreamCallback(); + } + + public function addReadStream($stream, $listener) + { + $key = (int) $stream; + if (isset($this->readListeners[$key])) { + return; + } + + $event = event_new(); + event_set($event, $stream, EV_PERSIST | EV_READ, $this->streamCallback); + event_base_set($event, $this->eventBase); + event_add($event); + + $this->readEvents[$key] = $event; + $this->readListeners[$key] = $listener; + } + + public function addWriteStream($stream, $listener) + { + $key = (int) $stream; + if (isset($this->writeListeners[$key])) { + return; + } + + $event = event_new(); + event_set($event, $stream, EV_PERSIST | EV_WRITE, $this->streamCallback); + event_base_set($event, $this->eventBase); + event_add($event); + + $this->writeEvents[$key] = $event; + $this->writeListeners[$key] = $listener; + } + + public function removeReadStream($stream) + { + $key = (int) $stream; + + if (isset($this->readListeners[$key])) { + $event = $this->readEvents[$key]; + event_del($event); + event_free($event); + + unset( + $this->readEvents[$key], + $this->readListeners[$key] + ); + } + } + + public function removeWriteStream($stream) + { + $key = (int) $stream; + + if (isset($this->writeListeners[$key])) { + $event = $this->writeEvents[$key]; + event_del($event); + event_free($event); + + unset( + $this->writeEvents[$key], + $this->writeListeners[$key] + ); + } + } + + public function addTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, false); + + $this->scheduleTimer($timer); + + return $timer; + } + + public function addPeriodicTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, true); + + $this->scheduleTimer($timer); + + return $timer; + } + + public function cancelTimer(TimerInterface $timer) + { + if ($this->timerEvents->contains($timer)) { + $event = $this->timerEvents[$timer]; + event_del($event); + event_free($event); + + $this->timerEvents->detach($timer); + } + } + + public function futureTick($listener) + { + $this->futureTickQueue->add($listener); + } + + public function addSignal($signal, $listener) + { + $this->signals->add($signal, $listener); + + if (!isset($this->signalEvents[$signal])) { + $this->signalEvents[$signal] = event_new(); + event_set($this->signalEvents[$signal], $signal, EV_PERSIST | EV_SIGNAL, array($this->signals, 'call')); + event_base_set($this->signalEvents[$signal], $this->eventBase); + event_add($this->signalEvents[$signal]); + } + } + + public function removeSignal($signal, $listener) + { + $this->signals->remove($signal, $listener); + + if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) { + event_del($this->signalEvents[$signal]); + event_free($this->signalEvents[$signal]); + unset($this->signalEvents[$signal]); + } + } + + public function run() + { + $this->running = true; + + while ($this->running) { + $this->futureTickQueue->tick(); + + $flags = EVLOOP_ONCE; + if (!$this->running || !$this->futureTickQueue->isEmpty()) { + $flags |= EVLOOP_NONBLOCK; + } elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) { + break; + } + + event_base_loop($this->eventBase, $flags); + } + } + + public function stop() + { + $this->running = false; + } + + /** + * Schedule a timer for execution. + * + * @param TimerInterface $timer + */ + private function scheduleTimer(TimerInterface $timer) + { + $this->timerEvents[$timer] = $event = event_timer_new(); + + event_timer_set($event, $this->timerCallback, $timer); + event_base_set($event, $this->eventBase); + event_add($event, $timer->getInterval() * self::MICROSECONDS_PER_SECOND); + } + + /** + * Create a callback used as the target of timer events. + * + * A reference is kept to the callback for the lifetime of the loop + * to prevent "Cannot destroy active lambda function" fatal error from + * the event extension. + */ + private function createTimerCallback() + { + $that = $this; + $timers = $this->timerEvents; + $this->timerCallback = function ($_, $__, $timer) use ($timers, $that) { + call_user_func($timer->getCallback(), $timer); + + // Timer already cancelled ... + if (!$timers->contains($timer)) { + return; + } + + // Reschedule periodic timers ... + if ($timer->isPeriodic()) { + event_add( + $timers[$timer], + $timer->getInterval() * ExtLibeventLoop::MICROSECONDS_PER_SECOND + ); + + // Clean-up one shot timers ... + } else { + $that->cancelTimer($timer); + } + }; + } + + /** + * Create a callback used as the target of stream events. + * + * A reference is kept to the callback for the lifetime of the loop + * to prevent "Cannot destroy active lambda function" fatal error from + * the event extension. + */ + private function createStreamCallback() + { + $read =& $this->readListeners; + $write =& $this->writeListeners; + $this->streamCallback = function ($stream, $flags) use (&$read, &$write) { + $key = (int) $stream; + + if (EV_READ === (EV_READ & $flags) && isset($read[$key])) { + call_user_func($read[$key], $stream); + } + + if (EV_WRITE === (EV_WRITE & $flags) && isset($write[$key])) { + call_user_func($write[$key], $stream); + } + }; + } +} diff --git a/assets/php/vendor/react/event-loop/src/Factory.php b/assets/php/vendor/react/event-loop/src/Factory.php new file mode 100644 index 0000000..b46fc07 --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/Factory.php @@ -0,0 +1,41 @@ +<?php + +namespace React\EventLoop; + +/** + * The `Factory` class exists as a convenient way to pick the best available event loop implementation. + */ +final class Factory +{ + /** + * Creates a new event loop instance + * + * ```php + * $loop = React\EventLoop\Factory::create(); + * ``` + * + * This method always returns an instance implementing `LoopInterface`, + * the actual event loop implementation is an implementation detail. + * + * This method should usually only be called once at the beginning of the program. + * + * @return LoopInterface + */ + public static function create() + { + // @codeCoverageIgnoreStart + if (class_exists('libev\EventLoop', false)) { + return new ExtLibevLoop(); + } elseif (class_exists('EvLoop', false)) { + return new ExtEvLoop(); + } elseif (class_exists('EventBase', false)) { + return new ExtEventLoop(); + } elseif (function_exists('event_base_new') && PHP_VERSION_ID < 70000) { + // only use ext-libevent on PHP < 7 for now + return new ExtLibeventLoop(); + } + + return new StreamSelectLoop(); + // @codeCoverageIgnoreEnd + } +} diff --git a/assets/php/vendor/react/event-loop/src/LoopInterface.php b/assets/php/vendor/react/event-loop/src/LoopInterface.php new file mode 100644 index 0000000..1cc8640 --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/LoopInterface.php @@ -0,0 +1,463 @@ +<?php + +namespace React\EventLoop; + +interface LoopInterface +{ + /** + * [Advanced] Register a listener to be notified when a stream is ready to read. + * + * Note that this low-level API is considered advanced usage. + * Most use cases should probably use the higher-level + * [readable Stream API](https://github.com/reactphp/stream#readablestreaminterface) + * instead. + * + * The first parameter MUST be a valid stream resource that supports + * checking whether it is ready to read by this loop implementation. + * A single stream resource MUST NOT be added more than once. + * Instead, either call [`removeReadStream()`](#removereadstream) first or + * react to this event with a single listener and then dispatch from this + * listener. This method MAY throw an `Exception` if the given resource type + * is not supported by this loop implementation. + * + * The listener callback function MUST be able to accept a single parameter, + * the stream resource added by this method or you MAY use a function which + * has no parameters at all. + * + * The listener callback function MUST NOT throw an `Exception`. + * The return value of the listener callback function will be ignored and has + * no effect, so for performance reasons you're recommended to not return + * any excessive data structures. + * + * If you want to access any variables within your callback function, you + * can bind arbitrary data to a callback closure like this: + * + * ```php + * $loop->addReadStream($stream, function ($stream) use ($name) { + * echo $name . ' said: ' . fread($stream); + * }); + * ``` + * + * See also [example #11](examples). + * + * You can invoke [`removeReadStream()`](#removereadstream) to remove the + * read event listener for this stream. + * + * The execution order of listeners when multiple streams become ready at + * the same time is not guaranteed. + * + * @param resource $stream The PHP stream resource to check. + * @param callable $listener Invoked when the stream is ready. + * @throws \Exception if the given resource type is not supported by this loop implementation + * @see self::removeReadStream() + */ + public function addReadStream($stream, $listener); + + /** + * [Advanced] Register a listener to be notified when a stream is ready to write. + * + * Note that this low-level API is considered advanced usage. + * Most use cases should probably use the higher-level + * [writable Stream API](https://github.com/reactphp/stream#writablestreaminterface) + * instead. + * + * The first parameter MUST be a valid stream resource that supports + * checking whether it is ready to write by this loop implementation. + * A single stream resource MUST NOT be added more than once. + * Instead, either call [`removeWriteStream()`](#removewritestream) first or + * react to this event with a single listener and then dispatch from this + * listener. This method MAY throw an `Exception` if the given resource type + * is not supported by this loop implementation. + * + * The listener callback function MUST be able to accept a single parameter, + * the stream resource added by this method or you MAY use a function which + * has no parameters at all. + * + * The listener callback function MUST NOT throw an `Exception`. + * The return value of the listener callback function will be ignored and has + * no effect, so for performance reasons you're recommended to not return + * any excessive data structures. + * + * If you want to access any variables within your callback function, you + * can bind arbitrary data to a callback closure like this: + * + * ```php + * $loop->addWriteStream($stream, function ($stream) use ($name) { + * fwrite($stream, 'Hello ' . $name); + * }); + * ``` + * + * See also [example #12](examples). + * + * You can invoke [`removeWriteStream()`](#removewritestream) to remove the + * write event listener for this stream. + * + * The execution order of listeners when multiple streams become ready at + * the same time is not guaranteed. + * + * Some event loop implementations are known to only trigger the listener if + * the stream *becomes* readable (edge-triggered) and may not trigger if the + * stream has already been readable from the beginning. + * This also implies that a stream may not be recognized as readable when data + * is still left in PHP's internal stream buffers. + * As such, it's recommended to use `stream_set_read_buffer($stream, 0);` + * to disable PHP's internal read buffer in this case. + * + * @param resource $stream The PHP stream resource to check. + * @param callable $listener Invoked when the stream is ready. + * @throws \Exception if the given resource type is not supported by this loop implementation + * @see self::removeWriteStream() + */ + public function addWriteStream($stream, $listener); + + /** + * Remove the read event listener for the given stream. + * + * Removing a stream from the loop that has already been removed or trying + * to remove a stream that was never added or is invalid has no effect. + * + * @param resource $stream The PHP stream resource. + */ + public function removeReadStream($stream); + + /** + * Remove the write event listener for the given stream. + * + * Removing a stream from the loop that has already been removed or trying + * to remove a stream that was never added or is invalid has no effect. + * + * @param resource $stream The PHP stream resource. + */ + public function removeWriteStream($stream); + + /** + * Enqueue a callback to be invoked once after the given interval. + * + * The timer callback function MUST be able to accept a single parameter, + * the timer instance as also returned by this method or you MAY use a + * function which has no parameters at all. + * + * The timer callback function MUST NOT throw an `Exception`. + * The return value of the timer callback function will be ignored and has + * no effect, so for performance reasons you're recommended to not return + * any excessive data structures. + * + * Unlike [`addPeriodicTimer()`](#addperiodictimer), this method will ensure + * the callback will be invoked only once after the given interval. + * You can invoke [`cancelTimer`](#canceltimer) to cancel a pending timer. + * + * ```php + * $loop->addTimer(0.8, function () { + * echo 'world!' . PHP_EOL; + * }); + * + * $loop->addTimer(0.3, function () { + * echo 'hello '; + * }); + * ``` + * + * See also [example #1](examples). + * + * If you want to access any variables within your callback function, you + * can bind arbitrary data to a callback closure like this: + * + * ```php + * function hello($name, LoopInterface $loop) + * { + * $loop->addTimer(1.0, function () use ($name) { + * echo "hello $name\n"; + * }); + * } + * + * hello('Tester', $loop); + * ``` + * + * This interface does not enforce any particular timer resolution, so + * special care may have to be taken if you rely on very high precision with + * millisecond accuracy or below. Event loop implementations SHOULD work on + * a best effort basis and SHOULD provide at least millisecond accuracy + * unless otherwise noted. Many existing event loop implementations are + * known to provide microsecond accuracy, but it's generally not recommended + * to rely on this high precision. + * + * Similarly, the execution order of timers scheduled to execute at the + * same time (within its possible accuracy) is not guaranteed. + * + * This interface suggests that event loop implementations SHOULD use a + * monotonic time source if available. Given that a monotonic time source is + * not available on PHP by default, event loop implementations MAY fall back + * to using wall-clock time. + * While this does not affect many common use cases, this is an important + * distinction for programs that rely on a high time precision or on systems + * that are subject to discontinuous time adjustments (time jumps). + * This means that if you schedule a timer to trigger in 30s and then adjust + * your system time forward by 20s, the timer SHOULD still trigger in 30s. + * See also [event loop implementations](#loop-implementations) for more details. + * + * @param int|float $interval The number of seconds to wait before execution. + * @param callable $callback The callback to invoke. + * + * @return TimerInterface + */ + public function addTimer($interval, $callback); + + /** + * Enqueue a callback to be invoked repeatedly after the given interval. + * + * The timer callback function MUST be able to accept a single parameter, + * the timer instance as also returned by this method or you MAY use a + * function which has no parameters at all. + * + * The timer callback function MUST NOT throw an `Exception`. + * The return value of the timer callback function will be ignored and has + * no effect, so for performance reasons you're recommended to not return + * any excessive data structures. + * + * Unlike [`addTimer()`](#addtimer), this method will ensure the the + * callback will be invoked infinitely after the given interval or until you + * invoke [`cancelTimer`](#canceltimer). + * + * ```php + * $timer = $loop->addPeriodicTimer(0.1, function () { + * echo 'tick!' . PHP_EOL; + * }); + * + * $loop->addTimer(1.0, function () use ($loop, $timer) { + * $loop->cancelTimer($timer); + * echo 'Done' . PHP_EOL; + * }); + * ``` + * + * See also [example #2](examples). + * + * If you want to limit the number of executions, you can bind + * arbitrary data to a callback closure like this: + * + * ```php + * function hello($name, LoopInterface $loop) + * { + * $n = 3; + * $loop->addPeriodicTimer(1.0, function ($timer) use ($name, $loop, &$n) { + * if ($n > 0) { + * --$n; + * echo "hello $name\n"; + * } else { + * $loop->cancelTimer($timer); + * } + * }); + * } + * + * hello('Tester', $loop); + * ``` + * + * This interface does not enforce any particular timer resolution, so + * special care may have to be taken if you rely on very high precision with + * millisecond accuracy or below. Event loop implementations SHOULD work on + * a best effort basis and SHOULD provide at least millisecond accuracy + * unless otherwise noted. Many existing event loop implementations are + * known to provide microsecond accuracy, but it's generally not recommended + * to rely on this high precision. + * + * Similarly, the execution order of timers scheduled to execute at the + * same time (within its possible accuracy) is not guaranteed. + * + * This interface suggests that event loop implementations SHOULD use a + * monotonic time source if available. Given that a monotonic time source is + * not available on PHP by default, event loop implementations MAY fall back + * to using wall-clock time. + * While this does not affect many common use cases, this is an important + * distinction for programs that rely on a high time precision or on systems + * that are subject to discontinuous time adjustments (time jumps). + * This means that if you schedule a timer to trigger in 30s and then adjust + * your system time forward by 20s, the timer SHOULD still trigger in 30s. + * See also [event loop implementations](#loop-implementations) for more details. + * + * Additionally, periodic timers may be subject to timer drift due to + * re-scheduling after each invocation. As such, it's generally not + * recommended to rely on this for high precision intervals with millisecond + * accuracy or below. + * + * @param int|float $interval The number of seconds to wait before execution. + * @param callable $callback The callback to invoke. + * + * @return TimerInterface + */ + public function addPeriodicTimer($interval, $callback); + + /** + * Cancel a pending timer. + * + * See also [`addPeriodicTimer()`](#addperiodictimer) and [example #2](examples). + * + * Calling this method on a timer instance that has not been added to this + * loop instance or on a timer that has already been cancelled has no effect. + * + * @param TimerInterface $timer The timer to cancel. + * + * @return void + */ + public function cancelTimer(TimerInterface $timer); + + /** + * Schedule a callback to be invoked on a future tick of the event loop. + * + * This works very much similar to timers with an interval of zero seconds, + * but does not require the overhead of scheduling a timer queue. + * + * The tick callback function MUST be able to accept zero parameters. + * + * The tick callback function MUST NOT throw an `Exception`. + * The return value of the tick callback function will be ignored and has + * no effect, so for performance reasons you're recommended to not return + * any excessive data structures. + * + * If you want to access any variables within your callback function, you + * can bind arbitrary data to a callback closure like this: + * + * ```php + * function hello($name, LoopInterface $loop) + * { + * $loop->futureTick(function () use ($name) { + * echo "hello $name\n"; + * }); + * } + * + * hello('Tester', $loop); + * ``` + * + * Unlike timers, tick callbacks are guaranteed to be executed in the order + * they are enqueued. + * Also, once a callback is enqueued, there's no way to cancel this operation. + * + * This is often used to break down bigger tasks into smaller steps (a form + * of cooperative multitasking). + * + * ```php + * $loop->futureTick(function () { + * echo 'b'; + * }); + * $loop->futureTick(function () { + * echo 'c'; + * }); + * echo 'a'; + * ``` + * + * See also [example #3](examples). + * + * @param callable $listener The callback to invoke. + * + * @return void + */ + public function futureTick($listener); + + /** + * Register a listener to be notified when a signal has been caught by this process. + * + * This is useful to catch user interrupt signals or shutdown signals from + * tools like `supervisor` or `systemd`. + * + * The listener callback function MUST be able to accept a single parameter, + * the signal added by this method or you MAY use a function which + * has no parameters at all. + * + * The listener callback function MUST NOT throw an `Exception`. + * The return value of the listener callback function will be ignored and has + * no effect, so for performance reasons you're recommended to not return + * any excessive data structures. + * + * ```php + * $loop->addSignal(SIGINT, function (int $signal) { + * echo 'Caught user interrupt signal' . PHP_EOL; + * }); + * ``` + * + * See also [example #4](examples). + * + * Signaling is only available on Unix-like platform, Windows isn't + * supported due to operating system limitations. + * This method may throw a `BadMethodCallException` if signals aren't + * supported on this platform, for example when required extensions are + * missing. + * + * **Note: A listener can only be added once to the same signal, any + * attempts to add it more then once will be ignored.** + * + * @param int $signal + * @param callable $listener + * + * @throws \BadMethodCallException when signals aren't supported on this + * platform, for example when required extensions are missing. + * + * @return void + */ + public function addSignal($signal, $listener); + + /** + * Removes a previously added signal listener. + * + * ```php + * $loop->removeSignal(SIGINT, $listener); + * ``` + * + * Any attempts to remove listeners that aren't registered will be ignored. + * + * @param int $signal + * @param callable $listener + * + * @return void + */ + public function removeSignal($signal, $listener); + + /** + * Run the event loop until there are no more tasks to perform. + * + * For many applications, this method is the only directly visible + * invocation on the event loop. + * As a rule of thumb, it is usally recommended to attach everything to the + * same loop instance and then run the loop once at the bottom end of the + * application. + * + * ```php + * $loop->run(); + * ``` + * + * This method will keep the loop running until there are no more tasks + * to perform. In other words: This method will block until the last + * timer, stream and/or signal has been removed. + * + * Likewise, it is imperative to ensure the application actually invokes + * this method once. Adding listeners to the loop and missing to actually + * run it will result in the application exiting without actually waiting + * for any of the attached listeners. + * + * This method MUST NOT be called while the loop is already running. + * This method MAY be called more than once after it has explicity been + * [`stop()`ped](#stop) or after it automatically stopped because it + * previously did no longer have anything to do. + * + * @return void + */ + public function run(); + + /** + * Instruct a running event loop to stop. + * + * This method is considered advanced usage and should be used with care. + * As a rule of thumb, it is usually recommended to let the loop stop + * only automatically when it no longer has anything to do. + * + * This method can be used to explicitly instruct the event loop to stop: + * + * ```php + * $loop->addTimer(3.0, function () use ($loop) { + * $loop->stop(); + * }); + * ``` + * + * Calling this method on a loop instance that is not currently running or + * on a loop instance that has already been stopped has no effect. + * + * @return void + */ + public function stop(); +} diff --git a/assets/php/vendor/react/event-loop/src/SignalsHandler.php b/assets/php/vendor/react/event-loop/src/SignalsHandler.php new file mode 100644 index 0000000..523e1ca --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/SignalsHandler.php @@ -0,0 +1,63 @@ +<?php + +namespace React\EventLoop; + +/** + * @internal + */ +final class SignalsHandler +{ + private $signals = array(); + + public function add($signal, $listener) + { + if (!isset($this->signals[$signal])) { + $this->signals[$signal] = array(); + } + + if (in_array($listener, $this->signals[$signal])) { + return; + } + + $this->signals[$signal][] = $listener; + } + + public function remove($signal, $listener) + { + if (!isset($this->signals[$signal])) { + return; + } + + $index = \array_search($listener, $this->signals[$signal], true); + unset($this->signals[$signal][$index]); + + if (isset($this->signals[$signal]) && \count($this->signals[$signal]) === 0) { + unset($this->signals[$signal]); + } + } + + public function call($signal) + { + if (!isset($this->signals[$signal])) { + return; + } + + foreach ($this->signals[$signal] as $listener) { + \call_user_func($listener, $signal); + } + } + + public function count($signal) + { + if (!isset($this->signals[$signal])) { + return 0; + } + + return \count($this->signals[$signal]); + } + + public function isEmpty() + { + return !$this->signals; + } +} diff --git a/assets/php/vendor/react/event-loop/src/StreamSelectLoop.php b/assets/php/vendor/react/event-loop/src/StreamSelectLoop.php new file mode 100644 index 0000000..e82e9e4 --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/StreamSelectLoop.php @@ -0,0 +1,275 @@ +<?php + +namespace React\EventLoop; + +use React\EventLoop\Signal\Pcntl; +use React\EventLoop\Tick\FutureTickQueue; +use React\EventLoop\Timer\Timer; +use React\EventLoop\Timer\Timers; + +/** + * A `stream_select()` based event loop. + * + * This uses the [`stream_select()`](http://php.net/manual/en/function.stream-select.php) + * function and is the only implementation which works out of the box with PHP. + * + * This event loop works out of the box on PHP 5.4 through PHP 7+ and HHVM. + * This means that no installation is required and this library works on all + * platforms and supported PHP versions. + * Accordingly, the [`Factory`](#factory) will use this event loop by default if + * you do not install any of the event loop extensions listed below. + * + * Under the hood, it does a simple `select` system call. + * This system call is limited to the maximum file descriptor number of + * `FD_SETSIZE` (platform dependent, commonly 1024) and scales with `O(m)` + * (`m` being the maximum file descriptor number passed). + * This means that you may run into issues when handling thousands of streams + * concurrently and you may want to look into using one of the alternative + * event loop implementations listed below in this case. + * If your use case is among the many common use cases that involve handling only + * dozens or a few hundred streams at once, then this event loop implementation + * performs really well. + * + * If you want to use signal handling (see also [`addSignal()`](#addsignal) below), + * this event loop implementation requires `ext-pcntl`. + * This extension is only available for Unix-like platforms and does not support + * Windows. + * It is commonly installed as part of many PHP distributions. + * If this extension is missing (or you're running on Windows), signal handling is + * not supported and throws a `BadMethodCallException` instead. + * + * This event loop is known to rely on wall-clock time to schedule future + * timers, because a monotonic time source is not available in PHP by default. + * While this does not affect many common use cases, this is an important + * distinction for programs that rely on a high time precision or on systems + * that are subject to discontinuous time adjustments (time jumps). + * This means that if you schedule a timer to trigger in 30s and then adjust + * your system time forward by 20s, the timer may trigger in 10s. + * See also [`addTimer()`](#addtimer) for more details. + * + * @link http://php.net/manual/en/function.stream-select.php + */ +final class StreamSelectLoop implements LoopInterface +{ + /** @internal */ + const MICROSECONDS_PER_SECOND = 1000000; + + private $futureTickQueue; + private $timers; + private $readStreams = array(); + private $readListeners = array(); + private $writeStreams = array(); + private $writeListeners = array(); + private $running; + private $pcntl = false; + private $signals; + + public function __construct() + { + $this->futureTickQueue = new FutureTickQueue(); + $this->timers = new Timers(); + $this->pcntl = extension_loaded('pcntl'); + $this->signals = new SignalsHandler(); + } + + public function addReadStream($stream, $listener) + { + $key = (int) $stream; + + if (!isset($this->readStreams[$key])) { + $this->readStreams[$key] = $stream; + $this->readListeners[$key] = $listener; + } + } + + public function addWriteStream($stream, $listener) + { + $key = (int) $stream; + + if (!isset($this->writeStreams[$key])) { + $this->writeStreams[$key] = $stream; + $this->writeListeners[$key] = $listener; + } + } + + public function removeReadStream($stream) + { + $key = (int) $stream; + + unset( + $this->readStreams[$key], + $this->readListeners[$key] + ); + } + + public function removeWriteStream($stream) + { + $key = (int) $stream; + + unset( + $this->writeStreams[$key], + $this->writeListeners[$key] + ); + } + + public function addTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, false); + + $this->timers->add($timer); + + return $timer; + } + + public function addPeriodicTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, true); + + $this->timers->add($timer); + + return $timer; + } + + public function cancelTimer(TimerInterface $timer) + { + $this->timers->cancel($timer); + } + + public function futureTick($listener) + { + $this->futureTickQueue->add($listener); + } + + public function addSignal($signal, $listener) + { + if ($this->pcntl === false) { + throw new \BadMethodCallException('Event loop feature "signals" isn\'t supported by the "StreamSelectLoop"'); + } + + $first = $this->signals->count($signal) === 0; + $this->signals->add($signal, $listener); + + if ($first) { + \pcntl_signal($signal, array($this->signals, 'call')); + } + } + + public function removeSignal($signal, $listener) + { + if (!$this->signals->count($signal)) { + return; + } + + $this->signals->remove($signal, $listener); + + if ($this->signals->count($signal) === 0) { + \pcntl_signal($signal, SIG_DFL); + } + } + + public function run() + { + $this->running = true; + + while ($this->running) { + $this->futureTickQueue->tick(); + + $this->timers->tick(); + + // Future-tick queue has pending callbacks ... + if (!$this->running || !$this->futureTickQueue->isEmpty()) { + $timeout = 0; + + // There is a pending timer, only block until it is due ... + } elseif ($scheduledAt = $this->timers->getFirst()) { + $timeout = $scheduledAt - $this->timers->getTime(); + if ($timeout < 0) { + $timeout = 0; + } else { + // Convert float seconds to int microseconds. + // Ensure we do not exceed maximum integer size, which may + // cause the loop to tick once every ~35min on 32bit systems. + $timeout *= self::MICROSECONDS_PER_SECOND; + $timeout = $timeout > PHP_INT_MAX ? PHP_INT_MAX : (int)$timeout; + } + + // The only possible event is stream or signal activity, so wait forever ... + } elseif ($this->readStreams || $this->writeStreams || !$this->signals->isEmpty()) { + $timeout = null; + + // There's nothing left to do ... + } else { + break; + } + + $this->waitForStreamActivity($timeout); + } + } + + public function stop() + { + $this->running = false; + } + + /** + * Wait/check for stream activity, or until the next timer is due. + * + * @param integer|null $timeout Activity timeout in microseconds, or null to wait forever. + */ + private function waitForStreamActivity($timeout) + { + $read = $this->readStreams; + $write = $this->writeStreams; + + $available = $this->streamSelect($read, $write, $timeout); + if ($this->pcntl) { + \pcntl_signal_dispatch(); + } + if (false === $available) { + // if a system call has been interrupted, + // we cannot rely on it's outcome + return; + } + + foreach ($read as $stream) { + $key = (int) $stream; + + if (isset($this->readListeners[$key])) { + call_user_func($this->readListeners[$key], $stream); + } + } + + foreach ($write as $stream) { + $key = (int) $stream; + + if (isset($this->writeListeners[$key])) { + call_user_func($this->writeListeners[$key], $stream); + } + } + } + + /** + * Emulate a stream_select() implementation that does not break when passed + * empty stream arrays. + * + * @param array &$read An array of read streams to select upon. + * @param array &$write An array of write streams to select upon. + * @param 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. + */ + private 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; + } +} diff --git a/assets/php/vendor/react/event-loop/src/Tick/FutureTickQueue.php b/assets/php/vendor/react/event-loop/src/Tick/FutureTickQueue.php new file mode 100644 index 0000000..c79afc5 --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/Tick/FutureTickQueue.php @@ -0,0 +1,60 @@ +<?php + +namespace React\EventLoop\Tick; + +use SplQueue; + +/** + * A tick queue implementation that can hold multiple callback functions + * + * This class should only be used internally, see LoopInterface instead. + * + * @see LoopInterface + * @internal + */ +final class FutureTickQueue +{ + private $queue; + + public function __construct() + { + $this->queue = new SplQueue(); + } + + /** + * Add a callback to be invoked on a future tick of the event loop. + * + * Callbacks are guaranteed to be executed in the order they are enqueued. + * + * @param callable $listener The callback to invoke. + */ + public function add($listener) + { + $this->queue->enqueue($listener); + } + + /** + * Flush the callback queue. + */ + public function tick() + { + // Only invoke as many callbacks as were on the queue when tick() was called. + $count = $this->queue->count(); + + while ($count--) { + call_user_func( + $this->queue->dequeue() + ); + } + } + + /** + * Check if the next tick queue is empty. + * + * @return boolean + */ + public function isEmpty() + { + return $this->queue->isEmpty(); + } +} diff --git a/assets/php/vendor/react/event-loop/src/Timer/Timer.php b/assets/php/vendor/react/event-loop/src/Timer/Timer.php new file mode 100644 index 0000000..da3602a --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/Timer/Timer.php @@ -0,0 +1,55 @@ +<?php + +namespace React\EventLoop\Timer; + +use React\EventLoop\TimerInterface; + +/** + * The actual connection implementation for TimerInterface + * + * This class should only be used internally, see TimerInterface instead. + * + * @see TimerInterface + * @internal + */ +final class Timer implements TimerInterface +{ + const MIN_INTERVAL = 0.000001; + + private $interval; + private $callback; + private $periodic; + + /** + * Constructor initializes the fields of the Timer + * + * @param float $interval The interval after which this timer will execute, in seconds + * @param callable $callback The callback that will be executed when this timer elapses + * @param bool $periodic Whether the time is periodic + */ + public function __construct($interval, $callback, $periodic = false) + { + if ($interval < self::MIN_INTERVAL) { + $interval = self::MIN_INTERVAL; + } + + $this->interval = (float) $interval; + $this->callback = $callback; + $this->periodic = (bool) $periodic; + } + + public function getInterval() + { + return $this->interval; + } + + public function getCallback() + { + return $this->callback; + } + + public function isPeriodic() + { + return $this->periodic; + } +} diff --git a/assets/php/vendor/react/event-loop/src/Timer/Timers.php b/assets/php/vendor/react/event-loop/src/Timer/Timers.php new file mode 100644 index 0000000..17bbdac --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/Timer/Timers.php @@ -0,0 +1,109 @@ +<?php + +namespace React\EventLoop\Timer; + +use React\EventLoop\TimerInterface; +use SplObjectStorage; +use SplPriorityQueue; + +/** + * A scheduler implementation that can hold multiple timer instances + * + * This class should only be used internally, see TimerInterface instead. + * + * @see TimerInterface + * @internal + */ +final class Timers +{ + private $time; + private $timers; + 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); + } + } + } +} diff --git a/assets/php/vendor/react/event-loop/src/TimerInterface.php b/assets/php/vendor/react/event-loop/src/TimerInterface.php new file mode 100644 index 0000000..cdcf773 --- /dev/null +++ b/assets/php/vendor/react/event-loop/src/TimerInterface.php @@ -0,0 +1,27 @@ +<?php + +namespace React\EventLoop; + +interface TimerInterface +{ + /** + * Get the interval after which this timer will execute, in seconds + * + * @return float + */ + public function getInterval(); + + /** + * Get the callback that will be executed when this timer elapses + * + * @return callable + */ + public function getCallback(); + + /** + * Determine whether the time is periodic + * + * @return bool + */ + public function isPeriodic(); +} diff --git a/assets/php/vendor/react/event-loop/tests/AbstractLoopTest.php b/assets/php/vendor/react/event-loop/tests/AbstractLoopTest.php new file mode 100644 index 0000000..dbfc91e --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/AbstractLoopTest.php @@ -0,0 +1,621 @@ +<?php + +namespace React\Tests\EventLoop; + +abstract class AbstractLoopTest extends TestCase +{ + /** + * @var \React\EventLoop\LoopInterface + */ + protected $loop; + + private $tickTimeout; + + const PHP_DEFAULT_CHUNK_SIZE = 8192; + + public function setUp() + { + // It's a timeout, don't set it too low. Travis and other CI systems are slow. + $this->tickTimeout = 0.02; + $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->tickLoop($this->loop); + + fwrite($output, "bar\n"); + $this->tickLoop($this->loop); + } + + 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->tickLoop($this->loop); + + fwrite($output, "bar\n"); + $this->tickLoop($this->loop); + } + + public function testAddReadStreamReceivesDataFromStreamReference() + { + $this->received = ''; + $this->subAddReadStreamReceivesDataFromStreamReference(); + $this->assertEquals('', $this->received); + + $this->assertRunFasterThan($this->tickTimeout * 2); + $this->assertEquals('[hello]X', $this->received); + } + + /** + * Helper for above test. This happens in another helper method to verify + * the loop keeps track of assigned stream resources (refcount). + */ + private function subAddReadStreamReceivesDataFromStreamReference() + { + list ($input, $output) = $this->createSocketPair(); + + fwrite($input, 'hello'); + fclose($input); + + $loop = $this->loop; + $received =& $this->received; + $loop->addReadStream($output, function ($output) use ($loop, &$received) { + $chunk = fread($output, 1024); + if ($chunk === '') { + $received .= 'X'; + $loop->removeReadStream($output); + fclose($output); + } else { + $received .= '[' . $chunk . ']'; + } + }); + } + + public function testAddWriteStream() + { + list ($input) = $this->createSocketPair(); + + $this->loop->addWriteStream($input, $this->expectCallableExactly(2)); + $this->tickLoop($this->loop); + $this->tickLoop($this->loop); + } + + public function testAddWriteStreamIgnoresSecondCallable() + { + list ($input) = $this->createSocketPair(); + + $this->loop->addWriteStream($input, $this->expectCallableExactly(2)); + $this->loop->addWriteStream($input, $this->expectCallableNever()); + $this->tickLoop($this->loop); + $this->tickLoop($this->loop); + } + + public function testRemoveReadStreamInstantly() + { + list ($input, $output) = $this->createSocketPair(); + + $this->loop->addReadStream($input, $this->expectCallableNever()); + $this->loop->removeReadStream($input); + + fwrite($output, "bar\n"); + $this->tickLoop($this->loop); + } + + public function testRemoveReadStreamAfterReading() + { + list ($input, $output) = $this->createSocketPair(); + + $this->loop->addReadStream($input, $this->expectCallableOnce()); + + fwrite($output, "foo\n"); + $this->tickLoop($this->loop); + + $this->loop->removeReadStream($input); + + fwrite($output, "bar\n"); + $this->tickLoop($this->loop); + } + + public function testRemoveWriteStreamInstantly() + { + list ($input) = $this->createSocketPair(); + + $this->loop->addWriteStream($input, $this->expectCallableNever()); + $this->loop->removeWriteStream($input); + $this->tickLoop($this->loop); + } + + public function testRemoveWriteStreamAfterWriting() + { + list ($input) = $this->createSocketPair(); + + $this->loop->addWriteStream($input, $this->expectCallableOnce()); + $this->tickLoop($this->loop); + + $this->loop->removeWriteStream($input); + $this->tickLoop($this->loop); + } + + 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->tickLoop($this->loop); + } + + 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->tickLoop($this->loop); + } + + public function testRemoveReadAndWriteStreamFromLoopOnceResourceClosesEndsLoop() + { + list($stream, $other) = $this->createSocketPair(); + stream_set_blocking($stream, false); + stream_set_blocking($other, false); + + // dummy writable handler + $this->loop->addWriteStream($stream, function () { }); + + // remove stream when the stream is readable (closes) + $loop = $this->loop; + $loop->addReadStream($stream, function ($stream) use ($loop) { + $loop->removeReadStream($stream); + $loop->removeWriteStream($stream); + fclose($stream); + }); + + // close other side + fclose($other); + + $this->assertRunFasterThan($this->tickTimeout); + } + + public function testRemoveReadAndWriteStreamFromLoopOnceResourceClosesOnEndOfFileEndsLoop() + { + list($stream, $other) = $this->createSocketPair(); + stream_set_blocking($stream, false); + stream_set_blocking($other, false); + + // dummy writable handler + $this->loop->addWriteStream($stream, function () { }); + + // remove stream when the stream is readable (closes) + $loop = $this->loop; + $loop->addReadStream($stream, function ($stream) use ($loop) { + $data = fread($stream, 1024); + if ($data !== '') { + return; + } + + $loop->removeReadStream($stream); + $loop->removeWriteStream($stream); + fclose($stream); + }); + + // send data and close stream + fwrite($other, str_repeat('.', static::PHP_DEFAULT_CHUNK_SIZE)); + $this->loop->addTimer(0.01, function () use ($other) { + fclose($other); + }); + + $this->assertRunFasterThan(0.1); + } + + public function testRemoveReadAndWriteStreamFromLoopWithClosingResourceEndsLoop() + { + // get only one part of the pair to ensure the other side will close immediately + list($stream) = $this->createSocketPair(); + stream_set_blocking($stream, false); + + // dummy writable handler + $this->loop->addWriteStream($stream, function () { }); + + // remove stream when the stream is readable (closes) + $loop = $this->loop; + $loop->addReadStream($stream, function ($stream) use ($loop) { + $loop->removeReadStream($stream); + $loop->removeWriteStream($stream); + fclose($stream); + }); + + $this->assertRunFasterThan($this->tickTimeout); + } + + 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->assertTrue(true); + } + + /** @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->removeReadStream($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() + { + $that = $this; + $this->loop->addTimer( + 1, + function () use ($that) { + $that->fail('Timer was executed.'); + } + ); + + $loop = $this->loop; + $this->loop->futureTick( + function () use ($loop) { + $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 + $that = $this; + $loop->addReadStream($input2, function () use (& $called, $that) { + if ($called) { + $that->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 testFutureTickEventGeneratedByFutureTick() + { + $loop = $this->loop; + $this->loop->futureTick( + function () use ($loop) { + $loop->futureTick( + function () { + echo 'future-tick' . PHP_EOL; + } + ); + } + ); + + $this->expectOutputString('future-tick' . PHP_EOL); + + $this->loop->run(); + } + + public function testFutureTick() + { + $called = false; + + $callback = function () use (&$called) { + $called = true; + }; + + $this->loop->futureTick($callback); + + $this->assertFalse($called); + + $this->tickLoop($this->loop); + + $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->tickLoop($this->loop); + } + + public function testRecursiveFutureTick() + { + list ($stream) = $this->createSocketPair(); + + $loop = $this->loop; + $this->loop->addWriteStream( + $stream, + function () use ($stream, $loop) { + echo 'stream' . PHP_EOL; + $loop->removeWriteStream($stream); + } + ); + + $this->loop->futureTick( + function () use ($loop) { + echo 'future-tick-1' . PHP_EOL; + $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(); + + $loop = $this->loop; + $this->loop->addWriteStream( + $stream, + function () use ($stream, $loop) { + $loop->removeWriteStream($stream); + $loop->futureTick( + function () { + echo 'future-tick' . PHP_EOL; + } + ); + } + ); + + $this->expectOutputString('future-tick' . PHP_EOL); + + $this->loop->run(); + } + + public function testFutureTickEventGeneratedByTimer() + { + $loop = $this->loop; + $this->loop->addTimer( + 0.001, + function () use ($loop) { + $loop->futureTick( + function () { + echo 'future-tick' . PHP_EOL; + } + ); + } + ); + + $this->expectOutputString('future-tick' . PHP_EOL); + + $this->loop->run(); + } + + public function testRemoveSignalNotRegisteredIsNoOp() + { + $this->loop->removeSignal(SIGINT, function () { }); + $this->assertTrue(true); + } + + public function testSignal() + { + if (!function_exists('posix_kill') || !function_exists('posix_getpid')) { + $this->markTestSkipped('Signal test skipped because functions "posix_kill" and "posix_getpid" are missing.'); + } + + $called = false; + $calledShouldNot = true; + + $timer = $this->loop->addPeriodicTimer(1, function () {}); + + $this->loop->addSignal(SIGUSR2, $func2 = function () use (&$calledShouldNot) { + $calledShouldNot = false; + }); + + $loop = $this->loop; + $this->loop->addSignal(SIGUSR1, $func1 = function () use (&$func1, &$func2, &$called, $timer, $loop) { + $called = true; + $loop->removeSignal(SIGUSR1, $func1); + $loop->removeSignal(SIGUSR2, $func2); + $loop->cancelTimer($timer); + }); + + $this->loop->futureTick(function () { + posix_kill(posix_getpid(), SIGUSR1); + }); + + $this->loop->run(); + + $this->assertTrue($called); + $this->assertTrue($calledShouldNot); + } + + public function testSignalMultipleUsagesForTheSameListener() + { + $funcCallCount = 0; + $func = function () use (&$funcCallCount) { + $funcCallCount++; + }; + $this->loop->addTimer(1, function () {}); + + $this->loop->addSignal(SIGUSR1, $func); + $this->loop->addSignal(SIGUSR1, $func); + + $this->loop->addTimer(0.4, function () { + posix_kill(posix_getpid(), SIGUSR1); + }); + $loop = $this->loop; + $this->loop->addTimer(0.9, function () use (&$func, $loop) { + $loop->removeSignal(SIGUSR1, $func); + }); + + $this->loop->run(); + + $this->assertSame(1, $funcCallCount); + } + + public function testSignalsKeepTheLoopRunning() + { + $loop = $this->loop; + $function = function () {}; + $this->loop->addSignal(SIGUSR1, $function); + $this->loop->addTimer(1.5, function () use ($function, $loop) { + $loop->removeSignal(SIGUSR1, $function); + $loop->stop(); + }); + + $this->assertRunSlowerThan(1.5); + } + + public function testSignalsKeepTheLoopRunningAndRemovingItStopsTheLoop() + { + $loop = $this->loop; + $function = function () {}; + $this->loop->addSignal(SIGUSR1, $function); + $this->loop->addTimer(1.5, function () use ($function, $loop) { + $loop->removeSignal(SIGUSR1, $function); + }); + + $this->assertRunFasterThan(1.6); + } + + public function testTimerIntervalCanBeFarInFuture() + { + // get only one part of the pair to ensure the other side will close immediately + list($stream) = $this->createSocketPair(); + + // start a timer very far in the future + $timer = $this->loop->addTimer(PHP_INT_MAX, function () { }); + + // remove stream and timer when the stream is readable (closes) + $loop = $this->loop; + $this->loop->addReadStream($stream, function ($stream) use ($timer, $loop) { + $loop->removeReadStream($stream); + $loop->cancelTimer($timer); + }); + + $this->assertRunFasterThan($this->tickTimeout); + } + + private function assertRunSlowerThan($minInterval) + { + $start = microtime(true); + + $this->loop->run(); + + $end = microtime(true); + $interval = $end - $start; + + $this->assertLessThan($interval, $minInterval); + } + + private function assertRunFasterThan($maxInterval) + { + $start = microtime(true); + + $this->loop->run(); + + $end = microtime(true); + $interval = $end - $start; + + $this->assertLessThan($maxInterval, $interval); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/CallableStub.php b/assets/php/vendor/react/event-loop/tests/CallableStub.php new file mode 100644 index 0000000..913d403 --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/CallableStub.php @@ -0,0 +1,10 @@ +<?php + +namespace React\Tests\EventLoop; + +class CallableStub +{ + public function __invoke() + { + } +} diff --git a/assets/php/vendor/react/event-loop/tests/ExtEvLoopTest.php b/assets/php/vendor/react/event-loop/tests/ExtEvLoopTest.php new file mode 100644 index 0000000..ab41c9f --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/ExtEvLoopTest.php @@ -0,0 +1,17 @@ +<?php + +namespace React\Tests\EventLoop; + +use React\EventLoop\ExtEvLoop; + +class ExtEvLoopTest extends AbstractLoopTest +{ + public function createLoop() + { + if (!class_exists('EvLoop')) { + $this->markTestSkipped('ExtEvLoop tests skipped because ext-ev extension is not installed.'); + } + + return new ExtEvLoop(); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/ExtEventLoopTest.php b/assets/php/vendor/react/event-loop/tests/ExtEventLoopTest.php new file mode 100644 index 0000000..2f88d18 --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/ExtEventLoopTest.php @@ -0,0 +1,84 @@ +<?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.'); + } + + return new ExtEventLoop(); + } + + 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->tickLoop($this->loop); + + fwrite($input, "bar\n"); + $this->tickLoop($this->loop); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/ExtLibevLoopTest.php b/assets/php/vendor/react/event-loop/tests/ExtLibevLoopTest.php new file mode 100644 index 0000000..19a5e87 --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/ExtLibevLoopTest.php @@ -0,0 +1,22 @@ +<?php + +namespace React\Tests\EventLoop; + +use React\EventLoop\ExtLibevLoop; + +class ExtLibevLoopTest extends AbstractLoopTest +{ + public function createLoop() + { + if (!class_exists('libev\EventLoop')) { + $this->markTestSkipped('libev tests skipped because ext-libev is not installed.'); + } + + return new ExtLibevLoop(); + } + + public function testLibEvConstructor() + { + $loop = new ExtLibevLoop(); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/ExtLibeventLoopTest.php b/assets/php/vendor/react/event-loop/tests/ExtLibeventLoopTest.php new file mode 100644 index 0000000..8497065 --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/ExtLibeventLoopTest.php @@ -0,0 +1,58 @@ +<?php + +namespace React\Tests\EventLoop; + +use React\EventLoop\ExtLibeventLoop; + +class ExtLibeventLoopTest 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 ExtLibeventLoop(); + } + + 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); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/SignalsHandlerTest.php b/assets/php/vendor/react/event-loop/tests/SignalsHandlerTest.php new file mode 100644 index 0000000..f8b7df3 --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/SignalsHandlerTest.php @@ -0,0 +1,55 @@ +<?php + +namespace React\Tests\EventLoop; + +use React\EventLoop\SignalsHandler; + +final class SignalsHandlerTest extends TestCase +{ + public function testEmittedEventsAndCallHandling() + { + $callCount = 0; + $func = function () use (&$callCount) { + $callCount++; + }; + $signals = new SignalsHandler(); + + $this->assertSame(0, $callCount); + + $signals->add(SIGUSR1, $func); + $this->assertSame(0, $callCount); + + $signals->add(SIGUSR1, $func); + $this->assertSame(0, $callCount); + + $signals->add(SIGUSR1, $func); + $this->assertSame(0, $callCount); + + $signals->call(SIGUSR1); + $this->assertSame(1, $callCount); + + $signals->add(SIGUSR2, $func); + $this->assertSame(1, $callCount); + + $signals->add(SIGUSR2, $func); + $this->assertSame(1, $callCount); + + $signals->call(SIGUSR2); + $this->assertSame(2, $callCount); + + $signals->remove(SIGUSR2, $func); + $this->assertSame(2, $callCount); + + $signals->remove(SIGUSR2, $func); + $this->assertSame(2, $callCount); + + $signals->call(SIGUSR2); + $this->assertSame(2, $callCount); + + $signals->remove(SIGUSR1, $func); + $this->assertSame(2, $callCount); + + $signals->call(SIGUSR1); + $this->assertSame(2, $callCount); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/StreamSelectLoopTest.php b/assets/php/vendor/react/event-loop/tests/StreamSelectLoopTest.php new file mode 100644 index 0000000..bd19e1c --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/StreamSelectLoopTest.php @@ -0,0 +1,148 @@ +<?php + +namespace React\Tests\EventLoop; + +use React\EventLoop\LoopInterface; +use React\EventLoop\StreamSelectLoop; + +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 array( + array('SIGUSR1'), + array('SIGHUP'), + array('SIGTERM'), + ); + } + + /** + * 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 every 10ms for 0.1s + $check = $this->loop->addPeriodicTimer(0.01, function() { + pcntl_signal_dispatch(); + }); + $loop = $this->loop; + $loop->addTimer(0.1, function () use ($check, $loop) { + $loop->cancelTimer($check); + }); + + $handled = false; + $this->assertTrue(pcntl_signal(constant($signal), function () use (&$handled) { + $handled = true; + })); + + // spawn external process to send signal to current process id + $this->forkSendSignal($signal); + + $this->loop->run(); + $this->assertTrue($handled); + } + + /** + * 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 + $loop = $this->loop; + list($writeStream, $readStream) = $this->createSocketPair(); + $loop->addReadStream($readStream, function ($stream) use ($loop) { + /** @var $loop LoopInterface */ + $read = fgets($stream); + if ($read === "end loop\n") { + $loop->stop(); + } + }); + $this->loop->addTimer(0.1, function() use ($writeStream) { + fwrite($writeStream, "end loop\n"); + }); + + $handled = false; + $this->assertTrue(pcntl_signal(constant($signal), function () use (&$handled) { + $handled = true; + })); + + // spawn external process to send signal to current process id + $this->forkSendSignal($signal); + + $this->loop->run(); + + $this->assertTrue($handled); + } + + /** + * 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(); + } + } +} diff --git a/assets/php/vendor/react/event-loop/tests/TestCase.php b/assets/php/vendor/react/event-loop/tests/TestCase.php new file mode 100644 index 0000000..dbdd54c --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/TestCase.php @@ -0,0 +1,53 @@ +<?php + +namespace React\Tests\EventLoop; + +use PHPUnit\Framework\TestCase as BaseTestCase; +use React\EventLoop\LoopInterface; + +class TestCase extends BaseTestCase +{ + 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(); + } + + protected function tickLoop(LoopInterface $loop) + { + $loop->futureTick(function () use ($loop) { + $loop->stop(); + }); + + $loop->run(); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/Timer/AbstractTimerTest.php b/assets/php/vendor/react/event-loop/tests/Timer/AbstractTimerTest.php new file mode 100644 index 0000000..294e683 --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/Timer/AbstractTimerTest.php @@ -0,0 +1,122 @@ +<?php + +namespace React\Tests\EventLoop\Timer; + +use React\EventLoop\LoopInterface; +use React\Tests\EventLoop\TestCase; + +abstract class AbstractTimerTest extends TestCase +{ + /** + * @return LoopInterface + */ + abstract public function createLoop(); + + public function testAddTimerReturnsNonPeriodicTimerInstance() + { + $loop = $this->createLoop(); + + $timer = $loop->addTimer(0.001, $this->expectCallableNever()); + + $this->assertInstanceOf('React\EventLoop\TimerInterface', $timer); + $this->assertFalse($timer->isPeriodic()); + } + + public function testAddTimerWillBeInvokedOnceAndBlocksLoopWhenRunning() + { + $loop = $this->createLoop(); + + $loop->addTimer(0.001, $this->expectCallableOnce()); + + $start = microtime(true); + $loop->run(); + $end = microtime(true); + + // make no strict assumptions about actual time interval. + // must be at least 0.001s (1ms) and should not take longer than 0.1s + $this->assertGreaterThanOrEqual(0.001, $end - $start); + $this->assertLessThan(0.1, $end - $start); + } + + public function testAddPeriodicTimerReturnsPeriodicTimerInstance() + { + $loop = $this->createLoop(); + + $periodic = $loop->addPeriodicTimer(0.1, $this->expectCallableNever()); + + $this->assertInstanceOf('React\EventLoop\TimerInterface', $periodic); + $this->assertTrue($periodic->isPeriodic()); + } + + public function testAddPeriodicTimerWillBeInvokedUntilItIsCancelled() + { + $loop = $this->createLoop(); + + $periodic = $loop->addPeriodicTimer(0.1, $this->expectCallableExactly(3)); + + // make no strict assumptions about actual time interval. + // leave some room to ensure this ticks exactly 3 times. + $loop->addTimer(0.399, function () use ($loop, $periodic) { + $loop->cancelTimer($periodic); + }); + + $loop->run(); + } + + public function testAddPeriodicTimerWillBeInvokedWithMaximumAccuracyUntilItIsCancelled() + { + $loop = $this->createLoop(); + + $i = 0; + $periodic = $loop->addPeriodicTimer(0.001, function () use (&$i) { + ++$i; + }); + + $loop->addTimer(0.02, function () use ($loop, $periodic) { + $loop->cancelTimer($periodic); + }); + + $loop->run(); + + // make no strict assumptions about number of invocations. + // we know it must be no more than 20 times and should at least be + // invoked twice for really slow loops + $this->assertLessThanOrEqual(20, $i); + $this->assertGreaterThan(2, $i); + } + + public function testAddPeriodicTimerCancelsItself() + { + $loop = $this->createLoop(); + + $i = 0; + $loop->addPeriodicTimer(0.001, function ($timer) use (&$i, $loop) { + $i++; + + if ($i === 5) { + $loop->cancelTimer($timer); + } + }); + + $start = microtime(true); + $loop->run(); + $end = microtime(true); + + $this->assertEquals(5, $i); + + // make no strict assumptions about time interval. + // 5 invocations must take at least 0.005s (5ms) and should not take + // longer than 0.1s for slower loops. + $this->assertGreaterThanOrEqual(0.005, $end - $start); + $this->assertLessThan(0.1, $end - $start); + } + + public function testMinimumIntervalOneMicrosecond() + { + $loop = $this->createLoop(); + + $timer = $loop->addTimer(0, function () {}); + + $this->assertEquals(0.000001, $timer->getInterval()); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php b/assets/php/vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php new file mode 100644 index 0000000..bfa9186 --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php @@ -0,0 +1,17 @@ +<?php + +namespace React\Tests\EventLoop\Timer; + +use React\EventLoop\ExtEvLoop; + +class ExtEvTimerTest extends AbstractTimerTest +{ + public function createLoop() + { + if (!class_exists('EvLoop')) { + $this->markTestSkipped('ExtEvLoop tests skipped because ext-ev extension is not installed.'); + } + + return new ExtEvLoop(); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php b/assets/php/vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php new file mode 100644 index 0000000..a7a6d00 --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php @@ -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(); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php b/assets/php/vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php new file mode 100644 index 0000000..65e82be --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php @@ -0,0 +1,17 @@ +<?php + +namespace React\Tests\EventLoop\Timer; + +use React\EventLoop\ExtLibevLoop; + +class ExtLibevTimerTest extends AbstractTimerTest +{ + public function createLoop() + { + if (!class_exists('libev\EventLoop')) { + $this->markTestSkipped('libev tests skipped because ext-libev is not installed.'); + } + + return new ExtLibevLoop(); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php b/assets/php/vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php new file mode 100644 index 0000000..9089b9a --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php @@ -0,0 +1,17 @@ +<?php + +namespace React\Tests\EventLoop\Timer; + +use React\EventLoop\ExtLibeventLoop; + +class ExtLibeventTimerTest extends AbstractTimerTest +{ + public function createLoop() + { + if (!function_exists('event_base_new')) { + $this->markTestSkipped('libevent tests skipped because ext-libevent is not installed.'); + } + + return new ExtLibeventLoop(); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php b/assets/php/vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php new file mode 100644 index 0000000..cfe1d7d --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php @@ -0,0 +1,13 @@ +<?php + +namespace React\Tests\EventLoop\Timer; + +use React\EventLoop\StreamSelectLoop; + +class StreamSelectTimerTest extends AbstractTimerTest +{ + public function createLoop() + { + return new StreamSelectLoop(); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/Timer/TimersTest.php b/assets/php/vendor/react/event-loop/tests/Timer/TimersTest.php new file mode 100644 index 0000000..b279478 --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/Timer/TimersTest.php @@ -0,0 +1,27 @@ +<?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() + { + $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(0.5, function () { + $this->fail("Timer shouldn't be called"); + })); + + $timers->tick(); + + $this->assertTrue(true); + } +} diff --git a/assets/php/vendor/react/event-loop/tests/bootstrap.php b/assets/php/vendor/react/event-loop/tests/bootstrap.php new file mode 100644 index 0000000..ea7dd4c --- /dev/null +++ b/assets/php/vendor/react/event-loop/tests/bootstrap.php @@ -0,0 +1,15 @@ +<?php + +$loader = @include __DIR__ . '/../vendor/autoload.php'; +if (!$loader) { + $loader = require __DIR__ . '/../../../../vendor/autoload.php'; +} +$loader->addPsr4('React\\Tests\\EventLoop\\', __DIR__); + +if (!defined('SIGUSR1')) { + define('SIGUSR1', 1); +} + +if (!defined('SIGUSR2')) { + define('SIGUSR2', 2); +} diff --git a/assets/php/vendor/react/event-loop/travis-init.sh b/assets/php/vendor/react/event-loop/travis-init.sh new file mode 100755 index 0000000..29ce884 --- /dev/null +++ b/assets/php/vendor/react/event-loop/travis-init.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -e +set -o pipefail + +if [[ "$TRAVIS_PHP_VERSION" != "hhvm" && + "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]]; then + + # install 'event' and 'ev' PHP extension + if [[ "$TRAVIS_PHP_VERSION" != "5.3" ]]; then + echo "yes" | pecl install event + echo "yes" | pecl install ev + fi + + # install 'libevent' PHP extension (does not support php 7) + if [[ "$TRAVIS_PHP_VERSION" != "7.0" && + "$TRAVIS_PHP_VERSION" != "7.1" && + "$TRAVIS_PHP_VERSION" != "7.2" ]]; 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" && + "$TRAVIS_PHP_VERSION" != "7.2" ]]; 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 |