mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-05 02:39:46 +02:00
Added vendor directory to source control
This commit is contained in:
parent
1c3c7b5c26
commit
aac245d32f
25330 changed files with 3486213 additions and 69 deletions
1
vendor/react/event-loop
vendored
1
vendor/react/event-loop
vendored
|
@ -1 +0,0 @@
|
|||
Subproject commit be6dee480fc4692cec0504e65eb486e3be1aa6f2
|
426
vendor/react/event-loop/CHANGELOG.md
vendored
Normal file
426
vendor/react/event-loop/CHANGELOG.md
vendored
Normal file
|
@ -0,0 +1,426 @@
|
|||
# Changelog
|
||||
|
||||
## 1.2.0 (2021-07-11)
|
||||
|
||||
A major new feature release, see [**release announcement**](https://clue.engineering/2021/announcing-reactphp-default-loop).
|
||||
|
||||
* Feature: Introduce new concept of default loop with the new `Loop` class.
|
||||
(#226 by @WyriHaximus, #229, #231 and #232 by @clue)
|
||||
|
||||
The `Loop` class exists as a convenient global accessor for the event loop.
|
||||
It provides all methods that exist on the `LoopInterface` as static methods and
|
||||
will automatically execute the loop at the end of the program:
|
||||
|
||||
```php
|
||||
$timer = Loop::addPeriodicTimer(0.1, function () {
|
||||
echo 'Tick' . PHP_EOL;
|
||||
});
|
||||
|
||||
Loop::addTimer(1.0, function () use ($timer) {
|
||||
Loop::cancelTimer($timer);
|
||||
echo 'Done' . PHP_EOL;
|
||||
});
|
||||
```
|
||||
|
||||
The explicit loop instructions are still valid and may still be useful in some applications,
|
||||
especially for a transition period towards the more concise style.
|
||||
The `Loop::get()` method can be used to get the currently active event loop instance.
|
||||
|
||||
```php
|
||||
// deprecated
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
// new
|
||||
$loop = React\EventLoop\Loop::get();
|
||||
```
|
||||
|
||||
* Minor documentation improvements and mark legacy extensions as deprecated.
|
||||
(#234 by @SimonFrings, #214 by @WyriHaximus and #233 and #235 by @nhedger)
|
||||
|
||||
* Improve test suite, use GitHub actions for continuous integration (CI),
|
||||
update PHPUnit config and run tests on PHP 8.
|
||||
(#212 and #215 by @SimonFrings and #230 by @clue)
|
||||
|
||||
## 1.1.1 (2020-01-01)
|
||||
|
||||
* Fix: Fix reporting connection refused errors with `ExtUvLoop` on Linux and `StreamSelectLoop` on Windows.
|
||||
(#207 and #208 by @clue)
|
||||
|
||||
* Fix: Fix unsupported EventConfig and `SEGFAULT` on shutdown with `ExtEventLoop` on Windows.
|
||||
(#205 by @clue)
|
||||
|
||||
* Fix: Prevent interval overflow for timers very far in the future with `ExtUvLoop`.
|
||||
(#196 by @PabloKowalczyk)
|
||||
|
||||
* Fix: Check PCNTL functions for signal support instead of PCNTL extension with `StreamSelectLoop`.
|
||||
(#195 by @clue)
|
||||
|
||||
* Add `.gitattributes` to exclude dev files from exports.
|
||||
(#201 by @reedy)
|
||||
|
||||
* Improve test suite to fix testing `ExtUvLoop` on Travis,
|
||||
fix Travis CI builds, do not install `libuv` on legacy PHP setups,
|
||||
fix failing test cases due to inaccurate timers,
|
||||
run tests on Windows via Travis CI and
|
||||
run tests on PHP 7.4 and simplify test matrix and test setup.
|
||||
(#197 by @WyriHaximus and #202, #203, #204 and #209 by @clue)
|
||||
|
||||
## 1.1.0 (2019-02-07)
|
||||
|
||||
* New UV based event loop (ext-uv).
|
||||
(#112 by @WyriHaximus)
|
||||
|
||||
* Use high resolution timer on PHP 7.3+.
|
||||
(#182 by @clue)
|
||||
|
||||
* Improve PCNTL signals by using async signal dispatching if available.
|
||||
(#179 by @CharlotteDunois)
|
||||
|
||||
* Improve test suite and test suite set up.
|
||||
(#174 by @WyriHaximus, #181 by @clue)
|
||||
|
||||
* Fix PCNTL signals edge case.
|
||||
(#183 by @clue)
|
||||
|
||||
## 1.0.0 (2018-07-11)
|
||||
|
||||
* First stable LTS release, now following [SemVer](https://semver.org/).
|
||||
We'd like to emphasize that this component is production ready and battle-tested.
|
||||
We plan to support all long-term support (LTS) releases for at least 24 months,
|
||||
so you have a rock-solid foundation to build on top of.
|
||||
|
||||
> Contains no other changes, so it's actually fully compatible with the v0.5.3 release.
|
||||
|
||||
## 0.5.3 (2018-07-09)
|
||||
|
||||
* Improve performance by importing global functions.
|
||||
(#167 by @Ocramius)
|
||||
|
||||
* Improve test suite by simplifying test bootstrap by using dev autoloader.
|
||||
(#169 by @lcobucci)
|
||||
|
||||
* Minor internal changes to improved backward compatibility with PHP 5.3.
|
||||
(#166 by @Donatello-za)
|
||||
|
||||
## 0.5.2 (2018-04-24)
|
||||
|
||||
* Feature: Improve memory consumption and runtime performance for `StreamSelectLoop` timers.
|
||||
(#164 by @clue)
|
||||
|
||||
* Improve test suite by removing I/O dependency at `StreamSelectLoopTest` to fix Mac OS X tests.
|
||||
(#161 by @nawarian)
|
||||
|
||||
## 0.5.1 (2018-04-09)
|
||||
|
||||
* Feature: New `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
|
21
vendor/react/event-loop/LICENSE
vendored
Normal file
21
vendor/react/event-loop/LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2012 Christian Lück, Cees-Jan Kiewiet, Jan Sorgalla, Chris Boden, Igor Wiedler
|
||||
|
||||
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.
|
919
vendor/react/event-loop/README.md
vendored
Normal file
919
vendor/react/event-loop/README.md
vendored
Normal file
|
@ -0,0 +1,919 @@
|
|||
# EventLoop Component
|
||||
|
||||
[](https://github.com/reactphp/event-loop/actions)
|
||||
|
||||
[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)
|
||||
* [Loop](#loop)
|
||||
* [Loop methods](#loop-methods)
|
||||
* [Loop autorun](#loop-autorun)
|
||||
* [get()](#get)
|
||||
* [~~Factory~~](#factory)
|
||||
* [~~create()~~](#create)
|
||||
* [Loop implementations](#loop-implementations)
|
||||
* [StreamSelectLoop](#streamselectloop)
|
||||
* [ExtEventLoop](#exteventloop)
|
||||
* [ExtEvLoop](#extevloop)
|
||||
* [ExtUvLoop](#extuvloop)
|
||||
* [~~ExtLibeventLoop~~](#extlibeventloop)
|
||||
* [~~ExtLibevLoop~~](#extlibevloop)
|
||||
* [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
|
||||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$server = stream_socket_server('tcp://127.0.0.1:8080');
|
||||
stream_set_blocking($server, false);
|
||||
|
||||
Loop::addReadStream($server, function ($server) {
|
||||
$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) {
|
||||
$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";
|
||||
});
|
||||
```
|
||||
|
||||
See also the [examples](examples).
|
||||
|
||||
## Usage
|
||||
|
||||
As of `v1.2.0`, typical applications would use the [`Loop` object](#loop)
|
||||
to use the currently active event loop like this:
|
||||
|
||||
```php
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
$timer = Loop::addPeriodicTimer(0.1, function () {
|
||||
echo 'Tick' . PHP_EOL;
|
||||
});
|
||||
|
||||
Loop::addTimer(1.0, function () use ($timer) {
|
||||
Loop::cancelTimer($timer);
|
||||
echo 'Done' . PHP_EOL;
|
||||
});
|
||||
```
|
||||
|
||||
As an alternative, you can also explicitly create an event loop instance at the
|
||||
beginning, reuse it throughout your program and finally run it at the end of the
|
||||
program like this:
|
||||
|
||||
```php
|
||||
$loop = React\EventLoop\Loop::get(); // or deprecated 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();
|
||||
```
|
||||
|
||||
While the former is more concise, the latter is more explicit.
|
||||
In both cases, the program would perform the exact same steps.
|
||||
|
||||
1. The event loop instance is created at the beginning of the program. This is
|
||||
implicitly done the first time you call the [`Loop` class](#loop) or
|
||||
explicitly when using the deprecated [`Factory::create() method`](#create)
|
||||
(or manually instantiating any of the [loop implementations](#loop-implementations)).
|
||||
2. The event loop is used directly or passed as an instance to library and
|
||||
application code. In this example, a periodic timer is registered with the
|
||||
event loop which simply outputs `Tick` every fraction of a second until another
|
||||
timer stops the periodic timer after a second.
|
||||
3. The event loop is run at the end of the program. This is automatically done
|
||||
when using [`Loop` class](#loop) or explicitly with a single [`run()`](#run)
|
||||
call at the end of the program.
|
||||
|
||||
As of `v1.2.0`, we highly recommend using the [`Loop` class](#loop).
|
||||
The explicit loop instructions are still valid and may still be useful in some
|
||||
applications, especially for a transition period towards the more concise style.
|
||||
|
||||
### Loop
|
||||
|
||||
The `Loop` class exists as a convenient global accessor for the event loop.
|
||||
|
||||
#### Loop methods
|
||||
|
||||
The `Loop` class provides all methods that exist on the [`LoopInterface`](#loopinterface)
|
||||
as static methods:
|
||||
|
||||
* [run()](#run)
|
||||
* [stop()](#stop)
|
||||
* [addTimer()](#addtimer)
|
||||
* [addPeriodicTimer()](#addperiodictimer)
|
||||
* [cancelTimer()](#canceltimer)
|
||||
* [futureTick()](#futuretick)
|
||||
* [addSignal()](#addsignal)
|
||||
* [removeSignal()](#removesignal)
|
||||
* [addReadStream()](#addreadstream)
|
||||
* [addWriteStream()](#addwritestream)
|
||||
* [removeReadStream()](#removereadstream)
|
||||
* [removeWriteStream()](#removewritestream)
|
||||
|
||||
If you're working with the event loop in your application code, it's often
|
||||
easiest to directly interface with the static methods defined on the `Loop` class
|
||||
like this:
|
||||
|
||||
```php
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
$timer = Loop::addPeriodicTimer(0.1, function () {
|
||||
echo 'Tick' . PHP_EOL;
|
||||
});
|
||||
|
||||
Loop::addTimer(1.0, function () use ($timer) {
|
||||
Loop::cancelTimer($timer);
|
||||
echo 'Done' . PHP_EOL;
|
||||
});
|
||||
```
|
||||
|
||||
On the other hand, if you're familiar with object-oriented programming (OOP) and
|
||||
dependency injection (DI), you may want to inject an event loop instance and
|
||||
invoke instance methods on the `LoopInterface` like this:
|
||||
|
||||
```php
|
||||
use React\EventLoop\Loop;
|
||||
use React\EventLoop\LoopInterface;
|
||||
|
||||
class Greeter
|
||||
{
|
||||
private $loop;
|
||||
|
||||
public function __construct(LoopInterface $loop)
|
||||
{
|
||||
$this->loop = $loop;
|
||||
}
|
||||
|
||||
public function greet(string $name)
|
||||
{
|
||||
$this->loop->addTimer(1.0, function () use ($name) {
|
||||
echo 'Hello ' . $name . '!' . PHP_EOL;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$greeter = new Greeter(Loop::get());
|
||||
$greeter->greet('Alice');
|
||||
$greeter->greet('Bob');
|
||||
```
|
||||
|
||||
Each static method call will be forwarded as-is to the underlying event loop
|
||||
instance by using the [`Loop::get()`](#get) call internally.
|
||||
See [`LoopInterface`](#loopinterface) for more details about available methods.
|
||||
|
||||
#### Loop autorun
|
||||
|
||||
When using the `Loop` class, it will automatically execute the loop at the end of
|
||||
the program. This means the following example will schedule a timer and will
|
||||
automatically execute the program until the timer event fires:
|
||||
|
||||
```php
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
Loop::addTimer(1.0, function () {
|
||||
echo 'Hello' . PHP_EOL;
|
||||
});
|
||||
```
|
||||
|
||||
As of `v1.2.0`, we highly recommend using the `Loop` class this way and omitting any
|
||||
explicit [`run()`](#run) calls. For BC reasons, the explicit [`run()`](#run)
|
||||
method is still valid and may still be useful in some applications, especially
|
||||
for a transition period towards the more concise style.
|
||||
|
||||
If you don't want the `Loop` to run automatically, you can either explicitly
|
||||
[`run()`](#run) or [`stop()`](#stop) it. This can be useful if you're using
|
||||
a global exception handler like this:
|
||||
|
||||
```php
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
Loop::addTimer(10.0, function () {
|
||||
echo 'Never happens';
|
||||
});
|
||||
|
||||
set_exception_handler(function (Throwable $e) {
|
||||
echo 'Error: ' . $e->getMessage() . PHP_EOL;
|
||||
Loop::stop();
|
||||
});
|
||||
|
||||
throw new RuntimeException('Demo');
|
||||
```
|
||||
|
||||
#### get()
|
||||
|
||||
The `get(): LoopInterface` method can be used to
|
||||
get the currently active event loop instance.
|
||||
|
||||
This method will always return the same event loop instance throughout the
|
||||
lifetime of your application.
|
||||
|
||||
```php
|
||||
use React\EventLoop\Loop;
|
||||
use React\EventLoop\LoopInterface;
|
||||
|
||||
$loop = Loop::get();
|
||||
|
||||
assert($loop instanceof LoopInterface);
|
||||
assert($loop === Loop::get());
|
||||
```
|
||||
|
||||
This is particularly useful if you're using object-oriented programming (OOP)
|
||||
and dependency injection (DI). In this case, you may want to inject an event
|
||||
loop instance and invoke instance methods on the `LoopInterface` like this:
|
||||
|
||||
```php
|
||||
use React\EventLoop\Loop;
|
||||
use React\EventLoop\LoopInterface;
|
||||
|
||||
class Greeter
|
||||
{
|
||||
private $loop;
|
||||
|
||||
public function __construct(LoopInterface $loop)
|
||||
{
|
||||
$this->loop = $loop;
|
||||
}
|
||||
|
||||
public function greet(string $name)
|
||||
{
|
||||
$this->loop->addTimer(1.0, function () use ($name) {
|
||||
echo 'Hello ' . $name . '!' . PHP_EOL;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$greeter = new Greeter(Loop::get());
|
||||
$greeter->greet('Alice');
|
||||
$greeter->greet('Bob');
|
||||
```
|
||||
|
||||
See [`LoopInterface`](#loopinterface) for more details about available methods.
|
||||
|
||||
### ~~Factory~~
|
||||
|
||||
> Deprecated since v1.2.0, see [`Loop` class](#loop) instead.
|
||||
|
||||
The deprecated `Factory` class exists as a convenient way to pick the best available
|
||||
[event loop implementation](#loop-implementations).
|
||||
|
||||
#### ~~create()~~
|
||||
|
||||
> Deprecated since v1.2.0, see [`Loop::get()`](#get) instead.
|
||||
|
||||
The deprecated `create(): LoopInterface` method can be used to
|
||||
create a new event loop instance:
|
||||
|
||||
```php
|
||||
// deprecated
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
// new
|
||||
$loop = React\EventLoop\Loop::get();
|
||||
```
|
||||
|
||||
This method always returns an instance implementing [`LoopInterface`](#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()`](https://www.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
|
||||
when using any version before PHP 7.3, because a monotonic time source is
|
||||
only available as of PHP 7.3 (`hrtime()`).
|
||||
While this does not affect many common use cases, this is an important
|
||||
distinction for programs that rely on a high time precision or on systems
|
||||
that are subject to discontinuous time adjustments (time jumps).
|
||||
This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and
|
||||
then adjust your system time forward by 20s, the timer may trigger in 10s.
|
||||
See also [`addTimer()`](#addtimer) for more details.
|
||||
|
||||
#### ExtEventLoop
|
||||
|
||||
An `ext-event` based event loop.
|
||||
|
||||
This uses the [`event` PECL extension](https://pecl.php.net/package/event),
|
||||
that provides an interface to `libevent` library.
|
||||
`libevent` itself supports a number of system-specific backends (epoll, kqueue).
|
||||
|
||||
This loop is known to work with PHP 5.4 through PHP 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.
|
||||
`libev` itself supports a number of system-specific backends (epoll, kqueue).
|
||||
|
||||
|
||||
This loop is known to work with PHP 5.4 through PHP 7+.
|
||||
|
||||
#### ExtUvLoop
|
||||
|
||||
An `ext-uv` based event loop.
|
||||
|
||||
This loop uses the [`uv` PECL extension](https://pecl.php.net/package/uv),
|
||||
that provides an interface to `libuv` library.
|
||||
`libuv` itself supports a number of system-specific backends (epoll, kqueue).
|
||||
|
||||
This loop is known to work with PHP 7+.
|
||||
|
||||
#### ~~ExtLibeventLoop~~
|
||||
|
||||
> Deprecated since v1.2.0, use [`ExtEventLoop`](#exteventloop) instead.
|
||||
|
||||
An `ext-libevent` based event loop.
|
||||
|
||||
This uses the [`libevent` PECL extension](https://pecl.php.net/package/libevent),
|
||||
that provides an interface to `libevent` library.
|
||||
`libevent` itself supports a number of system-specific backends (epoll, kqueue).
|
||||
|
||||
This event loop does only work with PHP 5.
|
||||
An [unofficial update](https://github.com/php/pecl-event-libevent/pull/2) for
|
||||
PHP 7 does exist, but it is known to cause regular crashes due to `SEGFAULT`s.
|
||||
To reiterate: Using this event loop on PHP 7 is not recommended.
|
||||
Accordingly, 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~~
|
||||
|
||||
> Deprecated since v1.2.0, use [`ExtEvLoop`](#extevloop) instead.
|
||||
|
||||
An `ext-libev` based event loop.
|
||||
|
||||
This uses an [unofficial `libev` extension](https://github.com/m4rw3r/php-libev),
|
||||
that provides an interface to `libev` library.
|
||||
`libev` itself supports a number of system-specific backends (epoll, kqueue).
|
||||
|
||||
This loop does only work with PHP 5.
|
||||
An update for PHP 7 is [unlikely](https://github.com/m4rw3r/php-libev/issues/8)
|
||||
to happen any time soon.
|
||||
|
||||
### 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
|
||||
only available as of PHP 7.3 by default, event loop implementations MAY
|
||||
fall back to using wall-clock time.
|
||||
While this does not affect many common use cases, this is an important
|
||||
distinction for programs that rely on a high time precision or on systems
|
||||
that are subject to discontinuous time adjustments (time jumps).
|
||||
This means that if you schedule a timer to trigger in 30s and then adjust
|
||||
your system time forward by 20s, the timer SHOULD still trigger in 30s.
|
||||
See also [event loop implementations](#loop-implementations) for more details.
|
||||
|
||||
#### 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
|
||||
only available as of PHP 7.3 by default, event loop implementations MAY
|
||||
fall back to using wall-clock time.
|
||||
While this does not affect many common use cases, this is an important
|
||||
distinction for programs that rely on a high time precision or on systems
|
||||
that are subject to discontinuous time adjustments (time jumps).
|
||||
This means that if you schedule a timer to trigger in 30s and then adjust
|
||||
your system time forward by 20s, the timer SHOULD still trigger in 30s.
|
||||
See also [event loop implementations](#loop-implementations) for more details.
|
||||
|
||||
Additionally, periodic timers may be subject to timer drift due to
|
||||
re-scheduling after each invocation. As such, it's generally not
|
||||
recommended to rely on this for high precision intervals with millisecond
|
||||
accuracy or below.
|
||||
|
||||
#### 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 project follows [SemVer](https://semver.org/).
|
||||
This will install the latest supported version:
|
||||
|
||||
```bash
|
||||
$ composer require react/event-loop:^1.2
|
||||
```
|
||||
|
||||
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 8+ 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.
|
49
vendor/react/event-loop/composer.json
vendored
Normal file
49
vendor/react/event-loop/composer.json
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"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",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Lück",
|
||||
"homepage": "https://clue.engineering/",
|
||||
"email": "christian@clue.engineering"
|
||||
},
|
||||
{
|
||||
"name": "Cees-Jan Kiewiet",
|
||||
"homepage": "https://wyrihaximus.net/",
|
||||
"email": "reactphp@ceesjankiewiet.nl"
|
||||
},
|
||||
{
|
||||
"name": "Jan Sorgalla",
|
||||
"homepage": "https://sorgalla.com/",
|
||||
"email": "jsorgalla@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Chris Boden",
|
||||
"homepage": "https://cboden.dev/",
|
||||
"email": "cboden@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-event": "~1.0 for ExtEventLoop",
|
||||
"ext-pcntl": "For signal handling support when using the StreamSelectLoop",
|
||||
"ext-uv": "* for ExtUvLoop"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"React\\EventLoop\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"React\\Tests\\EventLoop\\": "tests"
|
||||
}
|
||||
}
|
||||
}
|
13
vendor/react/event-loop/examples/01-timers.php
vendored
Normal file
13
vendor/react/event-loop/examples/01-timers.php
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
Loop::addTimer(0.8, function () {
|
||||
echo 'world!' . PHP_EOL;
|
||||
});
|
||||
|
||||
Loop::addTimer(0.3, function () {
|
||||
echo 'hello ';
|
||||
});
|
14
vendor/react/event-loop/examples/02-periodic.php
vendored
Normal file
14
vendor/react/event-loop/examples/02-periodic.php
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$timer = Loop::addPeriodicTimer(0.1, function () {
|
||||
echo 'Tick' . PHP_EOL;
|
||||
});
|
||||
|
||||
Loop::addTimer(1.0, function () use ($timer) {
|
||||
Loop::cancelTimer($timer);
|
||||
echo 'Done' . PHP_EOL;
|
||||
});
|
13
vendor/react/event-loop/examples/03-ticks.php
vendored
Normal file
13
vendor/react/event-loop/examples/03-ticks.php
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
Loop::futureTick(function () {
|
||||
echo 'b';
|
||||
});
|
||||
Loop::futureTick(function () {
|
||||
echo 'c';
|
||||
});
|
||||
echo 'a';
|
17
vendor/react/event-loop/examples/04-signals.php
vendored
Normal file
17
vendor/react/event-loop/examples/04-signals.php
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
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::addSignal(SIGINT, $func = function ($signal) use (&$func) {
|
||||
echo 'Signal: ', (string)$signal, PHP_EOL;
|
||||
Loop::removeSignal(SIGINT, $func);
|
||||
});
|
||||
|
||||
echo 'Listening for SIGINT. Use "kill -SIGINT ' . getmypid() . '" or CTRL+C' . PHP_EOL;
|
26
vendor/react/event-loop/examples/11-consume-stdin.php
vendored
Normal file
26
vendor/react/event-loop/examples/11-consume-stdin.php
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// read everything from STDIN and report number of bytes
|
||||
// for illustration purposes only, should use react/stream instead
|
||||
Loop::addReadStream(STDIN, function ($stream) {
|
||||
$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;
|
||||
});
|
39
vendor/react/event-loop/examples/12-generate-yes.php
vendored
Normal file
39
vendor/react/event-loop/examples/12-generate-yes.php
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
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)));
|
||||
|
||||
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 (&$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);
|
||||
}
|
||||
});
|
31
vendor/react/event-loop/examples/13-http-client-blocking.php
vendored
Normal file
31
vendor/react/event-loop/examples/13-http-client-blocking.php
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// 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) {
|
||||
$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;
|
||||
});
|
60
vendor/react/event-loop/examples/14-http-client-async.php
vendored
Normal file
60
vendor/react/event-loop/examples/14-http-client-async.php
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Factory;
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// 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 ($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) {
|
||||
$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;
|
||||
});
|
||||
});
|
34
vendor/react/event-loop/examples/21-http-server.php
vendored
Normal file
34
vendor/react/event-loop/examples/21-http-server.php
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// 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) {
|
||||
$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) {
|
||||
$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";
|
||||
});
|
11
vendor/react/event-loop/examples/91-benchmark-ticks.php
vendored
Normal file
11
vendor/react/event-loop/examples/91-benchmark-ticks.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$n = isset($argv[1]) ? (int)$argv[1] : 1000 * 100;
|
||||
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
Loop::futureTick(function () { });
|
||||
}
|
11
vendor/react/event-loop/examples/92-benchmark-timers.php
vendored
Normal file
11
vendor/react/event-loop/examples/92-benchmark-timers.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$n = isset($argv[1]) ? (int)$argv[1] : 1000 * 100;
|
||||
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
Loop::addTimer(0, function () { });
|
||||
}
|
18
vendor/react/event-loop/examples/93-benchmark-ticks-delay.php
vendored
Normal file
18
vendor/react/event-loop/examples/93-benchmark-ticks-delay.php
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$ticks = isset($argv[1]) ? (int)$argv[1] : 1000 * 100;
|
||||
$tick = function () use (&$tick, &$ticks) {
|
||||
if ($ticks > 0) {
|
||||
--$ticks;
|
||||
//$loop->addTimer(0, $tick);
|
||||
Loop::futureTick($tick);
|
||||
} else {
|
||||
echo 'done';
|
||||
}
|
||||
};
|
||||
|
||||
$tick();
|
18
vendor/react/event-loop/examples/94-benchmark-timers-delay.php
vendored
Normal file
18
vendor/react/event-loop/examples/94-benchmark-timers-delay.php
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$ticks = isset($argv[1]) ? (int)$argv[1] : 1000 * 100;
|
||||
$tick = function () use (&$tick, &$ticks) {
|
||||
if ($ticks > 0) {
|
||||
--$ticks;
|
||||
//$loop->futureTick($tick);
|
||||
Loop::addTimer(0, $tick);
|
||||
} else {
|
||||
echo 'done';
|
||||
}
|
||||
};
|
||||
|
||||
$tick();
|
67
vendor/react/event-loop/examples/95-benchmark-memory.php
vendored
Normal file
67
vendor/react/event-loop/examples/95-benchmark-memory.php
vendored
Normal file
|
@ -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\Loop;
|
||||
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' : Loop::get();
|
||||
|
||||
if (!($loop instanceof LoopInterface)) {
|
||||
Loop::set(new $loop());
|
||||
}
|
||||
|
||||
$r = isset($args['r']) ? (int)$args['r'] : 2;
|
||||
|
||||
$runs = 0;
|
||||
|
||||
if (5 < $t) {
|
||||
Loop::addTimer($t, function () {
|
||||
Loop::stop();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Loop::addPeriodicTimer(0.001, function () use (&$runs) {
|
||||
$runs++;
|
||||
|
||||
Loop::addPeriodicTimer(1, function (TimerInterface $timer) {
|
||||
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::get()), "\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::get()), "\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";
|
19
vendor/react/event-loop/phpunit.xml.dist
vendored
Normal file
19
vendor/react/event-loop/phpunit.xml.dist
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- PHPUnit configuration file with new format for PHPUnit 9.3+ -->
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
cacheResult="false">
|
||||
<testsuites>
|
||||
<testsuite name="React test suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage>
|
||||
<include>
|
||||
<directory>./src/</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
</phpunit>
|
18
vendor/react/event-loop/phpunit.xml.legacy
vendored
Normal file
18
vendor/react/event-loop/phpunit.xml.legacy
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- PHPUnit configuration file with old format for PHPUnit 9.2 or older -->
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/4.8/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="React test suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
253
vendor/react/event-loop/src/ExtEvLoop.php
vendored
Normal file
253
vendor/react/event-loop/src/ExtEvLoop.php
vendored
Normal file
|
@ -0,0 +1,253 @@
|
|||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use Ev;
|
||||
use EvIo;
|
||||
use EvLoop;
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* An `ext-ev` based event loop.
|
||||
*
|
||||
* This loop uses the [`ev` PECL extension](https://pecl.php.net/package/ev),
|
||||
* that provides an interface to `libev` library.
|
||||
* `libev` itself supports a number of system-specific backends (epoll, kqueue).
|
||||
*
|
||||
* This loop is known to work with PHP 5.4 through PHP 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 = array();
|
||||
|
||||
/**
|
||||
* @var EvIo[]
|
||||
*/
|
||||
private $writeStreams = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $running;
|
||||
|
||||
/**
|
||||
* @var SignalsHandler
|
||||
*/
|
||||
private $signals;
|
||||
|
||||
/**
|
||||
* @var \EvSignal[]
|
||||
*/
|
||||
private $signalEvents = array();
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->loop = new EvLoop();
|
||||
$this->futureTickQueue = new FutureTickQueue();
|
||||
$this->timers = new SplObjectStorage();
|
||||
$this->signals = new SignalsHandler();
|
||||
}
|
||||
|
||||
public function addReadStream($stream, $listener)
|
||||
{
|
||||
$key = (int)$stream;
|
||||
|
||||
if (isset($this->readStreams[$key])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$callback = $this->getStreamListenerClosure($stream, $listener);
|
||||
$event = $this->loop->io($stream, Ev::READ, $callback);
|
||||
$this->readStreams[$key] = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $stream
|
||||
* @param callable $listener
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
private function getStreamListenerClosure($stream, $listener)
|
||||
{
|
||||
return function () use ($stream, $listener) {
|
||||
\call_user_func($listener, $stream);
|
||||
};
|
||||
}
|
||||
|
||||
public function addWriteStream($stream, $listener)
|
||||
{
|
||||
$key = (int)$stream;
|
||||
|
||||
if (isset($this->writeStreams[$key])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$callback = $this->getStreamListenerClosure($stream, $listener);
|
||||
$event = $this->loop->io($stream, Ev::WRITE, $callback);
|
||||
$this->writeStreams[$key] = $event;
|
||||
}
|
||||
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int)$stream;
|
||||
|
||||
if (!isset($this->readStreams[$key])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->readStreams[$key]->stop();
|
||||
unset($this->readStreams[$key]);
|
||||
}
|
||||
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int)$stream;
|
||||
|
||||
if (!isset($this->writeStreams[$key])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->writeStreams[$key]->stop();
|
||||
unset($this->writeStreams[$key]);
|
||||
}
|
||||
|
||||
public function addTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer($interval, $callback, false);
|
||||
|
||||
$that = $this;
|
||||
$timers = $this->timers;
|
||||
$callback = function () use ($timer, $timers, $that) {
|
||||
\call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
if ($timers->contains($timer)) {
|
||||
$that->cancelTimer($timer);
|
||||
}
|
||||
};
|
||||
|
||||
$event = $this->loop->timer($timer->getInterval(), 0.0, $callback);
|
||||
$this->timers->attach($timer, $event);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
public function addPeriodicTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer($interval, $callback, true);
|
||||
|
||||
$callback = function () use ($timer) {
|
||||
\call_user_func($timer->getCallback(), $timer);
|
||||
};
|
||||
|
||||
$event = $this->loop->timer($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]);
|
||||
}
|
||||
}
|
||||
}
|
275
vendor/react/event-loop/src/ExtEventLoop.php
vendored
Normal file
275
vendor/react/event-loop/src/ExtEventLoop.php
vendored
Normal file
|
@ -0,0 +1,275 @@
|
|||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Event;
|
||||
use EventBase;
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* An `ext-event` based event loop.
|
||||
*
|
||||
* This uses the [`event` PECL extension](https://pecl.php.net/package/event),
|
||||
* that provides an interface to `libevent` library.
|
||||
* `libevent` itself supports a number of system-specific backends (epoll, kqueue).
|
||||
*
|
||||
* This loop is known to work with PHP 5.4 through PHP 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');
|
||||
}
|
||||
|
||||
// support arbitrary file descriptors and not just sockets
|
||||
// Windows only has limited file descriptor support, so do not require this (will fail otherwise)
|
||||
// @link http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html#_setting_up_a_complicated_event_base
|
||||
$config = new \EventConfig();
|
||||
if (\DIRECTORY_SEPARATOR !== '\\') {
|
||||
$config->requireFeatures(\EventConfig::FEATURE_FDS);
|
||||
}
|
||||
|
||||
$this->eventBase = new EventBase($config);
|
||||
$this->futureTickQueue = new FutureTickQueue();
|
||||
$this->timerEvents = new SplObjectStorage();
|
||||
$this->signals = new SignalsHandler();
|
||||
|
||||
$this->createTimerCallback();
|
||||
$this->createStreamCallback();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// explicitly clear all references to Event objects to prevent SEGFAULTs on Windows
|
||||
foreach ($this->timerEvents as $timer) {
|
||||
$this->timerEvents->detach($timer);
|
||||
}
|
||||
|
||||
$this->readEvents = array();
|
||||
$this->writeEvents = array();
|
||||
}
|
||||
|
||||
public function addReadStream($stream, $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
if (isset($this->readListeners[$key])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event = new Event($this->eventBase, $stream, Event::PERSIST | Event::READ, $this->streamCallback);
|
||||
$event->add();
|
||||
$this->readEvents[$key] = $event;
|
||||
$this->readListeners[$key] = $listener;
|
||||
|
||||
// ext-event does not increase refcount on stream resources for PHP 7+
|
||||
// manually keep track of stream resource to prevent premature garbage collection
|
||||
if (\PHP_VERSION_ID >= 70000) {
|
||||
$this->readRefs[$key] = $stream;
|
||||
}
|
||||
}
|
||||
|
||||
public function addWriteStream($stream, $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
if (isset($this->writeListeners[$key])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event = new Event($this->eventBase, $stream, Event::PERSIST | Event::WRITE, $this->streamCallback);
|
||||
$event->add();
|
||||
$this->writeEvents[$key] = $event;
|
||||
$this->writeListeners[$key] = $listener;
|
||||
|
||||
// ext-event does not increase refcount on stream resources for PHP 7+
|
||||
// manually keep track of stream resource to prevent premature garbage collection
|
||||
if (\PHP_VERSION_ID >= 70000) {
|
||||
$this->writeRefs[$key] = $stream;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->readEvents[$key])) {
|
||||
$this->readEvents[$key]->free();
|
||||
unset(
|
||||
$this->readEvents[$key],
|
||||
$this->readListeners[$key],
|
||||
$this->readRefs[$key]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeEvents[$key])) {
|
||||
$this->writeEvents[$key]->free();
|
||||
unset(
|
||||
$this->writeEvents[$key],
|
||||
$this->writeListeners[$key],
|
||||
$this->writeRefs[$key]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function addTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer($interval, $callback, false);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
public function addPeriodicTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer($interval, $callback, true);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
if ($this->timerEvents->contains($timer)) {
|
||||
$this->timerEvents[$timer]->free();
|
||||
$this->timerEvents->detach($timer);
|
||||
}
|
||||
}
|
||||
|
||||
public function futureTick($listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
public function addSignal($signal, $listener)
|
||||
{
|
||||
$this->signals->add($signal, $listener);
|
||||
|
||||
if (!isset($this->signalEvents[$signal])) {
|
||||
$this->signalEvents[$signal] = Event::signal($this->eventBase, $signal, array($this->signals, 'call'));
|
||||
$this->signalEvents[$signal]->add();
|
||||
}
|
||||
}
|
||||
|
||||
public function removeSignal($signal, $listener)
|
||||
{
|
||||
$this->signals->remove($signal, $listener);
|
||||
|
||||
if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) {
|
||||
$this->signalEvents[$signal]->free();
|
||||
unset($this->signalEvents[$signal]);
|
||||
}
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$flags = EventBase::LOOP_ONCE;
|
||||
if (!$this->running || !$this->futureTickQueue->isEmpty()) {
|
||||
$flags |= EventBase::LOOP_NONBLOCK;
|
||||
} elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->eventBase->loop($flags);
|
||||
}
|
||||
}
|
||||
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a timer for execution.
|
||||
*
|
||||
* @param TimerInterface $timer
|
||||
*/
|
||||
private function scheduleTimer(TimerInterface $timer)
|
||||
{
|
||||
$flags = Event::TIMEOUT;
|
||||
|
||||
if ($timer->isPeriodic()) {
|
||||
$flags |= Event::PERSIST;
|
||||
}
|
||||
|
||||
$event = new Event($this->eventBase, -1, $flags, $this->timerCallback, $timer);
|
||||
$this->timerEvents[$timer] = $event;
|
||||
|
||||
$event->add($timer->getInterval());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of timer events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createTimerCallback()
|
||||
{
|
||||
$timers = $this->timerEvents;
|
||||
$this->timerCallback = function ($_, $__, $timer) use ($timers) {
|
||||
\call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
if (!$timer->isPeriodic() && $timers->contains($timer)) {
|
||||
$this->cancelTimer($timer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of stream events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createStreamCallback()
|
||||
{
|
||||
$read =& $this->readListeners;
|
||||
$write =& $this->writeListeners;
|
||||
$this->streamCallback = function ($stream, $flags) use (&$read, &$write) {
|
||||
$key = (int) $stream;
|
||||
|
||||
if (Event::READ === (Event::READ & $flags) && isset($read[$key])) {
|
||||
\call_user_func($read[$key], $stream);
|
||||
}
|
||||
|
||||
if (Event::WRITE === (Event::WRITE & $flags) && isset($write[$key])) {
|
||||
\call_user_func($write[$key], $stream);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
201
vendor/react/event-loop/src/ExtLibevLoop.php
vendored
Normal file
201
vendor/react/event-loop/src/ExtLibevLoop.php
vendored
Normal file
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use BadMethodCallException;
|
||||
use libev\EventLoop;
|
||||
use libev\IOEvent;
|
||||
use libev\SignalEvent;
|
||||
use libev\TimerEvent;
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* [Deprecated] An `ext-libev` based event loop.
|
||||
*
|
||||
* This uses an [unofficial `libev` extension](https://github.com/m4rw3r/php-libev),
|
||||
* that provides an interface to `libev` library.
|
||||
* `libev` itself supports a number of system-specific backends (epoll, kqueue).
|
||||
*
|
||||
* This loop does only work with PHP 5.
|
||||
* An update for PHP 7 is [unlikely](https://github.com/m4rw3r/php-libev/issues/8)
|
||||
* to happen any time soon.
|
||||
*
|
||||
* @see https://github.com/m4rw3r/php-libev
|
||||
* @see https://gist.github.com/1688204
|
||||
* @deprecated 1.2.0, use [`ExtEvLoop`](#extevloop) instead.
|
||||
*/
|
||||
final class ExtLibevLoop implements LoopInterface
|
||||
{
|
||||
private $loop;
|
||||
private $futureTickQueue;
|
||||
private $timerEvents;
|
||||
private $readEvents = array();
|
||||
private $writeEvents = array();
|
||||
private $running;
|
||||
private $signals;
|
||||
private $signalEvents = array();
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (!\class_exists('libev\EventLoop', false)) {
|
||||
throw new BadMethodCallException('Cannot create ExtLibevLoop, ext-libev extension missing');
|
||||
}
|
||||
|
||||
$this->loop = new EventLoop();
|
||||
$this->futureTickQueue = new FutureTickQueue();
|
||||
$this->timerEvents = new SplObjectStorage();
|
||||
$this->signals = new SignalsHandler();
|
||||
}
|
||||
|
||||
public function addReadStream($stream, $listener)
|
||||
{
|
||||
if (isset($this->readEvents[(int) $stream])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$callback = function () use ($stream, $listener) {
|
||||
\call_user_func($listener, $stream);
|
||||
};
|
||||
|
||||
$event = new IOEvent($callback, $stream, IOEvent::READ);
|
||||
$this->loop->add($event);
|
||||
|
||||
$this->readEvents[(int) $stream] = $event;
|
||||
}
|
||||
|
||||
public function addWriteStream($stream, $listener)
|
||||
{
|
||||
if (isset($this->writeEvents[(int) $stream])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$callback = function () use ($stream, $listener) {
|
||||
\call_user_func($listener, $stream);
|
||||
};
|
||||
|
||||
$event = new IOEvent($callback, $stream, IOEvent::WRITE);
|
||||
$this->loop->add($event);
|
||||
|
||||
$this->writeEvents[(int) $stream] = $event;
|
||||
}
|
||||
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->readEvents[$key])) {
|
||||
$this->readEvents[$key]->stop();
|
||||
$this->loop->remove($this->readEvents[$key]);
|
||||
unset($this->readEvents[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeEvents[$key])) {
|
||||
$this->writeEvents[$key]->stop();
|
||||
$this->loop->remove($this->writeEvents[$key]);
|
||||
unset($this->writeEvents[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
public function addTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer( $interval, $callback, false);
|
||||
|
||||
$that = $this;
|
||||
$timers = $this->timerEvents;
|
||||
$callback = function () use ($timer, $timers, $that) {
|
||||
\call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
if ($timers->contains($timer)) {
|
||||
$that->cancelTimer($timer);
|
||||
}
|
||||
};
|
||||
|
||||
$event = new TimerEvent($callback, $timer->getInterval());
|
||||
$this->timerEvents->attach($timer, $event);
|
||||
$this->loop->add($event);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
public function addPeriodicTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer($interval, $callback, true);
|
||||
|
||||
$callback = function () use ($timer) {
|
||||
\call_user_func($timer->getCallback(), $timer);
|
||||
};
|
||||
|
||||
$event = new TimerEvent($callback, $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;
|
||||
}
|
||||
}
|
285
vendor/react/event-loop/src/ExtLibeventLoop.php
vendored
Normal file
285
vendor/react/event-loop/src/ExtLibeventLoop.php
vendored
Normal file
|
@ -0,0 +1,285 @@
|
|||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Event;
|
||||
use EventBase;
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* [Deprecated] An `ext-libevent` based event loop.
|
||||
*
|
||||
* This uses the [`libevent` PECL extension](https://pecl.php.net/package/libevent),
|
||||
* that provides an interface to `libevent` library.
|
||||
* `libevent` itself supports a number of system-specific backends (epoll, kqueue).
|
||||
*
|
||||
* This event loop does only work with PHP 5.
|
||||
* An [unofficial update](https://github.com/php/pecl-event-libevent/pull/2) for
|
||||
* PHP 7 does exist, but it is known to cause regular crashes due to `SEGFAULT`s.
|
||||
* To reiterate: Using this event loop on PHP 7 is not recommended.
|
||||
* Accordingly, 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
|
||||
* @deprecated 1.2.0, use [`ExtEventLoop`](#exteventloop) instead.
|
||||
*/
|
||||
final class ExtLibeventLoop implements LoopInterface
|
||||
{
|
||||
/** @internal */
|
||||
const MICROSECONDS_PER_SECOND = 1000000;
|
||||
|
||||
private $eventBase;
|
||||
private $futureTickQueue;
|
||||
private $timerCallback;
|
||||
private $timerEvents;
|
||||
private $streamCallback;
|
||||
private $readEvents = array();
|
||||
private $writeEvents = array();
|
||||
private $readListeners = array();
|
||||
private $writeListeners = array();
|
||||
private $running;
|
||||
private $signals;
|
||||
private $signalEvents = array();
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (!\function_exists('event_base_new')) {
|
||||
throw new BadMethodCallException('Cannot create ExtLibeventLoop, ext-libevent extension missing');
|
||||
}
|
||||
|
||||
$this->eventBase = \event_base_new();
|
||||
$this->futureTickQueue = new FutureTickQueue();
|
||||
$this->timerEvents = new SplObjectStorage();
|
||||
$this->signals = new SignalsHandler();
|
||||
|
||||
$this->createTimerCallback();
|
||||
$this->createStreamCallback();
|
||||
}
|
||||
|
||||
public function addReadStream($stream, $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
if (isset($this->readListeners[$key])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event = \event_new();
|
||||
\event_set($event, $stream, \EV_PERSIST | \EV_READ, $this->streamCallback);
|
||||
\event_base_set($event, $this->eventBase);
|
||||
\event_add($event);
|
||||
|
||||
$this->readEvents[$key] = $event;
|
||||
$this->readListeners[$key] = $listener;
|
||||
}
|
||||
|
||||
public function addWriteStream($stream, $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
if (isset($this->writeListeners[$key])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event = \event_new();
|
||||
\event_set($event, $stream, \EV_PERSIST | \EV_WRITE, $this->streamCallback);
|
||||
\event_base_set($event, $this->eventBase);
|
||||
\event_add($event);
|
||||
|
||||
$this->writeEvents[$key] = $event;
|
||||
$this->writeListeners[$key] = $listener;
|
||||
}
|
||||
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->readListeners[$key])) {
|
||||
$event = $this->readEvents[$key];
|
||||
\event_del($event);
|
||||
\event_free($event);
|
||||
|
||||
unset(
|
||||
$this->readEvents[$key],
|
||||
$this->readListeners[$key]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeListeners[$key])) {
|
||||
$event = $this->writeEvents[$key];
|
||||
\event_del($event);
|
||||
\event_free($event);
|
||||
|
||||
unset(
|
||||
$this->writeEvents[$key],
|
||||
$this->writeListeners[$key]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function addTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer($interval, $callback, false);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
public function addPeriodicTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer($interval, $callback, true);
|
||||
|
||||
$this->scheduleTimer($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
if ($this->timerEvents->contains($timer)) {
|
||||
$event = $this->timerEvents[$timer];
|
||||
\event_del($event);
|
||||
\event_free($event);
|
||||
|
||||
$this->timerEvents->detach($timer);
|
||||
}
|
||||
}
|
||||
|
||||
public function futureTick($listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
public function addSignal($signal, $listener)
|
||||
{
|
||||
$this->signals->add($signal, $listener);
|
||||
|
||||
if (!isset($this->signalEvents[$signal])) {
|
||||
$this->signalEvents[$signal] = \event_new();
|
||||
\event_set($this->signalEvents[$signal], $signal, \EV_PERSIST | \EV_SIGNAL, array($this->signals, 'call'));
|
||||
\event_base_set($this->signalEvents[$signal], $this->eventBase);
|
||||
\event_add($this->signalEvents[$signal]);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeSignal($signal, $listener)
|
||||
{
|
||||
$this->signals->remove($signal, $listener);
|
||||
|
||||
if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) {
|
||||
\event_del($this->signalEvents[$signal]);
|
||||
\event_free($this->signalEvents[$signal]);
|
||||
unset($this->signalEvents[$signal]);
|
||||
}
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$flags = \EVLOOP_ONCE;
|
||||
if (!$this->running || !$this->futureTickQueue->isEmpty()) {
|
||||
$flags |= \EVLOOP_NONBLOCK;
|
||||
} elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
\event_base_loop($this->eventBase, $flags);
|
||||
}
|
||||
}
|
||||
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a timer for execution.
|
||||
*
|
||||
* @param TimerInterface $timer
|
||||
*/
|
||||
private function scheduleTimer(TimerInterface $timer)
|
||||
{
|
||||
$this->timerEvents[$timer] = $event = \event_timer_new();
|
||||
|
||||
\event_timer_set($event, $this->timerCallback, $timer);
|
||||
\event_base_set($event, $this->eventBase);
|
||||
\event_add($event, $timer->getInterval() * self::MICROSECONDS_PER_SECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of timer events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createTimerCallback()
|
||||
{
|
||||
$that = $this;
|
||||
$timers = $this->timerEvents;
|
||||
$this->timerCallback = function ($_, $__, $timer) use ($timers, $that) {
|
||||
\call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
// Timer already cancelled ...
|
||||
if (!$timers->contains($timer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reschedule periodic timers ...
|
||||
if ($timer->isPeriodic()) {
|
||||
\event_add(
|
||||
$timers[$timer],
|
||||
$timer->getInterval() * ExtLibeventLoop::MICROSECONDS_PER_SECOND
|
||||
);
|
||||
|
||||
// Clean-up one shot timers ...
|
||||
} else {
|
||||
$that->cancelTimer($timer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback used as the target of stream events.
|
||||
*
|
||||
* A reference is kept to the callback for the lifetime of the loop
|
||||
* to prevent "Cannot destroy active lambda function" fatal error from
|
||||
* the event extension.
|
||||
*/
|
||||
private function createStreamCallback()
|
||||
{
|
||||
$read =& $this->readListeners;
|
||||
$write =& $this->writeListeners;
|
||||
$this->streamCallback = function ($stream, $flags) use (&$read, &$write) {
|
||||
$key = (int) $stream;
|
||||
|
||||
if (\EV_READ === (\EV_READ & $flags) && isset($read[$key])) {
|
||||
\call_user_func($read[$key], $stream);
|
||||
}
|
||||
|
||||
if (\EV_WRITE === (\EV_WRITE & $flags) && isset($write[$key])) {
|
||||
\call_user_func($write[$key], $stream);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
342
vendor/react/event-loop/src/ExtUvLoop.php
vendored
Normal file
342
vendor/react/event-loop/src/ExtUvLoop.php
vendored
Normal file
|
@ -0,0 +1,342 @@
|
|||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* An `ext-uv` based event loop.
|
||||
*
|
||||
* This loop uses the [`uv` PECL extension](https://pecl.php.net/package/uv),
|
||||
* that provides an interface to `libuv` library.
|
||||
* `libuv` itself supports a number of system-specific backends (epoll, kqueue).
|
||||
*
|
||||
* This loop is known to work with PHP 7+.
|
||||
*
|
||||
* @see https://github.com/bwoebi/php-uv
|
||||
*/
|
||||
final class ExtUvLoop implements LoopInterface
|
||||
{
|
||||
private $uv;
|
||||
private $futureTickQueue;
|
||||
private $timers;
|
||||
private $streamEvents = array();
|
||||
private $readStreams = array();
|
||||
private $writeStreams = array();
|
||||
private $running;
|
||||
private $signals;
|
||||
private $signalEvents = array();
|
||||
private $streamListener;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (!\function_exists('uv_loop_new')) {
|
||||
throw new \BadMethodCallException('Cannot create LibUvLoop, ext-uv extension missing');
|
||||
}
|
||||
|
||||
$this->uv = \uv_loop_new();
|
||||
$this->futureTickQueue = new FutureTickQueue();
|
||||
$this->timers = new SplObjectStorage();
|
||||
$this->streamListener = $this->createStreamListener();
|
||||
$this->signals = new SignalsHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying ext-uv event loop. (Internal ReactPHP use only.)
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public function getUvLoop()
|
||||
{
|
||||
return $this->uv;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addReadStream($stream, $listener)
|
||||
{
|
||||
if (isset($this->readStreams[(int) $stream])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->readStreams[(int) $stream] = $listener;
|
||||
$this->addStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addWriteStream($stream, $listener)
|
||||
{
|
||||
if (isset($this->writeStreams[(int) $stream])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->writeStreams[(int) $stream] = $listener;
|
||||
$this->addStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
if (!isset($this->streamEvents[(int) $stream])) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset($this->readStreams[(int) $stream]);
|
||||
$this->removeStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
if (!isset($this->streamEvents[(int) $stream])) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset($this->writeStreams[(int) $stream]);
|
||||
$this->removeStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer($interval, $callback, false);
|
||||
|
||||
$that = $this;
|
||||
$timers = $this->timers;
|
||||
$callback = function () use ($timer, $timers, $that) {
|
||||
\call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
if ($timers->contains($timer)) {
|
||||
$that->cancelTimer($timer);
|
||||
}
|
||||
};
|
||||
|
||||
$event = \uv_timer_init($this->uv);
|
||||
$this->timers->attach($timer, $event);
|
||||
\uv_timer_start(
|
||||
$event,
|
||||
$this->convertFloatSecondsToMilliseconds($interval),
|
||||
0,
|
||||
$callback
|
||||
);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addPeriodicTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer($interval, $callback, true);
|
||||
|
||||
$callback = function () use ($timer) {
|
||||
\call_user_func($timer->getCallback(), $timer);
|
||||
};
|
||||
|
||||
$interval = $this->convertFloatSecondsToMilliseconds($interval);
|
||||
$event = \uv_timer_init($this->uv);
|
||||
$this->timers->attach($timer, $event);
|
||||
\uv_timer_start(
|
||||
$event,
|
||||
$interval,
|
||||
(int) $interval === 0 ? 1 : $interval,
|
||||
$callback
|
||||
);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
if (isset($this->timers[$timer])) {
|
||||
@\uv_timer_stop($this->timers[$timer]);
|
||||
$this->timers->detach($timer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function futureTick($listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
public function addSignal($signal, $listener)
|
||||
{
|
||||
$this->signals->add($signal, $listener);
|
||||
|
||||
if (!isset($this->signalEvents[$signal])) {
|
||||
$signals = $this->signals;
|
||||
$this->signalEvents[$signal] = \uv_signal_init($this->uv);
|
||||
\uv_signal_start($this->signalEvents[$signal], function () use ($signals, $signal) {
|
||||
$signals->call($signal);
|
||||
}, $signal);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeSignal($signal, $listener)
|
||||
{
|
||||
$this->signals->remove($signal, $listener);
|
||||
|
||||
if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) {
|
||||
\uv_signal_stop($this->signalEvents[$signal]);
|
||||
unset($this->signalEvents[$signal]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$hasPendingCallbacks = !$this->futureTickQueue->isEmpty();
|
||||
$wasJustStopped = !$this->running;
|
||||
$nothingLeftToDo = !$this->readStreams
|
||||
&& !$this->writeStreams
|
||||
&& !$this->timers->count()
|
||||
&& $this->signals->isEmpty();
|
||||
|
||||
// Use UV::RUN_ONCE when there are only I/O events active in the loop and block until one of those triggers,
|
||||
// otherwise use UV::RUN_NOWAIT.
|
||||
// @link http://docs.libuv.org/en/v1.x/loop.html#c.uv_run
|
||||
$flags = \UV::RUN_ONCE;
|
||||
if ($wasJustStopped || $hasPendingCallbacks) {
|
||||
$flags = \UV::RUN_NOWAIT;
|
||||
} elseif ($nothingLeftToDo) {
|
||||
break;
|
||||
}
|
||||
|
||||
\uv_run($this->uv, $flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
private function addStream($stream)
|
||||
{
|
||||
if (!isset($this->streamEvents[(int) $stream])) {
|
||||
$this->streamEvents[(int)$stream] = \uv_poll_init_socket($this->uv, $stream);
|
||||
}
|
||||
|
||||
if ($this->streamEvents[(int) $stream] !== false) {
|
||||
$this->pollStream($stream);
|
||||
}
|
||||
}
|
||||
|
||||
private function removeStream($stream)
|
||||
{
|
||||
if (!isset($this->streamEvents[(int) $stream])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($this->readStreams[(int) $stream])
|
||||
&& !isset($this->writeStreams[(int) $stream])) {
|
||||
\uv_poll_stop($this->streamEvents[(int) $stream]);
|
||||
\uv_close($this->streamEvents[(int) $stream]);
|
||||
unset($this->streamEvents[(int) $stream]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pollStream($stream);
|
||||
}
|
||||
|
||||
private function pollStream($stream)
|
||||
{
|
||||
if (!isset($this->streamEvents[(int) $stream])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$flags = 0;
|
||||
if (isset($this->readStreams[(int) $stream])) {
|
||||
$flags |= \UV::READABLE;
|
||||
}
|
||||
|
||||
if (isset($this->writeStreams[(int) $stream])) {
|
||||
$flags |= \UV::WRITABLE;
|
||||
}
|
||||
|
||||
\uv_poll_start($this->streamEvents[(int) $stream], $flags, $this->streamListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a stream listener
|
||||
*
|
||||
* @return callable Returns a callback
|
||||
*/
|
||||
private function createStreamListener()
|
||||
{
|
||||
$callback = function ($event, $status, $events, $stream) {
|
||||
// libuv automatically stops polling on error, re-enable polling to match other loop implementations
|
||||
if ($status !== 0) {
|
||||
$this->pollStream($stream);
|
||||
|
||||
// libuv may report no events on error, but this should still invoke stream listeners to report closed connections
|
||||
// re-enable both readable and writable, correct listeners will be checked below anyway
|
||||
if ($events === 0) {
|
||||
$events = \UV::READABLE | \UV::WRITABLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->readStreams[(int) $stream]) && ($events & \UV::READABLE)) {
|
||||
\call_user_func($this->readStreams[(int) $stream], $stream);
|
||||
}
|
||||
|
||||
if (isset($this->writeStreams[(int) $stream]) && ($events & \UV::WRITABLE)) {
|
||||
\call_user_func($this->writeStreams[(int) $stream], $stream);
|
||||
}
|
||||
};
|
||||
|
||||
return $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $interval
|
||||
* @return int
|
||||
*/
|
||||
private function convertFloatSecondsToMilliseconds($interval)
|
||||
{
|
||||
if ($interval < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$maxValue = (int) (\PHP_INT_MAX / 1000);
|
||||
$intInterval = (int) $interval;
|
||||
|
||||
if (($intInterval <= 0 && $interval > 1) || $intInterval >= $maxValue) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$interval}' passed."
|
||||
);
|
||||
}
|
||||
|
||||
return (int) \floor($interval * 1000);
|
||||
}
|
||||
}
|
75
vendor/react/event-loop/src/Factory.php
vendored
Normal file
75
vendor/react/event-loop/src/Factory.php
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
/**
|
||||
* [Deprecated] The `Factory` class exists as a convenient way to pick the best available event loop implementation.
|
||||
*
|
||||
* @deprecated 1.2.0 See Loop instead.
|
||||
* @see Loop
|
||||
*/
|
||||
final class Factory
|
||||
{
|
||||
/**
|
||||
* [Deprecated] Creates a new event loop instance
|
||||
*
|
||||
* ```php
|
||||
* // deprecated
|
||||
* $loop = React\EventLoop\Factory::create();
|
||||
*
|
||||
* // new
|
||||
* $loop = React\EventLoop\Loop::get();
|
||||
* ```
|
||||
*
|
||||
* This method always returns an instance implementing `LoopInterface`,
|
||||
* the actual event loop implementation is an implementation detail.
|
||||
*
|
||||
* This method should usually only be called once at the beginning of the program.
|
||||
*
|
||||
* @deprecated 1.2.0 See Loop::get() instead.
|
||||
* @see Loop::get()
|
||||
*
|
||||
* @return LoopInterface
|
||||
*/
|
||||
public static function create()
|
||||
{
|
||||
$loop = self::construct();
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
return $loop;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return LoopInterface
|
||||
*/
|
||||
private static function construct()
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if (\function_exists('uv_loop_new')) {
|
||||
// only use ext-uv on PHP 7
|
||||
return new ExtUvLoop();
|
||||
}
|
||||
|
||||
if (\class_exists('libev\EventLoop', false)) {
|
||||
return new ExtLibevLoop();
|
||||
}
|
||||
|
||||
if (\class_exists('EvLoop', false)) {
|
||||
return new ExtEvLoop();
|
||||
}
|
||||
|
||||
if (\class_exists('EventBase', false)) {
|
||||
return new ExtEventLoop();
|
||||
}
|
||||
|
||||
if (\function_exists('event_base_new') && \PHP_MAJOR_VERSION === 5) {
|
||||
// only use ext-libevent on PHP 5 for now
|
||||
return new ExtLibeventLoop();
|
||||
}
|
||||
|
||||
return new StreamSelectLoop();
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
225
vendor/react/event-loop/src/Loop.php
vendored
Normal file
225
vendor/react/event-loop/src/Loop.php
vendored
Normal file
|
@ -0,0 +1,225 @@
|
|||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
/**
|
||||
* The `Loop` class exists as a convenient way to get the currently relevant loop
|
||||
*/
|
||||
final class Loop
|
||||
{
|
||||
/**
|
||||
* @var LoopInterface
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/** @var bool */
|
||||
private static $stopped = false;
|
||||
|
||||
/**
|
||||
* Returns the event loop.
|
||||
* When no loop is set, it will call the factory to create one.
|
||||
*
|
||||
* This method always returns an instance implementing `LoopInterface`,
|
||||
* the actual event loop implementation is an implementation detail.
|
||||
*
|
||||
* This method is the preferred way to get the event loop and using
|
||||
* Factory::create has been deprecated.
|
||||
*
|
||||
* @return LoopInterface
|
||||
*/
|
||||
public static function get()
|
||||
{
|
||||
if (self::$instance instanceof LoopInterface) {
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
self::$instance = $loop = Factory::create();
|
||||
|
||||
// Automatically run loop at end of program, unless already started or stopped explicitly.
|
||||
// This is tested using child processes, so coverage is actually 100%, see BinTest.
|
||||
// @codeCoverageIgnoreStart
|
||||
$hasRun = false;
|
||||
$loop->futureTick(function () use (&$hasRun) {
|
||||
$hasRun = true;
|
||||
});
|
||||
|
||||
$stopped =& self::$stopped;
|
||||
register_shutdown_function(function () use ($loop, &$hasRun, &$stopped) {
|
||||
// Don't run if we're coming from a fatal error (uncaught exception).
|
||||
$error = error_get_last();
|
||||
if ((isset($error['type']) ? $error['type'] : 0) & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$hasRun && !$stopped) {
|
||||
$loop->run();
|
||||
}
|
||||
});
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal undocumented method, behavior might change or throw in the
|
||||
* future. Use with caution and at your own risk.
|
||||
*
|
||||
* @internal
|
||||
* @return void
|
||||
*/
|
||||
public static function set(LoopInterface $loop)
|
||||
{
|
||||
self::$instance = $loop;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Advanced] Register a listener to be notified when a stream is ready to read.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param callable $listener
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
* @see LoopInterface::addReadStream()
|
||||
*/
|
||||
public static function addReadStream($stream, $listener)
|
||||
{
|
||||
self::get()->addReadStream($stream, $listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* [Advanced] Register a listener to be notified when a stream is ready to write.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param callable $listener
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
* @see LoopInterface::addWriteStream()
|
||||
*/
|
||||
public static function addWriteStream($stream, $listener)
|
||||
{
|
||||
self::get()->addWriteStream($stream, $listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the read event listener for the given stream.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @return void
|
||||
* @see LoopInterface::removeReadStream()
|
||||
*/
|
||||
public static function removeReadStream($stream)
|
||||
{
|
||||
self::get()->removeReadStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the write event listener for the given stream.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @return void
|
||||
* @see LoopInterface::removeWriteStream()
|
||||
*/
|
||||
public static function removeWriteStream($stream)
|
||||
{
|
||||
self::get()->removeWriteStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue a callback to be invoked once after the given interval.
|
||||
*
|
||||
* @param float $interval
|
||||
* @param callable $callback
|
||||
* @return TimerInterface
|
||||
* @see LoopInterface::addTimer()
|
||||
*/
|
||||
public static function addTimer($interval, $callback)
|
||||
{
|
||||
return self::get()->addTimer($interval, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue a callback to be invoked repeatedly after the given interval.
|
||||
*
|
||||
* @param float $interval
|
||||
* @param callable $callback
|
||||
* @return TimerInterface
|
||||
* @see LoopInterface::addPeriodicTimer()
|
||||
*/
|
||||
public static function addPeriodicTimer($interval, $callback)
|
||||
{
|
||||
return self::get()->addPeriodicTimer($interval, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a pending timer.
|
||||
*
|
||||
* @param TimerInterface $timer
|
||||
* @return void
|
||||
* @see LoopInterface::cancelTimer()
|
||||
*/
|
||||
public static function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
return self::get()->cancelTimer($timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a callback to be invoked on a future tick of the event loop.
|
||||
*
|
||||
* @param callable $listener
|
||||
* @return void
|
||||
* @see LoopInterface::futureTick()
|
||||
*/
|
||||
public static function futureTick($listener)
|
||||
{
|
||||
self::get()->futureTick($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener to be notified when a signal has been caught by this process.
|
||||
*
|
||||
* @param int $signal
|
||||
* @param callable $listener
|
||||
* @return void
|
||||
* @see LoopInterface::addSignal()
|
||||
*/
|
||||
public static function addSignal($signal, $listener)
|
||||
{
|
||||
self::get()->addSignal($signal, $listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a previously added signal listener.
|
||||
*
|
||||
* @param int $signal
|
||||
* @param callable $listener
|
||||
* @return void
|
||||
* @see LoopInterface::removeSignal()
|
||||
*/
|
||||
public static function removeSignal($signal, $listener)
|
||||
{
|
||||
self::get()->removeSignal($signal, $listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the event loop until there are no more tasks to perform.
|
||||
*
|
||||
* @return void
|
||||
* @see LoopInterface::run()
|
||||
*/
|
||||
public static function run()
|
||||
{
|
||||
self::get()->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instruct a running event loop to stop.
|
||||
*
|
||||
* @return void
|
||||
* @see LoopInterface::stop()
|
||||
*/
|
||||
public static function stop()
|
||||
{
|
||||
self::$stopped = true;
|
||||
self::get()->stop();
|
||||
}
|
||||
}
|
463
vendor/react/event-loop/src/LoopInterface.php
vendored
Normal file
463
vendor/react/event-loop/src/LoopInterface.php
vendored
Normal file
|
@ -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
|
||||
* only available as of PHP 7.3 by default, event loop implementations MAY
|
||||
* fall back to using wall-clock time.
|
||||
* While this does not affect many common use cases, this is an important
|
||||
* distinction for programs that rely on a high time precision or on systems
|
||||
* that are subject to discontinuous time adjustments (time jumps).
|
||||
* This means that if you schedule a timer to trigger in 30s and then adjust
|
||||
* your system time forward by 20s, the timer SHOULD still trigger in 30s.
|
||||
* See also [event loop implementations](#loop-implementations) for more details.
|
||||
*
|
||||
* @param int|float $interval The number of seconds to wait before execution.
|
||||
* @param callable $callback The callback to invoke.
|
||||
*
|
||||
* @return TimerInterface
|
||||
*/
|
||||
public function addTimer($interval, $callback);
|
||||
|
||||
/**
|
||||
* Enqueue a callback to be invoked repeatedly after the given interval.
|
||||
*
|
||||
* The 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
|
||||
* only available as of PHP 7.3 by default, event loop implementations MAY
|
||||
* fall back to using wall-clock time.
|
||||
* While this does not affect many common use cases, this is an important
|
||||
* distinction for programs that rely on a high time precision or on systems
|
||||
* that are subject to discontinuous time adjustments (time jumps).
|
||||
* This means that if you schedule a timer to trigger in 30s and then adjust
|
||||
* your system time forward by 20s, the timer SHOULD still trigger in 30s.
|
||||
* See also [event loop implementations](#loop-implementations) for more details.
|
||||
*
|
||||
* Additionally, periodic timers may be subject to timer drift due to
|
||||
* re-scheduling after each invocation. As such, it's generally not
|
||||
* recommended to rely on this for high precision intervals with millisecond
|
||||
* accuracy or below.
|
||||
*
|
||||
* @param int|float $interval The number of seconds to wait before execution.
|
||||
* @param callable $callback The callback to invoke.
|
||||
*
|
||||
* @return TimerInterface
|
||||
*/
|
||||
public function addPeriodicTimer($interval, $callback);
|
||||
|
||||
/**
|
||||
* Cancel a pending timer.
|
||||
*
|
||||
* See also [`addPeriodicTimer()`](#addperiodictimer) and [example #2](examples).
|
||||
*
|
||||
* Calling this method on a timer instance that has not been added to this
|
||||
* loop instance or on a timer that has already been cancelled has no effect.
|
||||
*
|
||||
* @param TimerInterface $timer The timer to cancel.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function cancelTimer(TimerInterface $timer);
|
||||
|
||||
/**
|
||||
* Schedule a callback to be invoked on a future tick of the event loop.
|
||||
*
|
||||
* This works very much similar to timers with an interval of zero seconds,
|
||||
* but does not require the overhead of scheduling a timer queue.
|
||||
*
|
||||
* The tick callback function MUST be able to accept zero parameters.
|
||||
*
|
||||
* The tick callback function MUST NOT throw an `Exception`.
|
||||
* The return value of the tick callback function will be ignored and has
|
||||
* no effect, so for performance reasons you're recommended to not return
|
||||
* any excessive data structures.
|
||||
*
|
||||
* If you want to access any variables within your callback function, you
|
||||
* can bind arbitrary data to a callback closure like this:
|
||||
*
|
||||
* ```php
|
||||
* function hello($name, LoopInterface $loop)
|
||||
* {
|
||||
* $loop->futureTick(function () use ($name) {
|
||||
* echo "hello $name\n";
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* hello('Tester', $loop);
|
||||
* ```
|
||||
*
|
||||
* Unlike timers, tick callbacks are guaranteed to be executed in the order
|
||||
* they are enqueued.
|
||||
* Also, once a callback is enqueued, there's no way to cancel this operation.
|
||||
*
|
||||
* This is often used to break down bigger tasks into smaller steps (a form
|
||||
* of cooperative multitasking).
|
||||
*
|
||||
* ```php
|
||||
* $loop->futureTick(function () {
|
||||
* echo 'b';
|
||||
* });
|
||||
* $loop->futureTick(function () {
|
||||
* echo 'c';
|
||||
* });
|
||||
* echo 'a';
|
||||
* ```
|
||||
*
|
||||
* See also [example #3](examples).
|
||||
*
|
||||
* @param callable $listener The callback to invoke.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function futureTick($listener);
|
||||
|
||||
/**
|
||||
* Register a listener to be notified when a signal has been caught by this process.
|
||||
*
|
||||
* This is useful to catch user interrupt signals or shutdown signals from
|
||||
* tools like `supervisor` or `systemd`.
|
||||
*
|
||||
* The 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();
|
||||
}
|
63
vendor/react/event-loop/src/SignalsHandler.php
vendored
Normal file
63
vendor/react/event-loop/src/SignalsHandler.php
vendored
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
308
vendor/react/event-loop/src/StreamSelectLoop.php
vendored
Normal file
308
vendor/react/event-loop/src/StreamSelectLoop.php
vendored
Normal file
|
@ -0,0 +1,308 @@
|
|||
<?php
|
||||
|
||||
namespace React\EventLoop;
|
||||
|
||||
use React\EventLoop\Tick\FutureTickQueue;
|
||||
use React\EventLoop\Timer\Timer;
|
||||
use React\EventLoop\Timer\Timers;
|
||||
|
||||
/**
|
||||
* A `stream_select()` based event loop.
|
||||
*
|
||||
* This uses the [`stream_select()`](https://www.php.net/manual/en/function.stream-select.php)
|
||||
* function and is the only implementation 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
|
||||
* when using any version before PHP 7.3, because a monotonic time source is
|
||||
* only available as of PHP 7.3 (`hrtime()`).
|
||||
* While this does not affect many common use cases, this is an important
|
||||
* distinction for programs that rely on a high time precision or on systems
|
||||
* that are subject to discontinuous time adjustments (time jumps).
|
||||
* This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and
|
||||
* then adjust your system time forward by 20s, the timer may trigger in 10s.
|
||||
* See also [`addTimer()`](#addtimer) for more details.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.stream-select.php
|
||||
*/
|
||||
final class StreamSelectLoop implements LoopInterface
|
||||
{
|
||||
/** @internal */
|
||||
const MICROSECONDS_PER_SECOND = 1000000;
|
||||
|
||||
private $futureTickQueue;
|
||||
private $timers;
|
||||
private $readStreams = array();
|
||||
private $readListeners = array();
|
||||
private $writeStreams = array();
|
||||
private $writeListeners = array();
|
||||
private $running;
|
||||
private $pcntl = false;
|
||||
private $pcntlPoll = false;
|
||||
private $signals;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->futureTickQueue = new FutureTickQueue();
|
||||
$this->timers = new Timers();
|
||||
$this->pcntl = \function_exists('pcntl_signal') && \function_exists('pcntl_signal_dispatch');
|
||||
$this->pcntlPoll = $this->pcntl && !\function_exists('pcntl_async_signals');
|
||||
$this->signals = new SignalsHandler();
|
||||
|
||||
// prefer async signals if available (PHP 7.1+) or fall back to dispatching on each tick
|
||||
if ($this->pcntl && !$this->pcntlPoll) {
|
||||
\pcntl_async_signals(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function addReadStream($stream, $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->readStreams[$key])) {
|
||||
$this->readStreams[$key] = $stream;
|
||||
$this->readListeners[$key] = $listener;
|
||||
}
|
||||
}
|
||||
|
||||
public function addWriteStream($stream, $listener)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
if (!isset($this->writeStreams[$key])) {
|
||||
$this->writeStreams[$key] = $stream;
|
||||
$this->writeListeners[$key] = $listener;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeReadStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
unset(
|
||||
$this->readStreams[$key],
|
||||
$this->readListeners[$key]
|
||||
);
|
||||
}
|
||||
|
||||
public function removeWriteStream($stream)
|
||||
{
|
||||
$key = (int) $stream;
|
||||
|
||||
unset(
|
||||
$this->writeStreams[$key],
|
||||
$this->writeListeners[$key]
|
||||
);
|
||||
}
|
||||
|
||||
public function addTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer($interval, $callback, false);
|
||||
|
||||
$this->timers->add($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
public function addPeriodicTimer($interval, $callback)
|
||||
{
|
||||
$timer = new Timer($interval, $callback, true);
|
||||
|
||||
$this->timers->add($timer);
|
||||
|
||||
return $timer;
|
||||
}
|
||||
|
||||
public function cancelTimer(TimerInterface $timer)
|
||||
{
|
||||
$this->timers->cancel($timer);
|
||||
}
|
||||
|
||||
public function futureTick($listener)
|
||||
{
|
||||
$this->futureTickQueue->add($listener);
|
||||
}
|
||||
|
||||
public function addSignal($signal, $listener)
|
||||
{
|
||||
if ($this->pcntl === false) {
|
||||
throw new \BadMethodCallException('Event loop feature "signals" isn\'t supported by the "StreamSelectLoop"');
|
||||
}
|
||||
|
||||
$first = $this->signals->count($signal) === 0;
|
||||
$this->signals->add($signal, $listener);
|
||||
|
||||
if ($first) {
|
||||
\pcntl_signal($signal, array($this->signals, 'call'));
|
||||
}
|
||||
}
|
||||
|
||||
public function removeSignal($signal, $listener)
|
||||
{
|
||||
if (!$this->signals->count($signal)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->signals->remove($signal, $listener);
|
||||
|
||||
if ($this->signals->count($signal) === 0) {
|
||||
\pcntl_signal($signal, \SIG_DFL);
|
||||
}
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
while ($this->running) {
|
||||
$this->futureTickQueue->tick();
|
||||
|
||||
$this->timers->tick();
|
||||
|
||||
// Future-tick queue has pending callbacks ...
|
||||
if (!$this->running || !$this->futureTickQueue->isEmpty()) {
|
||||
$timeout = 0;
|
||||
|
||||
// There is a pending timer, only block until it is due ...
|
||||
} elseif ($scheduledAt = $this->timers->getFirst()) {
|
||||
$timeout = $scheduledAt - $this->timers->getTime();
|
||||
if ($timeout < 0) {
|
||||
$timeout = 0;
|
||||
} else {
|
||||
// Convert float seconds to int microseconds.
|
||||
// Ensure we do not exceed maximum integer size, which may
|
||||
// cause the loop to tick once every ~35min on 32bit systems.
|
||||
$timeout *= self::MICROSECONDS_PER_SECOND;
|
||||
$timeout = $timeout > \PHP_INT_MAX ? \PHP_INT_MAX : (int)$timeout;
|
||||
}
|
||||
|
||||
// The only possible event is stream or signal activity, so wait forever ...
|
||||
} elseif ($this->readStreams || $this->writeStreams || !$this->signals->isEmpty()) {
|
||||
$timeout = null;
|
||||
|
||||
// There's nothing left to do ...
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->waitForStreamActivity($timeout);
|
||||
}
|
||||
}
|
||||
|
||||
public function stop()
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait/check for stream activity, or until the next timer is due.
|
||||
*
|
||||
* @param integer|null $timeout Activity timeout in microseconds, or null to wait forever.
|
||||
*/
|
||||
private function waitForStreamActivity($timeout)
|
||||
{
|
||||
$read = $this->readStreams;
|
||||
$write = $this->writeStreams;
|
||||
|
||||
$available = $this->streamSelect($read, $write, $timeout);
|
||||
if ($this->pcntlPoll) {
|
||||
\pcntl_signal_dispatch();
|
||||
}
|
||||
if (false === $available) {
|
||||
// if a system call has been interrupted,
|
||||
// we cannot rely on it's outcome
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($read as $stream) {
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->readListeners[$key])) {
|
||||
\call_user_func($this->readListeners[$key], $stream);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($write as $stream) {
|
||||
$key = (int) $stream;
|
||||
|
||||
if (isset($this->writeListeners[$key])) {
|
||||
\call_user_func($this->writeListeners[$key], $stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulate a stream_select() implementation that does not break when passed
|
||||
* empty stream arrays.
|
||||
*
|
||||
* @param array $read An array of read streams to select upon.
|
||||
* @param array $write An array of write streams to select upon.
|
||||
* @param int|null $timeout Activity timeout in microseconds, or null to wait forever.
|
||||
*
|
||||
* @return int|false The total number of streams that are ready for read/write.
|
||||
* Can return false if stream_select() is interrupted by a signal.
|
||||
*/
|
||||
private function streamSelect(array &$read, array &$write, $timeout)
|
||||
{
|
||||
if ($read || $write) {
|
||||
// We do not usually use or expose the `exceptfds` parameter passed to the underlying `select`.
|
||||
// However, Windows does not report failed connection attempts in `writefds` passed to `select` like most other platforms.
|
||||
// Instead, it uses `writefds` only for successful connection attempts and `exceptfds` for failed connection attempts.
|
||||
// We work around this by adding all sockets that look like a pending connection attempt to `exceptfds` automatically on Windows and merge it back later.
|
||||
// This ensures the public API matches other loop implementations across all platforms (see also test suite or rather test matrix).
|
||||
// Lacking better APIs, every write-only socket that has not yet read any data is assumed to be in a pending connection attempt state.
|
||||
// @link https://docs.microsoft.com/de-de/windows/win32/api/winsock2/nf-winsock2-select
|
||||
$except = null;
|
||||
if (\DIRECTORY_SEPARATOR === '\\') {
|
||||
$except = array();
|
||||
foreach ($write as $key => $socket) {
|
||||
if (!isset($read[$key]) && @\ftell($socket) === 0) {
|
||||
$except[$key] = $socket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// suppress warnings that occur, when stream_select is interrupted by a signal
|
||||
$ret = @\stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout);
|
||||
|
||||
if ($except) {
|
||||
$write = \array_merge($write, $except);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if ($timeout > 0) {
|
||||
\usleep($timeout);
|
||||
} elseif ($timeout === null) {
|
||||
// wait forever (we only reach this if we're only awaiting signals)
|
||||
// this may be interrupted and return earlier when a signal is received
|
||||
\sleep(PHP_INT_MAX);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
60
vendor/react/event-loop/src/Tick/FutureTickQueue.php
vendored
Normal file
60
vendor/react/event-loop/src/Tick/FutureTickQueue.php
vendored
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
55
vendor/react/event-loop/src/Timer/Timer.php
vendored
Normal file
55
vendor/react/event-loop/src/Timer/Timer.php
vendored
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
107
vendor/react/event-loop/src/Timer/Timers.php
vendored
Normal file
107
vendor/react/event-loop/src/Timer/Timers.php
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace React\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\TimerInterface;
|
||||
|
||||
/**
|
||||
* A scheduler implementation that can hold multiple timer instances
|
||||
*
|
||||
* This class should only be used internally, see TimerInterface instead.
|
||||
*
|
||||
* @see TimerInterface
|
||||
* @internal
|
||||
*/
|
||||
final class Timers
|
||||
{
|
||||
private $time;
|
||||
private $timers = array();
|
||||
private $schedule = array();
|
||||
private $sorted = true;
|
||||
private $useHighResolution;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// prefer high-resolution timer, available as of PHP 7.3+
|
||||
$this->useHighResolution = \function_exists('hrtime');
|
||||
}
|
||||
|
||||
public function updateTime()
|
||||
{
|
||||
return $this->time = $this->useHighResolution ? \hrtime(true) * 1e-9 : \microtime(true);
|
||||
}
|
||||
|
||||
public function getTime()
|
||||
{
|
||||
return $this->time ?: $this->updateTime();
|
||||
}
|
||||
|
||||
public function add(TimerInterface $timer)
|
||||
{
|
||||
$id = \spl_object_hash($timer);
|
||||
$this->timers[$id] = $timer;
|
||||
$this->schedule[$id] = $timer->getInterval() + $this->updateTime();
|
||||
$this->sorted = false;
|
||||
}
|
||||
|
||||
public function contains(TimerInterface $timer)
|
||||
{
|
||||
return isset($this->timers[\spl_object_hash($timer)]);
|
||||
}
|
||||
|
||||
public function cancel(TimerInterface $timer)
|
||||
{
|
||||
$id = \spl_object_hash($timer);
|
||||
unset($this->timers[$id], $this->schedule[$id]);
|
||||
}
|
||||
|
||||
public function getFirst()
|
||||
{
|
||||
// ensure timers are sorted to simply accessing next (first) one
|
||||
if (!$this->sorted) {
|
||||
$this->sorted = true;
|
||||
\asort($this->schedule);
|
||||
}
|
||||
|
||||
return \reset($this->schedule);
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
{
|
||||
return \count($this->timers) === 0;
|
||||
}
|
||||
|
||||
public function tick()
|
||||
{
|
||||
// ensure timers are sorted so we can execute in order
|
||||
if (!$this->sorted) {
|
||||
$this->sorted = true;
|
||||
\asort($this->schedule);
|
||||
}
|
||||
|
||||
$time = $this->updateTime();
|
||||
|
||||
foreach ($this->schedule as $id => $scheduled) {
|
||||
// schedule is ordered, so loop until first timer that is not scheduled for execution now
|
||||
if ($scheduled >= $time) {
|
||||
break;
|
||||
}
|
||||
|
||||
// skip any timers that are removed while we process the current schedule
|
||||
if (!isset($this->schedule[$id]) || $this->schedule[$id] !== $scheduled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$timer = $this->timers[$id];
|
||||
\call_user_func($timer->getCallback(), $timer);
|
||||
|
||||
// re-schedule if this is a periodic timer and it has not been cancelled explicitly already
|
||||
if ($timer->isPeriodic() && isset($this->timers[$id])) {
|
||||
$this->schedule[$id] = $timer->getInterval() + $time;
|
||||
$this->sorted = false;
|
||||
} else {
|
||||
unset($this->timers[$id], $this->schedule[$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
vendor/react/event-loop/src/TimerInterface.php
vendored
Normal file
27
vendor/react/event-loop/src/TimerInterface.php
vendored
Normal file
|
@ -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();
|
||||
}
|
786
vendor/react/event-loop/tests/AbstractLoopTest.php
vendored
Normal file
786
vendor/react/event-loop/tests/AbstractLoopTest.php
vendored
Normal file
|
@ -0,0 +1,786 @@
|
|||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
use React\EventLoop\StreamSelectLoop;
|
||||
use React\EventLoop\ExtUvLoop;
|
||||
|
||||
abstract class AbstractLoopTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \React\EventLoop\LoopInterface
|
||||
*/
|
||||
protected $loop;
|
||||
|
||||
private $tickTimeout;
|
||||
|
||||
const PHP_DEFAULT_CHUNK_SIZE = 8192;
|
||||
|
||||
/**
|
||||
* @before
|
||||
*/
|
||||
public function setUpLoop()
|
||||
{
|
||||
// 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 testAddReadStreamTriggersWhenSocketReceivesData()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
$loop = $this->loop;
|
||||
$timeout = $loop->addTimer(0.1, function () use ($input, $loop) {
|
||||
$loop->removeReadStream($input);
|
||||
});
|
||||
|
||||
$called = 0;
|
||||
$this->loop->addReadStream($input, function () use (&$called, $loop, $input, $timeout) {
|
||||
++$called;
|
||||
$loop->removeReadStream($input);
|
||||
$loop->cancelTimer($timeout);
|
||||
});
|
||||
|
||||
fwrite($output, "foo\n");
|
||||
|
||||
$this->loop->run();
|
||||
|
||||
$this->assertEquals(1, $called);
|
||||
}
|
||||
|
||||
public function testAddReadStreamTriggersWhenSocketCloses()
|
||||
{
|
||||
list ($input, $output) = $this->createSocketPair();
|
||||
|
||||
$loop = $this->loop;
|
||||
$timeout = $loop->addTimer(0.1, function () use ($input, $loop) {
|
||||
$loop->removeReadStream($input);
|
||||
});
|
||||
|
||||
$called = 0;
|
||||
$this->loop->addReadStream($input, function () use (&$called, $loop, $input, $timeout) {
|
||||
++$called;
|
||||
$loop->removeReadStream($input);
|
||||
$loop->cancelTimer($timeout);
|
||||
});
|
||||
|
||||
fclose($output);
|
||||
|
||||
$this->loop->run();
|
||||
|
||||
$this->assertEquals(1, $called);
|
||||
}
|
||||
|
||||
public function testAddWriteStreamTriggersWhenSocketConnectionSucceeds()
|
||||
{
|
||||
$server = stream_socket_server('127.0.0.1:0');
|
||||
|
||||
$errno = $errstr = null;
|
||||
$connecting = stream_socket_client(stream_socket_get_name($server, false), $errno, $errstr, 0, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT);
|
||||
|
||||
$loop = $this->loop;
|
||||
$timeout = $loop->addTimer(0.1, function () use ($connecting, $loop) {
|
||||
$loop->removeWriteStream($connecting);
|
||||
});
|
||||
|
||||
$called = 0;
|
||||
$this->loop->addWriteStream($connecting, function () use (&$called, $loop, $connecting, $timeout) {
|
||||
++$called;
|
||||
$loop->removeWriteStream($connecting);
|
||||
$loop->cancelTimer($timeout);
|
||||
});
|
||||
|
||||
$this->loop->run();
|
||||
|
||||
$this->assertEquals(1, $called);
|
||||
}
|
||||
|
||||
public function testAddWriteStreamTriggersWhenSocketConnectionRefused()
|
||||
{
|
||||
if (defined('HHVM_VERSION')) {
|
||||
$this->markTestSkipped('Not supported on HHVM');
|
||||
}
|
||||
|
||||
// first verify the operating system actually refuses the connection and no firewall is in place
|
||||
// use higher timeout because Windows retires multiple times and has a noticeable delay
|
||||
// @link https://stackoverflow.com/questions/19440364/why-do-failed-attempts-of-socket-connect-take-1-sec-on-windows
|
||||
$errno = $errstr = null;
|
||||
if (@stream_socket_client('127.0.0.1:1', $errno, $errstr, 10.0) !== false || (defined('SOCKET_ECONNREFUSED') && $errno !== SOCKET_ECONNREFUSED)) {
|
||||
$this->markTestSkipped('Expected host to refuse connection, but got error ' . $errno . ': ' . $errstr);
|
||||
}
|
||||
|
||||
$connecting = stream_socket_client('127.0.0.1:1', $errno, $errstr, 0, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT);
|
||||
|
||||
$loop = $this->loop;
|
||||
$timeout = $loop->addTimer(10.0, function () use ($connecting, $loop) {
|
||||
$loop->removeWriteStream($connecting);
|
||||
});
|
||||
|
||||
$called = 0;
|
||||
$this->loop->addWriteStream($connecting, function () use (&$called, $loop, $connecting, $timeout) {
|
||||
++$called;
|
||||
$loop->removeWriteStream($connecting);
|
||||
$loop->cancelTimer($timeout);
|
||||
});
|
||||
|
||||
$this->loop->run();
|
||||
|
||||
$this->assertEquals(1, $called);
|
||||
}
|
||||
|
||||
public function testAddReadStream()
|
||||
{
|
||||
if ($this->loop instanceof ExtUvLoop && DIRECTORY_SEPARATOR === '\\') {
|
||||
$this->markTestIncomplete('Ticking ExtUvLoop not supported on Windows');
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if ($this->loop instanceof ExtUvLoop && DIRECTORY_SEPARATOR === '\\') {
|
||||
$this->markTestIncomplete('Ticking ExtUvLoop not supported on Windows');
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if ($this->loop instanceof ExtUvLoop && DIRECTORY_SEPARATOR === '\\') {
|
||||
$this->markTestIncomplete('Ticking ExtUvLoop not supported on Windows');
|
||||
}
|
||||
|
||||
list ($input) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream($input, $this->expectCallableExactly(2));
|
||||
$this->tickLoop($this->loop);
|
||||
$this->tickLoop($this->loop);
|
||||
}
|
||||
|
||||
public function testAddWriteStreamIgnoresSecondCallable()
|
||||
{
|
||||
if ($this->loop instanceof ExtUvLoop && DIRECTORY_SEPARATOR === '\\') {
|
||||
$this->markTestIncomplete('Ticking ExtUvLoop not supported on Windows');
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if ($this->loop instanceof ExtUvLoop && DIRECTORY_SEPARATOR === '\\') {
|
||||
$this->markTestIncomplete('Ticking ExtUvLoop not supported on Windows');
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if ($this->loop instanceof ExtUvLoop && DIRECTORY_SEPARATOR === '\\') {
|
||||
$this->markTestIncomplete('Ticking ExtUvLoop not supported on Windows');
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if ($this->loop instanceof ExtUvLoop && DIRECTORY_SEPARATOR === '\\') {
|
||||
$this->markTestIncomplete('Ticking ExtUvLoop not supported on Windows');
|
||||
}
|
||||
|
||||
list ($input) = $this->createSocketPair();
|
||||
|
||||
$this->loop->addWriteStream($input, $this->expectCallableNever());
|
||||
$this->loop->removeWriteStream($input);
|
||||
$this->tickLoop($this->loop);
|
||||
}
|
||||
|
||||
public function testRemoveWriteStreamAfterWriting()
|
||||
{
|
||||
if ($this->loop instanceof ExtUvLoop && DIRECTORY_SEPARATOR === '\\') {
|
||||
$this->markTestIncomplete('Ticking ExtUvLoop not supported on Windows');
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if ($this->loop instanceof ExtUvLoop && DIRECTORY_SEPARATOR === '\\') {
|
||||
$this->markTestIncomplete('Ticking ExtUvLoop not supported on Windows');
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if ($this->loop instanceof ExtUvLoop && DIRECTORY_SEPARATOR === '\\') {
|
||||
$this->markTestIncomplete('Ticking ExtUvLoop not supported on Windows');
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if ($this->loop instanceof ExtUvLoop && DIRECTORY_SEPARATOR === '\\') {
|
||||
$this->markTestIncomplete('Ticking ExtUvLoop not supported on Windows');
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFutureTickFiresBeforeIO
|
||||
*/
|
||||
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(2, function () { });
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
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.4);
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
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()
|
||||
{
|
||||
// Maximum interval for ExtUvLoop implementation
|
||||
$interval = ((int) (PHP_INT_MAX / 1000)) - 1;
|
||||
$loop = $this->loop;
|
||||
// start a timer very far in the future
|
||||
$timer = $this->loop->addTimer($interval, function () { });
|
||||
|
||||
$this->loop->futureTick(function () use ($timer, $loop) {
|
||||
$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);
|
||||
}
|
||||
}
|
75
vendor/react/event-loop/tests/BinTest.php
vendored
Normal file
75
vendor/react/event-loop/tests/BinTest.php
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
class BinTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @before
|
||||
*/
|
||||
public function setUpBin()
|
||||
{
|
||||
if (!defined('PHP_BINARY') || defined('HHVM_VERSION')) {
|
||||
$this->markTestSkipped('Tests not supported on legacy PHP 5.3 or HHVM');
|
||||
}
|
||||
|
||||
chdir(__DIR__ . '/bin/');
|
||||
}
|
||||
|
||||
public function testExecuteExampleWithoutLoopRunRunsLoopAndExecutesTicks()
|
||||
{
|
||||
$output = exec(escapeshellarg(PHP_BINARY) . ' 01-ticks-loop-class.php');
|
||||
|
||||
$this->assertEquals('abc', $output);
|
||||
}
|
||||
|
||||
public function testExecuteExampleWithExplicitLoopRunRunsLoopAndExecutesTicks()
|
||||
{
|
||||
$output = exec(escapeshellarg(PHP_BINARY) . ' 02-ticks-loop-instance.php');
|
||||
|
||||
$this->assertEquals('abc', $output);
|
||||
}
|
||||
|
||||
public function testExecuteExampleWithExplicitLoopRunAndStopRunsLoopAndExecutesTicksUntilStopped()
|
||||
{
|
||||
$output = exec(escapeshellarg(PHP_BINARY) . ' 03-ticks-loop-stop.php');
|
||||
|
||||
$this->assertEquals('abc', $output);
|
||||
}
|
||||
|
||||
public function testExecuteExampleWithUncaughtExceptionShouldNotRunLoop()
|
||||
{
|
||||
$time = microtime(true);
|
||||
exec(escapeshellarg(PHP_BINARY) . ' 11-uncaught.php 2>/dev/null');
|
||||
$time = microtime(true) - $time;
|
||||
|
||||
$this->assertLessThan(1.0, $time);
|
||||
}
|
||||
|
||||
public function testExecuteExampleWithUndefinedVariableShouldNotRunLoop()
|
||||
{
|
||||
$time = microtime(true);
|
||||
exec(escapeshellarg(PHP_BINARY) . ' 12-undefined.php 2>/dev/null');
|
||||
$time = microtime(true) - $time;
|
||||
|
||||
$this->assertLessThan(1.0, $time);
|
||||
}
|
||||
|
||||
public function testExecuteExampleWithExplicitStopShouldNotRunLoop()
|
||||
{
|
||||
$time = microtime(true);
|
||||
exec(escapeshellarg(PHP_BINARY) . ' 21-stop.php 2>/dev/null');
|
||||
$time = microtime(true) - $time;
|
||||
|
||||
$this->assertLessThan(1.0, $time);
|
||||
}
|
||||
|
||||
public function testExecuteExampleWithExplicitStopInExceptionHandlerShouldNotRunLoop()
|
||||
{
|
||||
$time = microtime(true);
|
||||
exec(escapeshellarg(PHP_BINARY) . ' 22-uncaught-stop.php 2>/dev/null');
|
||||
$time = microtime(true) - $time;
|
||||
|
||||
$this->assertLessThan(1.0, $time);
|
||||
}
|
||||
}
|
17
vendor/react/event-loop/tests/ExtEvLoopTest.php
vendored
Normal file
17
vendor/react/event-loop/tests/ExtEvLoopTest.php
vendored
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
84
vendor/react/event-loop/tests/ExtEventLoopTest.php
vendored
Normal file
84
vendor/react/event-loop/tests/ExtEventLoopTest.php
vendored
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
22
vendor/react/event-loop/tests/ExtLibevLoopTest.php
vendored
Normal file
22
vendor/react/event-loop/tests/ExtLibevLoopTest.php
vendored
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
61
vendor/react/event-loop/tests/ExtLibeventLoopTest.php
vendored
Normal file
61
vendor/react/event-loop/tests/ExtLibeventLoopTest.php
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @after
|
||||
*/
|
||||
public function tearDownFile()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
95
vendor/react/event-loop/tests/ExtUvLoopTest.php
vendored
Normal file
95
vendor/react/event-loop/tests/ExtUvLoopTest.php
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
use React\EventLoop\ExtUvLoop;
|
||||
|
||||
class ExtUvLoopTest extends AbstractLoopTest
|
||||
{
|
||||
public function createLoop()
|
||||
{
|
||||
if (!function_exists('uv_loop_new')) {
|
||||
$this->markTestSkipped('uv tests skipped because ext-uv is not installed.');
|
||||
}
|
||||
|
||||
return new ExtUvLoop();
|
||||
}
|
||||
|
||||
/** @dataProvider intervalProvider */
|
||||
public function testTimerInterval($interval, $expectedExceptionMessage)
|
||||
{
|
||||
$this->expectException('InvalidArgumentException');
|
||||
$this->expectExceptionMessage($expectedExceptionMessage);
|
||||
|
||||
$this->loop
|
||||
->addTimer(
|
||||
$interval,
|
||||
function () {
|
||||
return 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public function intervalProvider()
|
||||
{
|
||||
$oversizeInterval = PHP_INT_MAX / 1000;
|
||||
$maxValue = (int) (PHP_INT_MAX / 1000);
|
||||
$oneMaxValue = $maxValue + 1;
|
||||
$tenMaxValue = $maxValue + 10;
|
||||
$tenMillionsMaxValue = $maxValue + 10000000;
|
||||
$intMax = PHP_INT_MAX;
|
||||
$oneIntMax = PHP_INT_MAX + 1;
|
||||
$tenIntMax = PHP_INT_MAX + 10;
|
||||
$oneHundredIntMax = PHP_INT_MAX + 100;
|
||||
$oneThousandIntMax = PHP_INT_MAX + 1000;
|
||||
$tenMillionsIntMax = PHP_INT_MAX + 10000000;
|
||||
$tenThousandsTimesIntMax = PHP_INT_MAX * 1000;
|
||||
|
||||
return array(
|
||||
array(
|
||||
$oversizeInterval,
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$oversizeInterval}' passed."
|
||||
),
|
||||
array(
|
||||
$oneMaxValue,
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$oneMaxValue}' passed.",
|
||||
),
|
||||
array(
|
||||
$tenMaxValue,
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$tenMaxValue}' passed.",
|
||||
),
|
||||
array(
|
||||
$tenMillionsMaxValue,
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$tenMillionsMaxValue}' passed.",
|
||||
),
|
||||
array(
|
||||
$intMax,
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$intMax}' passed.",
|
||||
),
|
||||
array(
|
||||
$oneIntMax,
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$oneIntMax}' passed.",
|
||||
),
|
||||
array(
|
||||
$tenIntMax,
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$tenIntMax}' passed.",
|
||||
),
|
||||
array(
|
||||
$oneHundredIntMax,
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$oneHundredIntMax}' passed.",
|
||||
),
|
||||
array(
|
||||
$oneThousandIntMax,
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$oneThousandIntMax}' passed.",
|
||||
),
|
||||
array(
|
||||
$tenMillionsIntMax,
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$tenMillionsIntMax}' passed.",
|
||||
),
|
||||
array(
|
||||
$tenThousandsTimesIntMax,
|
||||
"Interval overflow, value must be lower than '{$maxValue}', but '{$tenThousandsTimesIntMax}' passed.",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
216
vendor/react/event-loop/tests/LoopTest.php
vendored
Normal file
216
vendor/react/event-loop/tests/LoopTest.php
vendored
Normal file
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
use React\EventLoop\Factory;
|
||||
use React\EventLoop\Loop;
|
||||
use ReflectionClass;
|
||||
|
||||
final class LoopTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider numberOfTests
|
||||
*/
|
||||
public function testFactoryCreateSetsEventLoopOnLoopAccessor()
|
||||
{
|
||||
$factoryLoop = Factory::create();
|
||||
$accessorLoop = Loop::get();
|
||||
|
||||
self::assertSame($factoryLoop, $accessorLoop);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider numberOfTests
|
||||
*/
|
||||
public function testCallingFactoryAfterCallingLoopGetYieldsADifferentInstanceOfTheEventLoop()
|
||||
{
|
||||
// Note that this behavior isn't wise and highly advised against. Always used Loop::get.
|
||||
$accessorLoop = Loop::get();
|
||||
$factoryLoop = Factory::create();
|
||||
|
||||
self::assertNotSame($factoryLoop, $accessorLoop);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider numberOfTests
|
||||
*/
|
||||
public function testCallingLoopGetShouldAlwaysReturnTheSameEventLoop()
|
||||
{
|
||||
self::assertSame(Loop::get(), Loop::get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Run several tests several times to ensure we reset the loop between tests and code is still behavior as expected.
|
||||
*
|
||||
* @return array<array>
|
||||
*/
|
||||
public function numberOfTests()
|
||||
{
|
||||
return array(array(), array(), array());
|
||||
}
|
||||
|
||||
public function testStaticAddReadStreamCallsAddReadStreamOnLoopInstance()
|
||||
{
|
||||
$stream = tmpfile();
|
||||
$listener = function () { };
|
||||
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('addReadStream')->with($stream, $listener);
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
Loop::addReadStream($stream, $listener);
|
||||
}
|
||||
|
||||
public function testStaticAddWriteStreamCallsAddWriteStreamOnLoopInstance()
|
||||
{
|
||||
$stream = tmpfile();
|
||||
$listener = function () { };
|
||||
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('addWriteStream')->with($stream, $listener);
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
Loop::addWriteStream($stream, $listener);
|
||||
}
|
||||
|
||||
public function testStaticRemoveReadStreamCallsRemoveReadStreamOnLoopInstance()
|
||||
{
|
||||
$stream = tmpfile();
|
||||
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('removeReadStream')->with($stream);
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
Loop::removeReadStream($stream);
|
||||
}
|
||||
|
||||
public function testStaticRemoveWriteStreamCallsRemoveWriteStreamOnLoopInstance()
|
||||
{
|
||||
$stream = tmpfile();
|
||||
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('removeWriteStream')->with($stream);
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
Loop::removeWriteStream($stream);
|
||||
}
|
||||
|
||||
public function testStaticAddTimerCallsAddTimerOnLoopInstanceAndReturnsTimerInstance()
|
||||
{
|
||||
$interval = 1.0;
|
||||
$callback = function () { };
|
||||
$timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
|
||||
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('addTimer')->with($interval, $callback)->willReturn($timer);
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
$ret = Loop::addTimer($interval, $callback);
|
||||
|
||||
$this->assertSame($timer, $ret);
|
||||
}
|
||||
|
||||
public function testStaticAddPeriodicTimerCallsAddPeriodicTimerOnLoopInstanceAndReturnsTimerInstance()
|
||||
{
|
||||
$interval = 1.0;
|
||||
$callback = function () { };
|
||||
$timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
|
||||
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('addPeriodicTimer')->with($interval, $callback)->willReturn($timer);
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
$ret = Loop::addPeriodicTimer($interval, $callback);
|
||||
|
||||
$this->assertSame($timer, $ret);
|
||||
}
|
||||
|
||||
public function testStaticCancelTimerCallsCancelTimerOnLoopInstance()
|
||||
{
|
||||
$timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
|
||||
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('cancelTimer')->with($timer);
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
Loop::cancelTimer($timer);
|
||||
}
|
||||
|
||||
public function testStaticFutureTickCallsFutureTickOnLoopInstance()
|
||||
{
|
||||
$listener = function () { };
|
||||
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('futureTick')->with($listener);
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
Loop::futureTick($listener);
|
||||
}
|
||||
|
||||
public function testStaticAddSignalCallsAddSignalOnLoopInstance()
|
||||
{
|
||||
$signal = 1;
|
||||
$listener = function () { };
|
||||
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('addSignal')->with($signal, $listener);
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
Loop::addSignal($signal, $listener);
|
||||
}
|
||||
|
||||
public function testStaticRemoveSignalCallsRemoveSignalOnLoopInstance()
|
||||
{
|
||||
$signal = 1;
|
||||
$listener = function () { };
|
||||
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('removeSignal')->with($signal, $listener);
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
Loop::removeSignal($signal, $listener);
|
||||
}
|
||||
|
||||
public function testStaticRunCallsRunOnLoopInstance()
|
||||
{
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('run')->with();
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
Loop::run();
|
||||
}
|
||||
|
||||
public function testStaticStopCallsStopOnLoopInstance()
|
||||
{
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
|
||||
$loop->expects($this->once())->method('stop')->with();
|
||||
|
||||
Loop::set($loop);
|
||||
|
||||
Loop::stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @after
|
||||
* @before
|
||||
*/
|
||||
public function unsetLoopFromLoopAccessor()
|
||||
{
|
||||
$ref = new ReflectionClass('\React\EventLoop\Loop');
|
||||
$prop = $ref->getProperty('instance');
|
||||
$prop->setAccessible(true);
|
||||
$prop->setValue(null);
|
||||
$prop->setAccessible(false);
|
||||
}
|
||||
}
|
58
vendor/react/event-loop/tests/SignalsHandlerTest.php
vendored
Normal file
58
vendor/react/event-loop/tests/SignalsHandlerTest.php
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
use React\EventLoop\SignalsHandler;
|
||||
|
||||
final class SignalsHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
145
vendor/react/event-loop/tests/StreamSelectLoopTest.php
vendored
Normal file
145
vendor/react/event-loop/tests/StreamSelectLoopTest.php
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop;
|
||||
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\EventLoop\StreamSelectLoop;
|
||||
|
||||
class StreamSelectLoopTest extends AbstractLoopTest
|
||||
{
|
||||
/**
|
||||
* @after
|
||||
*/
|
||||
protected function tearDownSignalHandlers()
|
||||
{
|
||||
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
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
public function testSignalInterruptNoStream($signal)
|
||||
{
|
||||
// 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
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
public function testSignalInterruptWithStream($signal)
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
59
vendor/react/event-loop/tests/TestCase.php
vendored
Normal file
59
vendor/react/event-loop/tests/TestCase.php
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?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()
|
||||
{
|
||||
if (method_exists('PHPUnit\Framework\MockObject\MockBuilder', 'addMethods')) {
|
||||
// PHPUnit 9+
|
||||
return $this->getMockBuilder('stdClass')->addMethods(array('__invoke'))->getMock();
|
||||
} else {
|
||||
// legacy PHPUnit 4 - PHPUnit 9
|
||||
return $this->getMockBuilder('stdClass')->setMethods(array('__invoke'))->getMock();
|
||||
}
|
||||
}
|
||||
|
||||
protected function tickLoop(LoopInterface $loop)
|
||||
{
|
||||
$loop->futureTick(function () use ($loop) {
|
||||
$loop->stop();
|
||||
});
|
||||
|
||||
$loop->run();
|
||||
}
|
||||
}
|
141
vendor/react/event-loop/tests/Timer/AbstractTimerTest.php
vendored
Normal file
141
vendor/react/event-loop/tests/Timer/AbstractTimerTest.php
vendored
Normal file
|
@ -0,0 +1,141 @@
|
|||
<?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.005, $this->expectCallableOnce());
|
||||
|
||||
$start = microtime(true);
|
||||
$loop->run();
|
||||
$end = microtime(true);
|
||||
|
||||
// 1 invocation should take 5ms (± a few milliseconds due to timer inaccuracies)
|
||||
// make no strict assumptions about time interval, must at least take 1ms
|
||||
// and should not take longer than 0.1s for slower loops.
|
||||
$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.350, 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.1, 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 100 times and should at least be
|
||||
// invoked 4 times for really slow loops
|
||||
$this->assertLessThanOrEqual(100, $i);
|
||||
$this->assertGreaterThanOrEqual(4, $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);
|
||||
|
||||
// 5 invocations should take 5ms (± 1ms due to timer inaccuracies)
|
||||
// make no strict assumptions about time interval, must at least take 4ms
|
||||
// and should not take longer than 0.2s for slower loops.
|
||||
$this->assertGreaterThanOrEqual(0.004, $end - $start);
|
||||
$this->assertLessThan(0.2, $end - $start);
|
||||
}
|
||||
|
||||
public function testMinimumIntervalOneMicrosecond()
|
||||
{
|
||||
$loop = $this->createLoop();
|
||||
|
||||
$timer = $loop->addTimer(0, function () {});
|
||||
|
||||
$this->assertEquals(0.000001, $timer->getInterval());
|
||||
}
|
||||
|
||||
public function testTimerIntervalBelowZeroRunsImmediately()
|
||||
{
|
||||
$loop = $this->createLoop();
|
||||
$start = 0;
|
||||
$loop->addTimer(
|
||||
-1,
|
||||
function () use (&$start) {
|
||||
$start = \microtime(true);
|
||||
}
|
||||
);
|
||||
|
||||
$loop->run();
|
||||
$end = \microtime(true);
|
||||
|
||||
// 1ms should be enough even on slow machines (± 1ms due to timer inaccuracies)
|
||||
$this->assertLessThan(0.002, $end - $start);
|
||||
}
|
||||
}
|
17
vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php
vendored
Normal file
17
vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php
vendored
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
17
vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php
vendored
Normal file
17
vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\ExtEventLoop;
|
||||
|
||||
class ExtEventTimerTest extends AbstractTimerTest
|
||||
{
|
||||
public function createLoop()
|
||||
{
|
||||
if (!extension_loaded('event')) {
|
||||
$this->markTestSkipped('ext-event tests skipped because ext-event is not installed.');
|
||||
}
|
||||
|
||||
return new ExtEventLoop();
|
||||
}
|
||||
}
|
17
vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php
vendored
Normal file
17
vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php
vendored
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
17
vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php
vendored
Normal file
17
vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php
vendored
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
17
vendor/react/event-loop/tests/Timer/ExtUvTimerTest.php
vendored
Normal file
17
vendor/react/event-loop/tests/Timer/ExtUvTimerTest.php
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\ExtUvLoop;
|
||||
|
||||
class ExtUvTimerTest extends AbstractTimerTest
|
||||
{
|
||||
public function createLoop()
|
||||
{
|
||||
if (!function_exists('uv_loop_new')) {
|
||||
$this->markTestSkipped('uv tests skipped because ext-uv is not installed.');
|
||||
}
|
||||
|
||||
return new ExtUvLoop();
|
||||
}
|
||||
}
|
13
vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php
vendored
Normal file
13
vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\StreamSelectLoop;
|
||||
|
||||
class StreamSelectTimerTest extends AbstractTimerTest
|
||||
{
|
||||
public function createLoop()
|
||||
{
|
||||
return new StreamSelectLoop();
|
||||
}
|
||||
}
|
40
vendor/react/event-loop/tests/Timer/TimersTest.php
vendored
Normal file
40
vendor/react/event-loop/tests/Timer/TimersTest.php
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?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);
|
||||
}
|
||||
|
||||
public function testContains()
|
||||
{
|
||||
$timers = new Timers();
|
||||
|
||||
$timer1 = new Timer(0.1, function () {});
|
||||
$timer2 = new Timer(0.1, function () {});
|
||||
|
||||
$timers->add($timer1);
|
||||
|
||||
self::assertTrue($timers->contains($timer1));
|
||||
self::assertFalse($timers->contains($timer2));
|
||||
}
|
||||
}
|
13
vendor/react/event-loop/tests/bin/01-ticks-loop-class.php
vendored
Normal file
13
vendor/react/event-loop/tests/bin/01-ticks-loop-class.php
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
Loop::futureTick(function () {
|
||||
echo 'b';
|
||||
});
|
||||
Loop::futureTick(function () {
|
||||
echo 'c';
|
||||
});
|
||||
echo 'a';
|
19
vendor/react/event-loop/tests/bin/02-ticks-loop-instance.php
vendored
Normal file
19
vendor/react/event-loop/tests/bin/02-ticks-loop-instance.php
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
$loop = Loop::get();
|
||||
|
||||
$loop->futureTick(function () {
|
||||
echo 'b';
|
||||
});
|
||||
|
||||
$loop->futureTick(function () {
|
||||
echo 'c';
|
||||
});
|
||||
|
||||
echo 'a';
|
||||
|
||||
$loop->run();
|
23
vendor/react/event-loop/tests/bin/03-ticks-loop-stop.php
vendored
Normal file
23
vendor/react/event-loop/tests/bin/03-ticks-loop-stop.php
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
$loop = Loop::get();
|
||||
|
||||
$loop->futureTick(function () use ($loop) {
|
||||
echo 'b';
|
||||
|
||||
$loop->stop();
|
||||
|
||||
$loop->futureTick(function () {
|
||||
echo 'never';
|
||||
});
|
||||
});
|
||||
|
||||
echo 'a';
|
||||
|
||||
$loop->run();
|
||||
|
||||
echo 'c';
|
11
vendor/react/event-loop/tests/bin/11-uncaught.php
vendored
Normal file
11
vendor/react/event-loop/tests/bin/11-uncaught.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
Loop::addTimer(10.0, function () {
|
||||
echo 'never';
|
||||
});
|
||||
|
||||
throw new RuntimeException();
|
11
vendor/react/event-loop/tests/bin/12-undefined.php
vendored
Normal file
11
vendor/react/event-loop/tests/bin/12-undefined.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
Loop::get()->addTimer(10.0, function () {
|
||||
echo 'never';
|
||||
});
|
||||
|
||||
$undefined->foo('bar');
|
11
vendor/react/event-loop/tests/bin/21-stop.php
vendored
Normal file
11
vendor/react/event-loop/tests/bin/21-stop.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
Loop::addTimer(10.0, function () {
|
||||
echo 'never';
|
||||
});
|
||||
|
||||
Loop::stop();
|
16
vendor/react/event-loop/tests/bin/22-stop-uncaught.php
vendored
Normal file
16
vendor/react/event-loop/tests/bin/22-stop-uncaught.php
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
Loop::addTimer(10.0, function () {
|
||||
echo 'never';
|
||||
});
|
||||
|
||||
set_exception_handler(function (Exception $e) {
|
||||
echo 'Uncaught error occured' . PHP_EOL;
|
||||
Loop::stop();
|
||||
});
|
||||
|
||||
throw new RuntimeException();
|
Loading…
Add table
Add a link
Reference in a new issue