mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-04 18:29:39 +02:00
Added vendor directory to source control
This commit is contained in:
parent
16534d4217
commit
d2589e176f
3049 changed files with 249997 additions and 395005 deletions
13
vendor/react/event-loop/examples/01-timers.php
vendored
13
vendor/react/event-loop/examples/01-timers.php
vendored
|
@ -1,13 +0,0 @@
|
|||
<?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
14
vendor/react/event-loop/examples/02-periodic.php
vendored
|
@ -1,14 +0,0 @@
|
|||
<?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
13
vendor/react/event-loop/examples/03-ticks.php
vendored
|
@ -1,13 +0,0 @@
|
|||
<?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
17
vendor/react/event-loop/examples/04-signals.php
vendored
|
@ -1,17 +0,0 @@
|
|||
<?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;
|
|
@ -1,26 +0,0 @@
|
|||
<?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;
|
||||
});
|
|
@ -1,39 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
});
|
|
@ -1,31 +0,0 @@
|
|||
<?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;
|
||||
});
|
|
@ -1,60 +0,0 @@
|
|||
<?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;
|
||||
});
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
<?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";
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
<?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 () { });
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?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 () { });
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?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();
|
|
@ -1,18 +0,0 @@
|
|||
<?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();
|
|
@ -1,67 +0,0 @@
|
|||
<?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
19
vendor/react/event-loop/phpunit.xml.dist
vendored
|
@ -1,19 +0,0 @@
|
|||
<?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
18
vendor/react/event-loop/phpunit.xml.legacy
vendored
|
@ -1,18 +0,0 @@
|
|||
<?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>
|
506
vendor/react/event-loop/src/ExtEvLoop.php
vendored
506
vendor/react/event-loop/src/ExtEvLoop.php
vendored
|
@ -1,253 +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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
550
vendor/react/event-loop/src/ExtEventLoop.php
vendored
550
vendor/react/event-loop/src/ExtEventLoop.php
vendored
|
@ -1,275 +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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
402
vendor/react/event-loop/src/ExtLibevLoop.php
vendored
402
vendor/react/event-loop/src/ExtLibevLoop.php
vendored
|
@ -1,201 +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;
|
||||
}
|
||||
}
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
|
570
vendor/react/event-loop/src/ExtLibeventLoop.php
vendored
570
vendor/react/event-loop/src/ExtLibeventLoop.php
vendored
|
@ -1,285 +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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
684
vendor/react/event-loop/src/ExtUvLoop.php
vendored
684
vendor/react/event-loop/src/ExtUvLoop.php
vendored
|
@ -1,342 +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);
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
|
|
150
vendor/react/event-loop/src/Factory.php
vendored
150
vendor/react/event-loop/src/Factory.php
vendored
|
@ -1,75 +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
|
||||
}
|
||||
}
|
||||
<?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
|
||||
}
|
||||
}
|
||||
|
|
450
vendor/react/event-loop/src/Loop.php
vendored
450
vendor/react/event-loop/src/Loop.php
vendored
|
@ -1,225 +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();
|
||||
}
|
||||
}
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
|
|
926
vendor/react/event-loop/src/LoopInterface.php
vendored
926
vendor/react/event-loop/src/LoopInterface.php
vendored
|
@ -1,463 +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();
|
||||
}
|
||||
<?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();
|
||||
}
|
||||
|
|
126
vendor/react/event-loop/src/SignalsHandler.php
vendored
126
vendor/react/event-loop/src/SignalsHandler.php
vendored
|
@ -1,63 +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;
|
||||
}
|
||||
}
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
|
616
vendor/react/event-loop/src/StreamSelectLoop.php
vendored
616
vendor/react/event-loop/src/StreamSelectLoop.php
vendored
|
@ -1,308 +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;
|
||||
}
|
||||
}
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
|
120
vendor/react/event-loop/src/Tick/FutureTickQueue.php
vendored
120
vendor/react/event-loop/src/Tick/FutureTickQueue.php
vendored
|
@ -1,60 +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();
|
||||
}
|
||||
}
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
|
|
110
vendor/react/event-loop/src/Timer/Timer.php
vendored
110
vendor/react/event-loop/src/Timer/Timer.php
vendored
|
@ -1,55 +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;
|
||||
}
|
||||
}
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
|
214
vendor/react/event-loop/src/Timer/Timers.php
vendored
214
vendor/react/event-loop/src/Timer/Timers.php
vendored
|
@ -1,107 +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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
54
vendor/react/event-loop/src/TimerInterface.php
vendored
54
vendor/react/event-loop/src/TimerInterface.php
vendored
|
@ -1,27 +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();
|
||||
}
|
||||
<?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
786
vendor/react/event-loop/tests/AbstractLoopTest.php
vendored
|
@ -1,786 +0,0 @@
|
|||
<?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
75
vendor/react/event-loop/tests/BinTest.php
vendored
|
@ -1,75 +0,0 @@
|
|||
<?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
17
vendor/react/event-loop/tests/ExtEvLoopTest.php
vendored
|
@ -1,17 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
<?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
95
vendor/react/event-loop/tests/ExtUvLoopTest.php
vendored
|
@ -1,95 +0,0 @@
|
|||
<?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
216
vendor/react/event-loop/tests/LoopTest.php
vendored
|
@ -1,216 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
<?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
59
vendor/react/event-loop/tests/TestCase.php
vendored
|
@ -1,59 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace React\Tests\EventLoop\Timer;
|
||||
|
||||
use React\EventLoop\StreamSelectLoop;
|
||||
|
||||
class StreamSelectTimerTest extends AbstractTimerTest
|
||||
{
|
||||
public function createLoop()
|
||||
{
|
||||
return new StreamSelectLoop();
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
<?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));
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
Loop::futureTick(function () {
|
||||
echo 'b';
|
||||
});
|
||||
Loop::futureTick(function () {
|
||||
echo 'c';
|
||||
});
|
||||
echo 'a';
|
|
@ -1,19 +0,0 @@
|
|||
<?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();
|
|
@ -1,23 +0,0 @@
|
|||
<?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';
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
Loop::addTimer(10.0, function () {
|
||||
echo 'never';
|
||||
});
|
||||
|
||||
throw new RuntimeException();
|
|
@ -1,11 +0,0 @@
|
|||
<?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
11
vendor/react/event-loop/tests/bin/21-stop.php
vendored
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
Loop::addTimer(10.0, function () {
|
||||
echo 'never';
|
||||
});
|
||||
|
||||
Loop::stop();
|
|
@ -1,16 +0,0 @@
|
|||
<?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