mirror of
https://github.com/Icinga/icingaweb2-module-reactbundle.git
synced 2025-07-12 16:34:26 +02:00
Version v0.3.0
This commit is contained in:
parent
1c574c6274
commit
c01e90d43b
7
vendor/autoload.php
vendored
Normal file
7
vendor/autoload.php
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInit3150568d47d14b39925e07c8c904a490::getLoader();
|
20
vendor/chrisboulton/php-resque/LICENSE
vendored
Normal file
20
vendor/chrisboulton/php-resque/LICENSE
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
(c) Chris Boulton <chris@bigcommerce.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
vendor/chrisboulton/php-resque/demo/bad_job.php
vendored
Normal file
9
vendor/chrisboulton/php-resque/demo/bad_job.php
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
class Bad_PHP_Job
|
||||||
|
{
|
||||||
|
public function perform()
|
||||||
|
{
|
||||||
|
throw new Exception('Unable to run this job!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
21
vendor/chrisboulton/php-resque/demo/check_status.php
vendored
Normal file
21
vendor/chrisboulton/php-resque/demo/check_status.php
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
if(empty($argv[1])) {
|
||||||
|
die('Specify the ID of a job to monitor the status of.');
|
||||||
|
}
|
||||||
|
|
||||||
|
require '../lib/Resque/Job/Status.php';
|
||||||
|
require '../lib/Resque.php';
|
||||||
|
date_default_timezone_set('GMT');
|
||||||
|
Resque::setBackend('127.0.0.1:6379');
|
||||||
|
|
||||||
|
$status = new Resque_Job_Status($argv[1]);
|
||||||
|
if(!$status->isTracking()) {
|
||||||
|
die("Resque is not tracking the status of this job.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Tracking status of ".$argv[1].". Press [break] to stop.\n\n";
|
||||||
|
while(true) {
|
||||||
|
fwrite(STDOUT, "Status of ".$argv[1]." is: ".$status->get()."\n");
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
?>
|
10
vendor/chrisboulton/php-resque/demo/job.php
vendored
Normal file
10
vendor/chrisboulton/php-resque/demo/job.php
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
class PHP_Job
|
||||||
|
{
|
||||||
|
public function perform()
|
||||||
|
{
|
||||||
|
sleep(120);
|
||||||
|
fwrite(STDOUT, 'Hello!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
9
vendor/chrisboulton/php-resque/demo/long_job.php
vendored
Normal file
9
vendor/chrisboulton/php-resque/demo/long_job.php
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
class Long_PHP_Job
|
||||||
|
{
|
||||||
|
public function perform()
|
||||||
|
{
|
||||||
|
sleep(600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
9
vendor/chrisboulton/php-resque/demo/php_error_job.php
vendored
Normal file
9
vendor/chrisboulton/php-resque/demo/php_error_job.php
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
class PHP_Error_Job
|
||||||
|
{
|
||||||
|
public function perform()
|
||||||
|
{
|
||||||
|
callToUndefinedFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
19
vendor/chrisboulton/php-resque/demo/queue.php
vendored
Normal file
19
vendor/chrisboulton/php-resque/demo/queue.php
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
if(empty($argv[1])) {
|
||||||
|
die('Specify the name of a job to add. e.g, php queue.php PHP_Job');
|
||||||
|
}
|
||||||
|
|
||||||
|
require '../lib/Resque.php';
|
||||||
|
date_default_timezone_set('GMT');
|
||||||
|
Resque::setBackend('127.0.0.1:6379');
|
||||||
|
|
||||||
|
$args = array(
|
||||||
|
'time' => time(),
|
||||||
|
'array' => array(
|
||||||
|
'test' => 'test',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$jobId = Resque::enqueue('default', $argv[1], $args, true);
|
||||||
|
echo "Queued job ".$jobId."\n\n";
|
||||||
|
?>
|
8
vendor/chrisboulton/php-resque/demo/resque.php
vendored
Normal file
8
vendor/chrisboulton/php-resque/demo/resque.php
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
date_default_timezone_set('GMT');
|
||||||
|
require 'bad_job.php';
|
||||||
|
require 'job.php';
|
||||||
|
require 'php_error_job.php';
|
||||||
|
|
||||||
|
require '../resque.php';
|
||||||
|
?>
|
49
vendor/chrisboulton/php-resque/extras/sample-plugin.php
vendored
Normal file
49
vendor/chrisboulton/php-resque/extras/sample-plugin.php
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
// Somewhere in our application, we need to register:
|
||||||
|
Resque_Event::listen('afterEnqueue', array('My_Resque_Plugin', 'afterEnqueue'));
|
||||||
|
Resque_Event::listen('beforeFirstFork', array('My_Resque_Plugin', 'beforeFirstFork'));
|
||||||
|
Resque_Event::listen('beforeFork', array('My_Resque_Plugin', 'beforeFork'));
|
||||||
|
Resque_Event::listen('afterFork', array('My_Resque_Plugin', 'afterFork'));
|
||||||
|
Resque_Event::listen('beforePerform', array('My_Resque_Plugin', 'beforePerform'));
|
||||||
|
Resque_Event::listen('afterPerform', array('My_Resque_Plugin', 'afterPerform'));
|
||||||
|
Resque_Event::listen('onFailure', array('My_Resque_Plugin', 'onFailure'));
|
||||||
|
|
||||||
|
class My_Resque_Plugin
|
||||||
|
{
|
||||||
|
public static function afterEnqueue($class, $arguments)
|
||||||
|
{
|
||||||
|
echo "Job was queued for " . $class . ". Arguments:";
|
||||||
|
print_r($arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function beforeFirstFork($worker)
|
||||||
|
{
|
||||||
|
echo "Worker started. Listening on queues: " . implode(', ', $worker->queues(false)) . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function beforeFork($job)
|
||||||
|
{
|
||||||
|
echo "Just about to fork to run " . $job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function afterFork($job)
|
||||||
|
{
|
||||||
|
echo "Forked to run " . $job . ". This is the child process.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function beforePerform($job)
|
||||||
|
{
|
||||||
|
echo "Cancelling " . $job . "\n";
|
||||||
|
// throw new Resque_Job_DontPerform;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function afterPerform($job)
|
||||||
|
{
|
||||||
|
echo "Just performed " . $job . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function onFailure($exception, $job)
|
||||||
|
{
|
||||||
|
echo $job . " threw an exception:\n" . $exception;
|
||||||
|
}
|
||||||
|
}
|
22
vendor/chrisboulton/php-resque/lib/Redisent/LICENSE
vendored
Normal file
22
vendor/chrisboulton/php-resque/lib/Redisent/LICENSE
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2009 Justin Poliey <jdp34@njit.edu>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
150
vendor/chrisboulton/php-resque/lib/Redisent/Redisent.php
vendored
Normal file
150
vendor/chrisboulton/php-resque/lib/Redisent/Redisent.php
vendored
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Redisent, a Redis interface for the modest
|
||||||
|
* @author Justin Poliey <jdp34@njit.edu>
|
||||||
|
* @copyright 2009 Justin Poliey <jdp34@njit.edu>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||||
|
* @package Redisent
|
||||||
|
*/
|
||||||
|
|
||||||
|
define('CRLF', sprintf('%s%s', chr(13), chr(10)));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps native Redis errors in friendlier PHP exceptions
|
||||||
|
* Only declared if class doesn't already exist to ensure compatibility with php-redis
|
||||||
|
*/
|
||||||
|
if (! class_exists('RedisException', false)) {
|
||||||
|
class RedisException extends Exception {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redisent, a Redis interface for the modest among us
|
||||||
|
*/
|
||||||
|
class Redisent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket connection to the Redis server
|
||||||
|
* @var resource
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
private $__sock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Host of the Redis server
|
||||||
|
* @var string
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
public $host;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port on which the Redis server is running
|
||||||
|
* @var integer
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
public $port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Redisent connection to the Redis server on host {@link $host} and port {@link $port}.
|
||||||
|
* @param string $host The hostname of the Redis server
|
||||||
|
* @param integer $port The port number of the Redis server
|
||||||
|
*/
|
||||||
|
function __construct($host, $port = 6379) {
|
||||||
|
$this->host = $host;
|
||||||
|
$this->port = $port;
|
||||||
|
$this->establishConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
function establishConnection() {
|
||||||
|
$this->__sock = fsockopen($this->host, $this->port, $errno, $errstr);
|
||||||
|
if (!$this->__sock) {
|
||||||
|
throw new Exception("{$errno} - {$errstr}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function __destruct() {
|
||||||
|
fclose($this->__sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
function __call($name, $args) {
|
||||||
|
|
||||||
|
/* Build the Redis unified protocol command */
|
||||||
|
array_unshift($args, strtoupper($name));
|
||||||
|
$command = sprintf('*%d%s%s%s', count($args), CRLF, implode(array_map(array($this, 'formatArgument'), $args), CRLF), CRLF);
|
||||||
|
|
||||||
|
/* Open a Redis connection and execute the command */
|
||||||
|
for ($written = 0; $written < strlen($command); $written += $fwrite) {
|
||||||
|
$fwrite = fwrite($this->__sock, substr($command, $written));
|
||||||
|
if ($fwrite === FALSE) {
|
||||||
|
throw new Exception('Failed to write entire command to stream');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse the response based on the reply identifier */
|
||||||
|
$reply = trim(fgets($this->__sock, 512));
|
||||||
|
switch (substr($reply, 0, 1)) {
|
||||||
|
/* Error reply */
|
||||||
|
case '-':
|
||||||
|
throw new RedisException(substr(trim($reply), 4));
|
||||||
|
break;
|
||||||
|
/* Inline reply */
|
||||||
|
case '+':
|
||||||
|
$response = substr(trim($reply), 1);
|
||||||
|
break;
|
||||||
|
/* Bulk reply */
|
||||||
|
case '$':
|
||||||
|
$response = null;
|
||||||
|
if ($reply == '$-1') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$read = 0;
|
||||||
|
$size = substr($reply, 1);
|
||||||
|
do {
|
||||||
|
$block_size = ($size - $read) > 1024 ? 1024 : ($size - $read);
|
||||||
|
$response .= fread($this->__sock, $block_size);
|
||||||
|
$read += $block_size;
|
||||||
|
} while ($read < $size);
|
||||||
|
fread($this->__sock, 2); /* discard crlf */
|
||||||
|
break;
|
||||||
|
/* Multi-bulk reply */
|
||||||
|
case '*':
|
||||||
|
$count = substr($reply, 1);
|
||||||
|
if ($count == '-1') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$response = array();
|
||||||
|
for ($i = 0; $i < $count; $i++) {
|
||||||
|
$bulk_head = trim(fgets($this->__sock, 512));
|
||||||
|
$size = substr($bulk_head, 1);
|
||||||
|
if ($size == '-1') {
|
||||||
|
$response[] = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$read = 0;
|
||||||
|
$block = "";
|
||||||
|
do {
|
||||||
|
$block_size = ($size - $read) > 1024 ? 1024 : ($size - $read);
|
||||||
|
$block .= fread($this->__sock, $block_size);
|
||||||
|
$read += $block_size;
|
||||||
|
} while ($read < $size);
|
||||||
|
fread($this->__sock, 2); /* discard crlf */
|
||||||
|
$response[] = $block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* Integer reply */
|
||||||
|
case ':':
|
||||||
|
$response = intval(substr(trim($reply), 1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RedisException("invalid server response: {$reply}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Party on */
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatArgument($arg) {
|
||||||
|
return sprintf('$%d%s%s', strlen($arg), CRLF, $arg);
|
||||||
|
}
|
||||||
|
}
|
138
vendor/chrisboulton/php-resque/lib/Redisent/RedisentCluster.php
vendored
Normal file
138
vendor/chrisboulton/php-resque/lib/Redisent/RedisentCluster.php
vendored
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Redisent, a Redis interface for the modest
|
||||||
|
* @author Justin Poliey <jdp34@njit.edu>
|
||||||
|
* @copyright 2009 Justin Poliey <jdp34@njit.edu>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||||
|
* @package Redisent
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once dirname(__FILE__) . '/Redisent.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generalized Redisent interface for a cluster of Redis servers
|
||||||
|
*/
|
||||||
|
class RedisentCluster {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of Redisent objects attached to Redis servers
|
||||||
|
* @var array
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
private $redisents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aliases of Redisent objects attached to Redis servers, used to route commands to specific servers
|
||||||
|
* @see RedisentCluster::to
|
||||||
|
* @var array
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
private $aliases;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash ring of Redis server nodes
|
||||||
|
* @var array
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
private $ring;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Individual nodes of pointers to Redis servers on the hash ring
|
||||||
|
* @var array
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
private $nodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of replicas of each node to make around the hash ring
|
||||||
|
* @var integer
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
private $replicas = 128;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The commands that are not subject to hashing
|
||||||
|
* @var array
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
private $dont_hash = array(
|
||||||
|
'RANDOMKEY', 'DBSIZE',
|
||||||
|
'SELECT', 'MOVE', 'FLUSHDB', 'FLUSHALL',
|
||||||
|
'SAVE', 'BGSAVE', 'LASTSAVE', 'SHUTDOWN',
|
||||||
|
'INFO', 'MONITOR', 'SLAVEOF'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Redisent interface to a cluster of Redis servers
|
||||||
|
* @param array $servers The Redis servers in the cluster. Each server should be in the format array('host' => hostname, 'port' => port)
|
||||||
|
*/
|
||||||
|
function __construct($servers) {
|
||||||
|
$this->ring = array();
|
||||||
|
$this->aliases = array();
|
||||||
|
foreach ($servers as $alias => $server) {
|
||||||
|
$this->redisents[] = new Redisent($server['host'], $server['port']);
|
||||||
|
if (is_string($alias)) {
|
||||||
|
$this->aliases[$alias] = $this->redisents[count($this->redisents)-1];
|
||||||
|
}
|
||||||
|
for ($replica = 1; $replica <= $this->replicas; $replica++) {
|
||||||
|
$this->ring[crc32($server['host'].':'.$server['port'].'-'.$replica)] = $this->redisents[count($this->redisents)-1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ksort($this->ring, SORT_NUMERIC);
|
||||||
|
$this->nodes = array_keys($this->ring);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routes a command to a specific Redis server aliased by {$alias}.
|
||||||
|
* @param string $alias The alias of the Redis server
|
||||||
|
* @return Redisent The Redisent object attached to the Redis server
|
||||||
|
*/
|
||||||
|
function to($alias) {
|
||||||
|
if (isset($this->aliases[$alias])) {
|
||||||
|
return $this->aliases[$alias];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Exception("That Redisent alias does not exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute a Redis command on the cluster */
|
||||||
|
function __call($name, $args) {
|
||||||
|
|
||||||
|
/* Pick a server node to send the command to */
|
||||||
|
$name = strtoupper($name);
|
||||||
|
if (!in_array($name, $this->dont_hash)) {
|
||||||
|
$node = $this->nextNode(crc32($args[0]));
|
||||||
|
$redisent = $this->ring[$node];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$redisent = $this->redisents[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute the command on the server */
|
||||||
|
return call_user_func_array(array($redisent, $name), $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routes to the proper server node
|
||||||
|
* @param integer $needle The hash value of the Redis command
|
||||||
|
* @return Redisent The Redisent object associated with the hash
|
||||||
|
*/
|
||||||
|
private function nextNode($needle) {
|
||||||
|
$haystack = $this->nodes;
|
||||||
|
while (count($haystack) > 2) {
|
||||||
|
$try = floor(count($haystack) / 2);
|
||||||
|
if ($haystack[$try] == $needle) {
|
||||||
|
return $needle;
|
||||||
|
}
|
||||||
|
if ($needle < $haystack[$try]) {
|
||||||
|
$haystack = array_slice($haystack, 0, $try + 1);
|
||||||
|
}
|
||||||
|
if ($needle > $haystack[$try]) {
|
||||||
|
$haystack = array_slice($haystack, $try + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $haystack[count($haystack)-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
189
vendor/chrisboulton/php-resque/lib/Resque.php
vendored
Normal file
189
vendor/chrisboulton/php-resque/lib/Resque.php
vendored
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__FILE__) . '/Resque/Event.php';
|
||||||
|
require_once dirname(__FILE__) . '/Resque/Exception.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base Resque class.
|
||||||
|
*
|
||||||
|
* @package Resque
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque
|
||||||
|
{
|
||||||
|
const VERSION = '1.2';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Resque_Redis Instance of Resque_Redis that talks to redis.
|
||||||
|
*/
|
||||||
|
public static $redis = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var mixed Host/port conbination separated by a colon, or a nested
|
||||||
|
* array of server swith host/port pairs
|
||||||
|
*/
|
||||||
|
protected static $redisServer = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int ID of Redis database to select.
|
||||||
|
*/
|
||||||
|
protected static $redisDatabase = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int PID of current process. Used to detect changes when forking
|
||||||
|
* and implement "thread" safety to avoid race conditions.
|
||||||
|
*/
|
||||||
|
protected static $pid = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a host/port combination separated by a colon, set it as
|
||||||
|
* the redis server that Resque will talk to.
|
||||||
|
*
|
||||||
|
* @param mixed $server Host/port combination separated by a colon, or
|
||||||
|
* a nested array of servers with host/port pairs.
|
||||||
|
* @param int $database
|
||||||
|
*/
|
||||||
|
public static function setBackend($server, $database = 0)
|
||||||
|
{
|
||||||
|
self::$redisServer = $server;
|
||||||
|
self::$redisDatabase = $database;
|
||||||
|
self::$redis = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance of the Resque_Redis class instantiated for Resque.
|
||||||
|
*
|
||||||
|
* @return Resque_Redis Instance of Resque_Redis.
|
||||||
|
*/
|
||||||
|
public static function redis()
|
||||||
|
{
|
||||||
|
// Detect when the PID of the current process has changed (from a fork, etc)
|
||||||
|
// and force a reconnect to redis.
|
||||||
|
$pid = getmypid();
|
||||||
|
if (self::$pid !== $pid) {
|
||||||
|
self::$redis = null;
|
||||||
|
self::$pid = $pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!is_null(self::$redis)) {
|
||||||
|
return self::$redis;
|
||||||
|
}
|
||||||
|
|
||||||
|
$server = self::$redisServer;
|
||||||
|
if (empty($server)) {
|
||||||
|
$server = 'localhost:6379';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_array($server)) {
|
||||||
|
require_once dirname(__FILE__) . '/Resque/RedisCluster.php';
|
||||||
|
self::$redis = new Resque_RedisCluster($server);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (strpos($server, 'unix:') === false) {
|
||||||
|
list($host, $port) = explode(':', $server);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$host = $server;
|
||||||
|
$port = null;
|
||||||
|
}
|
||||||
|
require_once dirname(__FILE__) . '/Resque/Redis.php';
|
||||||
|
self::$redis = new Resque_Redis($host, $port);
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$redis->select(self::$redisDatabase);
|
||||||
|
return self::$redis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push a job to the end of a specific queue. If the queue does not
|
||||||
|
* exist, then create it as well.
|
||||||
|
*
|
||||||
|
* @param string $queue The name of the queue to add the job to.
|
||||||
|
* @param array $item Job description as an array to be JSON encoded.
|
||||||
|
*/
|
||||||
|
public static function push($queue, $item)
|
||||||
|
{
|
||||||
|
self::redis()->sadd('queues', $queue);
|
||||||
|
self::redis()->rpush('queue:' . $queue, json_encode($item));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pop an item off the end of the specified queue, decode it and
|
||||||
|
* return it.
|
||||||
|
*
|
||||||
|
* @param string $queue The name of the queue to fetch an item from.
|
||||||
|
* @return array Decoded item from the queue.
|
||||||
|
*/
|
||||||
|
public static function pop($queue)
|
||||||
|
{
|
||||||
|
$item = self::redis()->lpop('queue:' . $queue);
|
||||||
|
if(!$item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($item, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the size (number of pending jobs) of the specified queue.
|
||||||
|
*
|
||||||
|
* @param $queue name of the queue to be checked for pending jobs
|
||||||
|
*
|
||||||
|
* @return int The size of the queue.
|
||||||
|
*/
|
||||||
|
public static function size($queue)
|
||||||
|
{
|
||||||
|
return self::redis()->llen('queue:' . $queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job and save it to the specified queue.
|
||||||
|
*
|
||||||
|
* @param string $queue The name of the queue to place the job in.
|
||||||
|
* @param string $class The name of the class that contains the code to execute the job.
|
||||||
|
* @param array $args Any optional arguments that should be passed when the job is executed.
|
||||||
|
* @param boolean $trackStatus Set to true to be able to monitor the status of a job.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function enqueue($queue, $class, $args = null, $trackStatus = false)
|
||||||
|
{
|
||||||
|
require_once dirname(__FILE__) . '/Resque/Job.php';
|
||||||
|
$result = Resque_Job::create($queue, $class, $args, $trackStatus);
|
||||||
|
if ($result) {
|
||||||
|
Resque_Event::trigger('afterEnqueue', array(
|
||||||
|
'class' => $class,
|
||||||
|
'args' => $args,
|
||||||
|
'queue' => $queue,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserve and return the next available job in the specified queue.
|
||||||
|
*
|
||||||
|
* @param string $queue Queue to fetch next available job from.
|
||||||
|
* @return Resque_Job Instance of Resque_Job to be processed, false if none or error.
|
||||||
|
*/
|
||||||
|
public static function reserve($queue)
|
||||||
|
{
|
||||||
|
require_once dirname(__FILE__) . '/Resque/Job.php';
|
||||||
|
return Resque_Job::reserve($queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of all known queues.
|
||||||
|
*
|
||||||
|
* @return array Array of queues.
|
||||||
|
*/
|
||||||
|
public static function queues()
|
||||||
|
{
|
||||||
|
$queues = self::redis()->smembers('queues');
|
||||||
|
if(!is_array($queues)) {
|
||||||
|
$queues = array();
|
||||||
|
}
|
||||||
|
return $queues;
|
||||||
|
}
|
||||||
|
}
|
88
vendor/chrisboulton/php-resque/lib/Resque/Event.php
vendored
Normal file
88
vendor/chrisboulton/php-resque/lib/Resque/Event.php
vendored
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Resque event/plugin system class
|
||||||
|
*
|
||||||
|
* @package Resque/Event
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque_Event
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array Array containing all registered callbacks, indexked by event name.
|
||||||
|
*/
|
||||||
|
private static $events = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raise a given event with the supplied data.
|
||||||
|
*
|
||||||
|
* @param string $event Name of event to be raised.
|
||||||
|
* @param mixed $data Optional, any data that should be passed to each callback.
|
||||||
|
* @return true
|
||||||
|
*/
|
||||||
|
public static function trigger($event, $data = null)
|
||||||
|
{
|
||||||
|
if (!is_array($data)) {
|
||||||
|
$data = array($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty(self::$events[$event])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (self::$events[$event] as $callback) {
|
||||||
|
if (!is_callable($callback)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
call_user_func_array($callback, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen in on a given event to have a specified callback fired.
|
||||||
|
*
|
||||||
|
* @param string $event Name of event to listen on.
|
||||||
|
* @param mixed $callback Any callback callable by call_user_func_array.
|
||||||
|
* @return true
|
||||||
|
*/
|
||||||
|
public static function listen($event, $callback)
|
||||||
|
{
|
||||||
|
if (!isset(self::$events[$event])) {
|
||||||
|
self::$events[$event] = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$events[$event][] = $callback;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop a given callback from listening on a specific event.
|
||||||
|
*
|
||||||
|
* @param string $event Name of event.
|
||||||
|
* @param mixed $callback The callback as defined when listen() was called.
|
||||||
|
* @return true
|
||||||
|
*/
|
||||||
|
public static function stopListening($event, $callback)
|
||||||
|
{
|
||||||
|
if (!isset(self::$events[$event])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = array_search($callback, self::$events[$event]);
|
||||||
|
if ($key !== false) {
|
||||||
|
unset(self::$events[$event][$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call all registered listeners.
|
||||||
|
*/
|
||||||
|
public static function clearListeners()
|
||||||
|
{
|
||||||
|
self::$events = array();
|
||||||
|
}
|
||||||
|
}
|
12
vendor/chrisboulton/php-resque/lib/Resque/Exception.php
vendored
Normal file
12
vendor/chrisboulton/php-resque/lib/Resque/Exception.php
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Resque exception.
|
||||||
|
*
|
||||||
|
* @package Resque
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque_Exception extends Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
?>
|
58
vendor/chrisboulton/php-resque/lib/Resque/Failure.php
vendored
Normal file
58
vendor/chrisboulton/php-resque/lib/Resque/Failure.php
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__FILE__) . '/Failure/Interface.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Failed Resque job.
|
||||||
|
*
|
||||||
|
* @package Resque/Failure
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque_Failure
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string Class name representing the backend to pass failed jobs off to.
|
||||||
|
*/
|
||||||
|
private static $backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new failed job on the backend.
|
||||||
|
*
|
||||||
|
* @param object $payload The contents of the job that has just failed.
|
||||||
|
* @param \Exception $exception The exception generated when the job failed to run.
|
||||||
|
* @param \Resque_Worker $worker Instance of Resque_Worker that was running this job when it failed.
|
||||||
|
* @param string $queue The name of the queue that this job was fetched from.
|
||||||
|
*/
|
||||||
|
public static function create($payload, Exception $exception, Resque_Worker $worker, $queue)
|
||||||
|
{
|
||||||
|
$backend = self::getBackend();
|
||||||
|
new $backend($payload, $exception, $worker, $queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance of the backend for saving job failures.
|
||||||
|
*
|
||||||
|
* @return object Instance of backend object.
|
||||||
|
*/
|
||||||
|
public static function getBackend()
|
||||||
|
{
|
||||||
|
if(self::$backend === null) {
|
||||||
|
require dirname(__FILE__) . '/Failure/Redis.php';
|
||||||
|
self::$backend = 'Resque_Failure_Redis';
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the backend to use for raised job failures. The supplied backend
|
||||||
|
* should be the name of a class to be instantiated when a job fails.
|
||||||
|
* It is your responsibility to have the backend class loaded (or autoloaded)
|
||||||
|
*
|
||||||
|
* @param string $backend The class name of the backend to pipe failures to.
|
||||||
|
*/
|
||||||
|
public static function setBackend($backend)
|
||||||
|
{
|
||||||
|
self::$backend = $backend;
|
||||||
|
}
|
||||||
|
}
|
21
vendor/chrisboulton/php-resque/lib/Resque/Failure/Interface.php
vendored
Normal file
21
vendor/chrisboulton/php-resque/lib/Resque/Failure/Interface.php
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Interface that all failure backends should implement.
|
||||||
|
*
|
||||||
|
* @package Resque/Failure
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
interface Resque_Failure_Interface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initialize a failed job class and save it (where appropriate).
|
||||||
|
*
|
||||||
|
* @param object $payload Object containing details of the failed job.
|
||||||
|
* @param object $exception Instance of the exception that was thrown by the failed job.
|
||||||
|
* @param object $worker Instance of Resque_Worker that received the job.
|
||||||
|
* @param string $queue The name of the queue the job was fetched from.
|
||||||
|
*/
|
||||||
|
public function __construct($payload, $exception, $worker, $queue);
|
||||||
|
}
|
||||||
|
?>
|
34
vendor/chrisboulton/php-resque/lib/Resque/Failure/Redis.php
vendored
Normal file
34
vendor/chrisboulton/php-resque/lib/Resque/Failure/Redis.php
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Redis backend for storing failed Resque jobs.
|
||||||
|
*
|
||||||
|
* @package Resque/Failure
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Resque_Failure_Redis implements Resque_Failure_Interface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initialize a failed job class and save it (where appropriate).
|
||||||
|
*
|
||||||
|
* @param object $payload Object containing details of the failed job.
|
||||||
|
* @param object $exception Instance of the exception that was thrown by the failed job.
|
||||||
|
* @param object $worker Instance of Resque_Worker that received the job.
|
||||||
|
* @param string $queue The name of the queue the job was fetched from.
|
||||||
|
*/
|
||||||
|
public function __construct($payload, $exception, $worker, $queue)
|
||||||
|
{
|
||||||
|
$data = new stdClass;
|
||||||
|
$data->failed_at = strftime('%a %b %d %H:%M:%S %Z %Y');
|
||||||
|
$data->payload = $payload;
|
||||||
|
$data->exception = get_class($exception);
|
||||||
|
$data->error = $exception->getMessage();
|
||||||
|
$data->backtrace = explode("\n", $exception->getTraceAsString());
|
||||||
|
$data->worker = (string)$worker;
|
||||||
|
$data->queue = $queue;
|
||||||
|
$data = json_encode($data);
|
||||||
|
Resque::redis()->rpush('failed', $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
257
vendor/chrisboulton/php-resque/lib/Resque/Job.php
vendored
Executable file
257
vendor/chrisboulton/php-resque/lib/Resque/Job.php
vendored
Executable file
@ -0,0 +1,257 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__FILE__) . '/Event.php';
|
||||||
|
require_once dirname(__FILE__) . '/Job/Status.php';
|
||||||
|
require_once dirname(__FILE__) . '/Job/DontPerform.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resque job.
|
||||||
|
*
|
||||||
|
* @package Resque/Job
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque_Job
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string The name of the queue that this job belongs to.
|
||||||
|
*/
|
||||||
|
public $queue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Resque_Worker Instance of the Resque worker running this job.
|
||||||
|
*/
|
||||||
|
public $worker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var object Object containing details of the job.
|
||||||
|
*/
|
||||||
|
public $payload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var object Instance of the class performing work for this job.
|
||||||
|
*/
|
||||||
|
private $instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a new instance of a job.
|
||||||
|
*
|
||||||
|
* @param string $queue The queue that the job belongs to.
|
||||||
|
* @param array $payload array containing details of the job.
|
||||||
|
*/
|
||||||
|
public function __construct($queue, $payload)
|
||||||
|
{
|
||||||
|
$this->queue = $queue;
|
||||||
|
$this->payload = $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job and save it to the specified queue.
|
||||||
|
*
|
||||||
|
* @param string $queue The name of the queue to place the job in.
|
||||||
|
* @param string $class The name of the class that contains the code to execute the job.
|
||||||
|
* @param array $args Any optional arguments that should be passed when the job is executed.
|
||||||
|
* @param boolean $monitor Set to true to be able to monitor the status of a job.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function create($queue, $class, $args = null, $monitor = false)
|
||||||
|
{
|
||||||
|
if($args !== null && !is_array($args)) {
|
||||||
|
throw new InvalidArgumentException(
|
||||||
|
'Supplied $args must be an array.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$id = md5(uniqid('', true));
|
||||||
|
Resque::push($queue, array(
|
||||||
|
'class' => $class,
|
||||||
|
'args' => array($args),
|
||||||
|
'id' => $id,
|
||||||
|
));
|
||||||
|
|
||||||
|
if($monitor) {
|
||||||
|
Resque_Job_Status::create($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the next available job from the specified queue and return an
|
||||||
|
* instance of Resque_Job for it.
|
||||||
|
*
|
||||||
|
* @param string $queue The name of the queue to check for a job in.
|
||||||
|
* @return null|object Null when there aren't any waiting jobs, instance of Resque_Job when a job was found.
|
||||||
|
*/
|
||||||
|
public static function reserve($queue)
|
||||||
|
{
|
||||||
|
$payload = Resque::pop($queue);
|
||||||
|
if(!is_array($payload)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Resque_Job($queue, $payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the status of the current job.
|
||||||
|
*
|
||||||
|
* @param int $status Status constant from Resque_Job_Status indicating the current status of a job.
|
||||||
|
*/
|
||||||
|
public function updateStatus($status)
|
||||||
|
{
|
||||||
|
if(empty($this->payload['id'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$statusInstance = new Resque_Job_Status($this->payload['id']);
|
||||||
|
$statusInstance->update($status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the status of the current job.
|
||||||
|
*
|
||||||
|
* @return int The status of the job as one of the Resque_Job_Status constants.
|
||||||
|
*/
|
||||||
|
public function getStatus()
|
||||||
|
{
|
||||||
|
$status = new Resque_Job_Status($this->payload['id']);
|
||||||
|
return $status->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the arguments supplied to this job.
|
||||||
|
*
|
||||||
|
* @return array Array of arguments.
|
||||||
|
*/
|
||||||
|
public function getArguments()
|
||||||
|
{
|
||||||
|
if (!isset($this->payload['args'])) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->payload['args'][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the instantiated object for this job that will be performing work.
|
||||||
|
*
|
||||||
|
* @return object Instance of the object that this job belongs to.
|
||||||
|
*/
|
||||||
|
public function getInstance()
|
||||||
|
{
|
||||||
|
if (!is_null($this->instance)) {
|
||||||
|
return $this->instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!class_exists($this->payload['class'])) {
|
||||||
|
throw new Resque_Exception(
|
||||||
|
'Could not find job class ' . $this->payload['class'] . '.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!method_exists($this->payload['class'], 'perform')) {
|
||||||
|
throw new Resque_Exception(
|
||||||
|
'Job class ' . $this->payload['class'] . ' does not contain a perform method.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->instance = new $this->payload['class']();
|
||||||
|
$this->instance->job = $this;
|
||||||
|
$this->instance->args = $this->getArguments();
|
||||||
|
$this->instance->queue = $this->queue;
|
||||||
|
return $this->instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually execute a job by calling the perform method on the class
|
||||||
|
* associated with the job with the supplied arguments.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws Resque_Exception When the job's class could not be found or it does not contain a perform method.
|
||||||
|
*/
|
||||||
|
public function perform()
|
||||||
|
{
|
||||||
|
$instance = $this->getInstance();
|
||||||
|
try {
|
||||||
|
Resque_Event::trigger('beforePerform', $this);
|
||||||
|
|
||||||
|
if(method_exists($instance, 'setUp')) {
|
||||||
|
$instance->setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
$instance->perform();
|
||||||
|
|
||||||
|
if(method_exists($instance, 'tearDown')) {
|
||||||
|
$instance->tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
Resque_Event::trigger('afterPerform', $this);
|
||||||
|
}
|
||||||
|
// beforePerform/setUp have said don't perform this job. Return.
|
||||||
|
catch(Resque_Job_DontPerform $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark the current job as having failed.
|
||||||
|
*
|
||||||
|
* @param $exception
|
||||||
|
*/
|
||||||
|
public function fail($exception)
|
||||||
|
{
|
||||||
|
Resque_Event::trigger('onFailure', array(
|
||||||
|
'exception' => $exception,
|
||||||
|
'job' => $this,
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->updateStatus(Resque_Job_Status::STATUS_FAILED);
|
||||||
|
require_once dirname(__FILE__) . '/Failure.php';
|
||||||
|
Resque_Failure::create(
|
||||||
|
$this->payload,
|
||||||
|
$exception,
|
||||||
|
$this->worker,
|
||||||
|
$this->queue
|
||||||
|
);
|
||||||
|
Resque_Stat::incr('failed');
|
||||||
|
Resque_Stat::incr('failed:' . $this->worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-queue the current job.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function recreate()
|
||||||
|
{
|
||||||
|
$status = new Resque_Job_Status($this->payload['id']);
|
||||||
|
$monitor = false;
|
||||||
|
if($status->isTracking()) {
|
||||||
|
$monitor = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::create($this->queue, $this->payload['class'], $this->payload['args'], $monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a string representation used to describe the current job.
|
||||||
|
*
|
||||||
|
* @return string The string representation of the job.
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
$name = array(
|
||||||
|
'Job{' . $this->queue .'}'
|
||||||
|
);
|
||||||
|
if(!empty($this->payload['id'])) {
|
||||||
|
$name[] = 'ID: ' . $this->payload['id'];
|
||||||
|
}
|
||||||
|
$name[] = $this->payload['class'];
|
||||||
|
if(!empty($this->payload['args'])) {
|
||||||
|
$name[] = json_encode($this->payload['args']);
|
||||||
|
}
|
||||||
|
return '(' . implode(' | ', $name) . ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
12
vendor/chrisboulton/php-resque/lib/Resque/Job/DirtyExitException.php
vendored
Normal file
12
vendor/chrisboulton/php-resque/lib/Resque/Job/DirtyExitException.php
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Runtime exception class for a job that does not exit cleanly.
|
||||||
|
*
|
||||||
|
* @package Resque/Job
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque_Job_DirtyExitException extends RuntimeException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
12
vendor/chrisboulton/php-resque/lib/Resque/Job/DontPerform.php
vendored
Normal file
12
vendor/chrisboulton/php-resque/lib/Resque/Job/DontPerform.php
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Exception to be thrown if a job should not be performed/run.
|
||||||
|
*
|
||||||
|
* @package Resque/Job
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque_Job_DontPerform extends Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
143
vendor/chrisboulton/php-resque/lib/Resque/Job/Status.php
vendored
Normal file
143
vendor/chrisboulton/php-resque/lib/Resque/Job/Status.php
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Status tracker/information for a job.
|
||||||
|
*
|
||||||
|
* @package Resque/Job
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque_Job_Status
|
||||||
|
{
|
||||||
|
const STATUS_WAITING = 1;
|
||||||
|
const STATUS_RUNNING = 2;
|
||||||
|
const STATUS_FAILED = 3;
|
||||||
|
const STATUS_COMPLETE = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string The ID of the job this status class refers back to.
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var mixed Cache variable if the status of this job is being monitored or not.
|
||||||
|
* True/false when checked at least once or null if not checked yet.
|
||||||
|
*/
|
||||||
|
private $isTracking = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Array of statuses that are considered final/complete.
|
||||||
|
*/
|
||||||
|
private static $completeStatuses = array(
|
||||||
|
self::STATUS_FAILED,
|
||||||
|
self::STATUS_COMPLETE
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup a new instance of the job monitor class for the supplied job ID.
|
||||||
|
*
|
||||||
|
* @param string $id The ID of the job to manage the status for.
|
||||||
|
*/
|
||||||
|
public function __construct($id)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new status monitor item for the supplied job ID. Will create
|
||||||
|
* all necessary keys in Redis to monitor the status of a job.
|
||||||
|
*
|
||||||
|
* @param string $id The ID of the job to monitor the status of.
|
||||||
|
*/
|
||||||
|
public static function create($id)
|
||||||
|
{
|
||||||
|
$statusPacket = array(
|
||||||
|
'status' => self::STATUS_WAITING,
|
||||||
|
'updated' => time(),
|
||||||
|
'started' => time(),
|
||||||
|
);
|
||||||
|
Resque::redis()->set('job:' . $id . ':status', json_encode($statusPacket));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if we're actually checking the status of the loaded job status
|
||||||
|
* instance.
|
||||||
|
*
|
||||||
|
* @return boolean True if the status is being monitored, false if not.
|
||||||
|
*/
|
||||||
|
public function isTracking()
|
||||||
|
{
|
||||||
|
if($this->isTracking === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Resque::redis()->exists((string)$this)) {
|
||||||
|
$this->isTracking = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->isTracking = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the status indicator for the current job with a new status.
|
||||||
|
*
|
||||||
|
* @param int The status of the job (see constants in Resque_Job_Status)
|
||||||
|
*/
|
||||||
|
public function update($status)
|
||||||
|
{
|
||||||
|
if(!$this->isTracking()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$statusPacket = array(
|
||||||
|
'status' => $status,
|
||||||
|
'updated' => time(),
|
||||||
|
);
|
||||||
|
Resque::redis()->set((string)$this, json_encode($statusPacket));
|
||||||
|
|
||||||
|
// Expire the status for completed jobs after 24 hours
|
||||||
|
if(in_array($status, self::$completeStatuses)) {
|
||||||
|
Resque::redis()->expire((string)$this, 86400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the status for the job being monitored.
|
||||||
|
*
|
||||||
|
* @return mixed False if the status is not being monitored, otherwise the status as
|
||||||
|
* as an integer, based on the Resque_Job_Status constants.
|
||||||
|
*/
|
||||||
|
public function get()
|
||||||
|
{
|
||||||
|
if(!$this->isTracking()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$statusPacket = json_decode(Resque::redis()->get((string)$this), true);
|
||||||
|
if(!$statusPacket) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $statusPacket['status'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop tracking the status of a job.
|
||||||
|
*/
|
||||||
|
public function stop()
|
||||||
|
{
|
||||||
|
Resque::redis()->del((string)$this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a string representation of this object.
|
||||||
|
*
|
||||||
|
* @return string String representation of the current job status class.
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return 'job:' . $this->id . ':status';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
117
vendor/chrisboulton/php-resque/lib/Resque/Redis.php
vendored
Normal file
117
vendor/chrisboulton/php-resque/lib/Resque/Redis.php
vendored
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
// Third- party apps may have already loaded Resident from elsewhere
|
||||||
|
// so lets be careful.
|
||||||
|
if(!class_exists('Redisent', false)) {
|
||||||
|
require_once dirname(__FILE__) . '/../Redisent/Redisent.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended Redisent class used by Resque for all communication with
|
||||||
|
* redis. Essentially adds namespace support to Redisent.
|
||||||
|
*
|
||||||
|
* @package Resque/Redis
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque_Redis extends Redisent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Redis namespace
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private static $defaultNamespace = 'resque:';
|
||||||
|
/**
|
||||||
|
* @var array List of all commands in Redis that supply a key as their
|
||||||
|
* first argument. Used to prefix keys with the Resque namespace.
|
||||||
|
*/
|
||||||
|
private $keyCommands = array(
|
||||||
|
'exists',
|
||||||
|
'del',
|
||||||
|
'type',
|
||||||
|
'keys',
|
||||||
|
'expire',
|
||||||
|
'ttl',
|
||||||
|
'move',
|
||||||
|
'set',
|
||||||
|
'get',
|
||||||
|
'getset',
|
||||||
|
'setnx',
|
||||||
|
'incr',
|
||||||
|
'incrby',
|
||||||
|
'decr',
|
||||||
|
'decrby',
|
||||||
|
'rpush',
|
||||||
|
'lpush',
|
||||||
|
'llen',
|
||||||
|
'lrange',
|
||||||
|
'ltrim',
|
||||||
|
'lindex',
|
||||||
|
'lset',
|
||||||
|
'lrem',
|
||||||
|
'lpop',
|
||||||
|
'rpop',
|
||||||
|
'sadd',
|
||||||
|
'srem',
|
||||||
|
'spop',
|
||||||
|
'scard',
|
||||||
|
'sismember',
|
||||||
|
'smembers',
|
||||||
|
'srandmember',
|
||||||
|
'zadd',
|
||||||
|
'zrem',
|
||||||
|
'zrange',
|
||||||
|
'zrevrange',
|
||||||
|
'zrangebyscore',
|
||||||
|
'zcard',
|
||||||
|
'zscore',
|
||||||
|
'zremrangebyscore',
|
||||||
|
'sort'
|
||||||
|
);
|
||||||
|
// sinterstore
|
||||||
|
// sunion
|
||||||
|
// sunionstore
|
||||||
|
// sdiff
|
||||||
|
// sdiffstore
|
||||||
|
// sinter
|
||||||
|
// smove
|
||||||
|
// rename
|
||||||
|
// rpoplpush
|
||||||
|
// mget
|
||||||
|
// msetnx
|
||||||
|
// mset
|
||||||
|
// renamenx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Redis namespace (prefix) default: resque
|
||||||
|
* @param string $namespace
|
||||||
|
*/
|
||||||
|
public static function prefix($namespace)
|
||||||
|
{
|
||||||
|
if (strpos($namespace, ':') === false) {
|
||||||
|
$namespace .= ':';
|
||||||
|
}
|
||||||
|
self::$defaultNamespace = $namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic method to handle all function requests and prefix key based
|
||||||
|
* operations with the {self::$defaultNamespace} key prefix.
|
||||||
|
*
|
||||||
|
* @param string $name The name of the method called.
|
||||||
|
* @param array $args Array of supplied arguments to the method.
|
||||||
|
* @return mixed Return value from Resident::call() based on the command.
|
||||||
|
*/
|
||||||
|
public function __call($name, $args) {
|
||||||
|
$args = func_get_args();
|
||||||
|
if(in_array($name, $this->keyCommands)) {
|
||||||
|
$args[1][0] = self::$defaultNamespace . $args[1][0];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return parent::__call($name, $args[1]);
|
||||||
|
}
|
||||||
|
catch(RedisException $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
117
vendor/chrisboulton/php-resque/lib/Resque/RedisCluster.php
vendored
Normal file
117
vendor/chrisboulton/php-resque/lib/Resque/RedisCluster.php
vendored
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
// Third- party apps may have already loaded Resident from elsewhere
|
||||||
|
// so lets be careful.
|
||||||
|
if(!class_exists('RedisentCluster', false)) {
|
||||||
|
require_once dirname(__FILE__) . '/../Redisent/RedisentCluster.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended Redisent class used by Resque for all communication with
|
||||||
|
* redis. Essentially adds namespace support to Redisent.
|
||||||
|
*
|
||||||
|
* @package Resque/Redis
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque_RedisCluster extends RedisentCluster
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Redis namespace
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private static $defaultNamespace = 'resque:';
|
||||||
|
/**
|
||||||
|
* @var array List of all commands in Redis that supply a key as their
|
||||||
|
* first argument. Used to prefix keys with the Resque namespace.
|
||||||
|
*/
|
||||||
|
private $keyCommands = array(
|
||||||
|
'exists',
|
||||||
|
'del',
|
||||||
|
'type',
|
||||||
|
'keys',
|
||||||
|
'expire',
|
||||||
|
'ttl',
|
||||||
|
'move',
|
||||||
|
'set',
|
||||||
|
'get',
|
||||||
|
'getset',
|
||||||
|
'setnx',
|
||||||
|
'incr',
|
||||||
|
'incrby',
|
||||||
|
'decrby',
|
||||||
|
'decrby',
|
||||||
|
'rpush',
|
||||||
|
'lpush',
|
||||||
|
'llen',
|
||||||
|
'lrange',
|
||||||
|
'ltrim',
|
||||||
|
'lindex',
|
||||||
|
'lset',
|
||||||
|
'lrem',
|
||||||
|
'lpop',
|
||||||
|
'rpop',
|
||||||
|
'sadd',
|
||||||
|
'srem',
|
||||||
|
'spop',
|
||||||
|
'scard',
|
||||||
|
'sismember',
|
||||||
|
'smembers',
|
||||||
|
'srandmember',
|
||||||
|
'zadd',
|
||||||
|
'zrem',
|
||||||
|
'zrange',
|
||||||
|
'zrevrange',
|
||||||
|
'zrangebyscore',
|
||||||
|
'zcard',
|
||||||
|
'zscore',
|
||||||
|
'zremrangebyscore',
|
||||||
|
'sort'
|
||||||
|
);
|
||||||
|
// sinterstore
|
||||||
|
// sunion
|
||||||
|
// sunionstore
|
||||||
|
// sdiff
|
||||||
|
// sdiffstore
|
||||||
|
// sinter
|
||||||
|
// smove
|
||||||
|
// rename
|
||||||
|
// rpoplpush
|
||||||
|
// mget
|
||||||
|
// msetnx
|
||||||
|
// mset
|
||||||
|
// renamenx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Redis namespace (prefix) default: resque
|
||||||
|
* @param string $namespace
|
||||||
|
*/
|
||||||
|
public static function prefix($namespace)
|
||||||
|
{
|
||||||
|
if (strpos($namespace, ':') === false) {
|
||||||
|
$namespace .= ':';
|
||||||
|
}
|
||||||
|
self::$defaultNamespace = $namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic method to handle all function requests and prefix key based
|
||||||
|
* operations with the '{self::$defaultNamespace}' key prefix.
|
||||||
|
*
|
||||||
|
* @param string $name The name of the method called.
|
||||||
|
* @param array $args Array of supplied arguments to the method.
|
||||||
|
* @return mixed Return value from Resident::call() based on the command.
|
||||||
|
*/
|
||||||
|
public function __call($name, $args) {
|
||||||
|
$args = func_get_args();
|
||||||
|
if(in_array($name, $this->keyCommands)) {
|
||||||
|
$args[1][0] = self::$defaultNamespace . $args[1][0];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return parent::__call($name, $args[1]);
|
||||||
|
}
|
||||||
|
catch(RedisException $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
56
vendor/chrisboulton/php-resque/lib/Resque/Stat.php
vendored
Normal file
56
vendor/chrisboulton/php-resque/lib/Resque/Stat.php
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Resque statistic management (jobs processed, failed, etc)
|
||||||
|
*
|
||||||
|
* @package Resque/Stat
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque_Stat
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the value of the supplied statistic counter for the specified statistic.
|
||||||
|
*
|
||||||
|
* @param string $stat The name of the statistic to get the stats for.
|
||||||
|
* @return mixed Value of the statistic.
|
||||||
|
*/
|
||||||
|
public static function get($stat)
|
||||||
|
{
|
||||||
|
return (int)Resque::redis()->get('stat:' . $stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the value of the specified statistic by a certain amount (default is 1)
|
||||||
|
*
|
||||||
|
* @param string $stat The name of the statistic to increment.
|
||||||
|
* @param int $by The amount to increment the statistic by.
|
||||||
|
* @return boolean True if successful, false if not.
|
||||||
|
*/
|
||||||
|
public static function incr($stat, $by = 1)
|
||||||
|
{
|
||||||
|
return (bool)Resque::redis()->incrby('stat:' . $stat, $by);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement the value of the specified statistic by a certain amount (default is 1)
|
||||||
|
*
|
||||||
|
* @param string $stat The name of the statistic to decrement.
|
||||||
|
* @param int $by The amount to decrement the statistic by.
|
||||||
|
* @return boolean True if successful, false if not.
|
||||||
|
*/
|
||||||
|
public static function decr($stat, $by = 1)
|
||||||
|
{
|
||||||
|
return (bool)Resque::redis()->decrby('stat:' . $stat, $by);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a statistic with the given name.
|
||||||
|
*
|
||||||
|
* @param string $stat The name of the statistic to delete.
|
||||||
|
* @return boolean True if successful, false if not.
|
||||||
|
*/
|
||||||
|
public static function clear($stat)
|
||||||
|
{
|
||||||
|
return (bool)Resque::redis()->del('stat:' . $stat);
|
||||||
|
}
|
||||||
|
}
|
585
vendor/chrisboulton/php-resque/lib/Resque/Worker.php
vendored
Normal file
585
vendor/chrisboulton/php-resque/lib/Resque/Worker.php
vendored
Normal file
@ -0,0 +1,585 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__FILE__) . '/Stat.php';
|
||||||
|
require_once dirname(__FILE__) . '/Event.php';
|
||||||
|
require_once dirname(__FILE__) . '/Job.php';
|
||||||
|
require_once dirname(__FILE__) . '/Job/DirtyExitException.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resque worker that handles checking queues for jobs, fetching them
|
||||||
|
* off the queues, running them and handling the result.
|
||||||
|
*
|
||||||
|
* @package Resque/Worker
|
||||||
|
* @author Chris Boulton <chris@bigcommerce.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
class Resque_Worker
|
||||||
|
{
|
||||||
|
const LOG_NONE = 0;
|
||||||
|
const LOG_NORMAL = 1;
|
||||||
|
const LOG_VERBOSE = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int Current log level of this worker.
|
||||||
|
*/
|
||||||
|
public $logLevel = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Array of all associated queues for this worker.
|
||||||
|
*/
|
||||||
|
private $queues = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string The hostname of this worker.
|
||||||
|
*/
|
||||||
|
private $hostname;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean True if on the next iteration, the worker should shutdown.
|
||||||
|
*/
|
||||||
|
private $shutdown = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean True if this worker is paused.
|
||||||
|
*/
|
||||||
|
private $paused = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string String identifying this worker.
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Resque_Job Current job, if any, being processed by this worker.
|
||||||
|
*/
|
||||||
|
private $currentJob = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int Process ID of child worker processes.
|
||||||
|
*/
|
||||||
|
private $child = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all workers known to Resque as instantiated instances.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function all()
|
||||||
|
{
|
||||||
|
$workers = Resque::redis()->smembers('workers');
|
||||||
|
if(!is_array($workers)) {
|
||||||
|
$workers = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$instances = array();
|
||||||
|
foreach($workers as $workerId) {
|
||||||
|
$instances[] = self::find($workerId);
|
||||||
|
}
|
||||||
|
return $instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a worker ID, check if it is registered/valid.
|
||||||
|
*
|
||||||
|
* @param string $workerId ID of the worker.
|
||||||
|
* @return boolean True if the worker exists, false if not.
|
||||||
|
*/
|
||||||
|
public static function exists($workerId)
|
||||||
|
{
|
||||||
|
return (bool)Resque::redis()->sismember('workers', $workerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a worker ID, find it and return an instantiated worker class for it.
|
||||||
|
*
|
||||||
|
* @param string $workerId The ID of the worker.
|
||||||
|
* @return Resque_Worker Instance of the worker. False if the worker does not exist.
|
||||||
|
*/
|
||||||
|
public static function find($workerId)
|
||||||
|
{
|
||||||
|
if(!self::exists($workerId) || false === strpos($workerId, ":")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($hostname, $pid, $queues) = explode(':', $workerId, 3);
|
||||||
|
$queues = explode(',', $queues);
|
||||||
|
$worker = new self($queues);
|
||||||
|
$worker->setId($workerId);
|
||||||
|
return $worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the ID of this worker to a given ID string.
|
||||||
|
*
|
||||||
|
* @param string $workerId ID for the worker.
|
||||||
|
*/
|
||||||
|
public function setId($workerId)
|
||||||
|
{
|
||||||
|
$this->id = $workerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a new worker, given a list of queues that it should be working
|
||||||
|
* on. The list of queues should be supplied in the priority that they should
|
||||||
|
* be checked for jobs (first come, first served)
|
||||||
|
*
|
||||||
|
* Passing a single '*' allows the worker to work on all queues in alphabetical
|
||||||
|
* order. You can easily add new queues dynamically and have them worked on using
|
||||||
|
* this method.
|
||||||
|
*
|
||||||
|
* @param string|array $queues String with a single queue name, array with multiple.
|
||||||
|
*/
|
||||||
|
public function __construct($queues)
|
||||||
|
{
|
||||||
|
if(!is_array($queues)) {
|
||||||
|
$queues = array($queues);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->queues = $queues;
|
||||||
|
if(function_exists('gethostname')) {
|
||||||
|
$hostname = gethostname();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$hostname = php_uname('n');
|
||||||
|
}
|
||||||
|
$this->hostname = $hostname;
|
||||||
|
$this->id = $this->hostname . ':'.getmypid() . ':' . implode(',', $this->queues);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The primary loop for a worker which when called on an instance starts
|
||||||
|
* the worker's life cycle.
|
||||||
|
*
|
||||||
|
* Queues are checked every $interval (seconds) for new jobs.
|
||||||
|
*
|
||||||
|
* @param int $interval How often to check for new jobs across the queues.
|
||||||
|
*/
|
||||||
|
public function work($interval = 5)
|
||||||
|
{
|
||||||
|
$this->updateProcLine('Starting');
|
||||||
|
$this->startup();
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
if($this->shutdown) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to find and reserve a job
|
||||||
|
$job = false;
|
||||||
|
if(!$this->paused) {
|
||||||
|
$job = $this->reserve();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$job) {
|
||||||
|
// For an interval of 0, break now - helps with unit testing etc
|
||||||
|
if($interval == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If no job was found, we sleep for $interval before continuing and checking again
|
||||||
|
$this->log('Sleeping for ' . $interval, true);
|
||||||
|
if($this->paused) {
|
||||||
|
$this->updateProcLine('Paused');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->updateProcLine('Waiting for ' . implode(',', $this->queues));
|
||||||
|
}
|
||||||
|
usleep($interval * 1000000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->log('got ' . $job);
|
||||||
|
Resque_Event::trigger('beforeFork', $job);
|
||||||
|
$this->workingOn($job);
|
||||||
|
|
||||||
|
$this->child = $this->fork();
|
||||||
|
|
||||||
|
// Forked and we're the child. Run the job.
|
||||||
|
if ($this->child === 0 || $this->child === false) {
|
||||||
|
$status = 'Processing ' . $job->queue . ' since ' . strftime('%F %T');
|
||||||
|
$this->updateProcLine($status);
|
||||||
|
$this->log($status, self::LOG_VERBOSE);
|
||||||
|
$this->perform($job);
|
||||||
|
if ($this->child === 0) {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->child > 0) {
|
||||||
|
// Parent process, sit and wait
|
||||||
|
$status = 'Forked ' . $this->child . ' at ' . strftime('%F %T');
|
||||||
|
$this->updateProcLine($status);
|
||||||
|
$this->log($status, self::LOG_VERBOSE);
|
||||||
|
|
||||||
|
// Wait until the child process finishes before continuing
|
||||||
|
pcntl_wait($status);
|
||||||
|
$exitStatus = pcntl_wexitstatus($status);
|
||||||
|
if($exitStatus !== 0) {
|
||||||
|
$job->fail(new Resque_Job_DirtyExitException(
|
||||||
|
'Job exited with exit code ' . $exitStatus
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->child = null;
|
||||||
|
$this->doneWorking();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->unregisterWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a single job.
|
||||||
|
*
|
||||||
|
* @param Resque_Job $job The job to be processed.
|
||||||
|
*/
|
||||||
|
public function perform(Resque_Job $job)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Resque_Event::trigger('afterFork', $job);
|
||||||
|
$job->perform();
|
||||||
|
}
|
||||||
|
catch(Exception $e) {
|
||||||
|
$this->log($job . ' failed: ' . $e->getMessage());
|
||||||
|
$job->fail($e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$job->updateStatus(Resque_Job_Status::STATUS_COMPLETE);
|
||||||
|
$this->log('done ' . $job);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to find a job from the top of one of the queues for this worker.
|
||||||
|
*
|
||||||
|
* @return object|boolean Instance of Resque_Job if a job is found, false if not.
|
||||||
|
*/
|
||||||
|
public function reserve()
|
||||||
|
{
|
||||||
|
$queues = $this->queues();
|
||||||
|
if(!is_array($queues)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach($queues as $queue) {
|
||||||
|
$this->log('Checking ' . $queue, self::LOG_VERBOSE);
|
||||||
|
$job = Resque_Job::reserve($queue);
|
||||||
|
if($job) {
|
||||||
|
$this->log('Found job on ' . $queue, self::LOG_VERBOSE);
|
||||||
|
return $job;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array containing all of the queues that this worker should use
|
||||||
|
* when searching for jobs.
|
||||||
|
*
|
||||||
|
* If * is found in the list of queues, every queue will be searched in
|
||||||
|
* alphabetic order. (@see $fetch)
|
||||||
|
*
|
||||||
|
* @param boolean $fetch If true, and the queue is set to *, will fetch
|
||||||
|
* all queue names from redis.
|
||||||
|
* @return array Array of associated queues.
|
||||||
|
*/
|
||||||
|
public function queues($fetch = true)
|
||||||
|
{
|
||||||
|
if(!in_array('*', $this->queues) || $fetch == false) {
|
||||||
|
return $this->queues;
|
||||||
|
}
|
||||||
|
|
||||||
|
$queues = Resque::queues();
|
||||||
|
sort($queues);
|
||||||
|
return $queues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to fork a child process from the parent to run a job in.
|
||||||
|
*
|
||||||
|
* Return values are those of pcntl_fork().
|
||||||
|
*
|
||||||
|
* @return int -1 if the fork failed, 0 for the forked child, the PID of the child for the parent.
|
||||||
|
*/
|
||||||
|
private function fork()
|
||||||
|
{
|
||||||
|
if(!function_exists('pcntl_fork')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pid = pcntl_fork();
|
||||||
|
if($pid === -1) {
|
||||||
|
throw new RuntimeException('Unable to fork child worker.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform necessary actions to start a worker.
|
||||||
|
*/
|
||||||
|
private function startup()
|
||||||
|
{
|
||||||
|
$this->registerSigHandlers();
|
||||||
|
$this->pruneDeadWorkers();
|
||||||
|
Resque_Event::trigger('beforeFirstFork', $this);
|
||||||
|
$this->registerWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On supported systems (with the PECL proctitle module installed), update
|
||||||
|
* the name of the currently running process to indicate the current state
|
||||||
|
* of a worker.
|
||||||
|
*
|
||||||
|
* @param string $status The updated process title.
|
||||||
|
*/
|
||||||
|
private function updateProcLine($status)
|
||||||
|
{
|
||||||
|
if(function_exists('setproctitle')) {
|
||||||
|
setproctitle('resque-' . Resque::VERSION . ': ' . $status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register signal handlers that a worker should respond to.
|
||||||
|
*
|
||||||
|
* TERM: Shutdown immediately and stop processing jobs.
|
||||||
|
* INT: Shutdown immediately and stop processing jobs.
|
||||||
|
* QUIT: Shutdown after the current job finishes processing.
|
||||||
|
* USR1: Kill the forked child immediately and continue processing jobs.
|
||||||
|
*/
|
||||||
|
private function registerSigHandlers()
|
||||||
|
{
|
||||||
|
if(!function_exists('pcntl_signal')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare(ticks = 1);
|
||||||
|
pcntl_signal(SIGTERM, array($this, 'shutDownNow'));
|
||||||
|
pcntl_signal(SIGINT, array($this, 'shutDownNow'));
|
||||||
|
pcntl_signal(SIGQUIT, array($this, 'shutdown'));
|
||||||
|
pcntl_signal(SIGUSR1, array($this, 'killChild'));
|
||||||
|
pcntl_signal(SIGUSR2, array($this, 'pauseProcessing'));
|
||||||
|
pcntl_signal(SIGCONT, array($this, 'unPauseProcessing'));
|
||||||
|
pcntl_signal(SIGPIPE, array($this, 'reestablishRedisConnection'));
|
||||||
|
$this->log('Registered signals', self::LOG_VERBOSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal handler callback for USR2, pauses processing of new jobs.
|
||||||
|
*/
|
||||||
|
public function pauseProcessing()
|
||||||
|
{
|
||||||
|
$this->log('USR2 received; pausing job processing');
|
||||||
|
$this->paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal handler callback for CONT, resumes worker allowing it to pick
|
||||||
|
* up new jobs.
|
||||||
|
*/
|
||||||
|
public function unPauseProcessing()
|
||||||
|
{
|
||||||
|
$this->log('CONT received; resuming job processing');
|
||||||
|
$this->paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal handler for SIGPIPE, in the event the redis connection has gone away.
|
||||||
|
* Attempts to reconnect to redis, or raises an Exception.
|
||||||
|
*/
|
||||||
|
public function reestablishRedisConnection()
|
||||||
|
{
|
||||||
|
$this->log('SIGPIPE received; attempting to reconnect');
|
||||||
|
Resque::redis()->establishConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a worker for shutdown. Will finish processing the current job
|
||||||
|
* and when the timeout interval is reached, the worker will shut down.
|
||||||
|
*/
|
||||||
|
public function shutdown()
|
||||||
|
{
|
||||||
|
$this->shutdown = true;
|
||||||
|
$this->log('Exiting...');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force an immediate shutdown of the worker, killing any child jobs
|
||||||
|
* currently running.
|
||||||
|
*/
|
||||||
|
public function shutdownNow()
|
||||||
|
{
|
||||||
|
$this->shutdown();
|
||||||
|
$this->killChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kill a forked child job immediately. The job it is processing will not
|
||||||
|
* be completed.
|
||||||
|
*/
|
||||||
|
public function killChild()
|
||||||
|
{
|
||||||
|
if(!$this->child) {
|
||||||
|
$this->log('No child to kill.', self::LOG_VERBOSE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->log('Killing child at ' . $this->child, self::LOG_VERBOSE);
|
||||||
|
if(exec('ps -o pid,state -p ' . $this->child, $output, $returnCode) && $returnCode != 1) {
|
||||||
|
$this->log('Killing child at ' . $this->child, self::LOG_VERBOSE);
|
||||||
|
posix_kill($this->child, SIGKILL);
|
||||||
|
$this->child = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->log('Child ' . $this->child . ' not found, restarting.', self::LOG_VERBOSE);
|
||||||
|
$this->shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look for any workers which should be running on this server and if
|
||||||
|
* they're not, remove them from Redis.
|
||||||
|
*
|
||||||
|
* This is a form of garbage collection to handle cases where the
|
||||||
|
* server may have been killed and the Resque workers did not die gracefully
|
||||||
|
* and therefore leave state information in Redis.
|
||||||
|
*/
|
||||||
|
public function pruneDeadWorkers()
|
||||||
|
{
|
||||||
|
$workerPids = $this->workerPids();
|
||||||
|
$workers = self::all();
|
||||||
|
foreach($workers as $worker) {
|
||||||
|
if (is_object($worker)) {
|
||||||
|
list($host, $pid, $queues) = explode(':', (string)$worker, 3);
|
||||||
|
if($host != $this->hostname || in_array($pid, $workerPids) || $pid == getmypid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->log('Pruning dead worker: ' . (string)$worker, self::LOG_VERBOSE);
|
||||||
|
$worker->unregisterWorker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of process IDs for all of the Resque workers currently
|
||||||
|
* running on this machine.
|
||||||
|
*
|
||||||
|
* @return array Array of Resque worker process IDs.
|
||||||
|
*/
|
||||||
|
public function workerPids()
|
||||||
|
{
|
||||||
|
$pids = array();
|
||||||
|
exec('ps -A -o pid,command | grep [r]esque', $cmdOutput);
|
||||||
|
foreach($cmdOutput as $line) {
|
||||||
|
list($pids[],) = explode(' ', trim($line), 2);
|
||||||
|
}
|
||||||
|
return $pids;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register this worker in Redis.
|
||||||
|
*/
|
||||||
|
public function registerWorker()
|
||||||
|
{
|
||||||
|
Resque::redis()->sadd('workers', $this);
|
||||||
|
Resque::redis()->set('worker:' . (string)$this . ':started', strftime('%a %b %d %H:%M:%S %Z %Y'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister this worker in Redis. (shutdown etc)
|
||||||
|
*/
|
||||||
|
public function unregisterWorker()
|
||||||
|
{
|
||||||
|
if(is_object($this->currentJob)) {
|
||||||
|
$this->currentJob->fail(new Resque_Job_DirtyExitException);
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (string)$this;
|
||||||
|
Resque::redis()->srem('workers', $id);
|
||||||
|
Resque::redis()->del('worker:' . $id);
|
||||||
|
Resque::redis()->del('worker:' . $id . ':started');
|
||||||
|
Resque_Stat::clear('processed:' . $id);
|
||||||
|
Resque_Stat::clear('failed:' . $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell Redis which job we're currently working on.
|
||||||
|
*
|
||||||
|
* @param object $job Resque_Job instance containing the job we're working on.
|
||||||
|
*/
|
||||||
|
public function workingOn(Resque_Job $job)
|
||||||
|
{
|
||||||
|
$job->worker = $this;
|
||||||
|
$this->currentJob = $job;
|
||||||
|
$job->updateStatus(Resque_Job_Status::STATUS_RUNNING);
|
||||||
|
$data = json_encode(array(
|
||||||
|
'queue' => $job->queue,
|
||||||
|
'run_at' => strftime('%a %b %d %H:%M:%S %Z %Y'),
|
||||||
|
'payload' => $job->payload
|
||||||
|
));
|
||||||
|
Resque::redis()->set('worker:' . $job->worker, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify Redis that we've finished working on a job, clearing the working
|
||||||
|
* state and incrementing the job stats.
|
||||||
|
*/
|
||||||
|
public function doneWorking()
|
||||||
|
{
|
||||||
|
$this->currentJob = null;
|
||||||
|
Resque_Stat::incr('processed');
|
||||||
|
Resque_Stat::incr('processed:' . (string)$this);
|
||||||
|
Resque::redis()->del('worker:' . (string)$this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a string representation of this worker.
|
||||||
|
*
|
||||||
|
* @return string String identifier for this worker instance.
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output a given log message to STDOUT.
|
||||||
|
*
|
||||||
|
* @param string $message Message to output.
|
||||||
|
*/
|
||||||
|
public function log($message)
|
||||||
|
{
|
||||||
|
if($this->logLevel == self::LOG_NORMAL) {
|
||||||
|
fwrite(STDOUT, "*** " . $message . "\n");
|
||||||
|
}
|
||||||
|
else if($this->logLevel == self::LOG_VERBOSE) {
|
||||||
|
fwrite(STDOUT, "** [" . strftime('%T %Y-%m-%d') . "] " . $message . "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an object describing the job this worker is currently working on.
|
||||||
|
*
|
||||||
|
* @return object Object with details of current job.
|
||||||
|
*/
|
||||||
|
public function job()
|
||||||
|
{
|
||||||
|
$job = Resque::redis()->get('worker:' . $this);
|
||||||
|
if(!$job) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return json_decode($job, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a statistic belonging to this worker.
|
||||||
|
*
|
||||||
|
* @param string $stat Statistic to fetch.
|
||||||
|
* @return int Statistic value.
|
||||||
|
*/
|
||||||
|
public function getStat($stat)
|
||||||
|
{
|
||||||
|
return Resque_Stat::get($stat . ':' . $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
79
vendor/chrisboulton/php-resque/resque.php
vendored
Normal file
79
vendor/chrisboulton/php-resque/resque.php
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
$QUEUE = getenv('QUEUE');
|
||||||
|
if(empty($QUEUE)) {
|
||||||
|
die("Set QUEUE env var containing the list of queues to work.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'lib/Resque.php';
|
||||||
|
require_once 'lib/Resque/Worker.php';
|
||||||
|
|
||||||
|
$REDIS_BACKEND = getenv('REDIS_BACKEND');
|
||||||
|
if(!empty($REDIS_BACKEND)) {
|
||||||
|
Resque::setBackend($REDIS_BACKEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$logLevel = 0;
|
||||||
|
$LOGGING = getenv('LOGGING');
|
||||||
|
$VERBOSE = getenv('VERBOSE');
|
||||||
|
$VVERBOSE = getenv('VVERBOSE');
|
||||||
|
if(!empty($LOGGING) || !empty($VERBOSE)) {
|
||||||
|
$logLevel = Resque_Worker::LOG_NORMAL;
|
||||||
|
}
|
||||||
|
else if(!empty($VVERBOSE)) {
|
||||||
|
$logLevel = Resque_Worker::LOG_VERBOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$APP_INCLUDE = getenv('APP_INCLUDE');
|
||||||
|
if($APP_INCLUDE) {
|
||||||
|
if(!file_exists($APP_INCLUDE)) {
|
||||||
|
die('APP_INCLUDE ('.$APP_INCLUDE.") does not exist.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once $APP_INCLUDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$interval = 5;
|
||||||
|
$INTERVAL = getenv('INTERVAL');
|
||||||
|
if(!empty($INTERVAL)) {
|
||||||
|
$interval = $INTERVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 1;
|
||||||
|
$COUNT = getenv('COUNT');
|
||||||
|
if(!empty($COUNT) && $COUNT > 1) {
|
||||||
|
$count = $COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($count > 1) {
|
||||||
|
for($i = 0; $i < $count; ++$i) {
|
||||||
|
$pid = pcntl_fork();
|
||||||
|
if($pid == -1) {
|
||||||
|
die("Could not fork worker ".$i."\n");
|
||||||
|
}
|
||||||
|
// Child, start the worker
|
||||||
|
else if(!$pid) {
|
||||||
|
$queues = explode(',', $QUEUE);
|
||||||
|
$worker = new Resque_Worker($queues);
|
||||||
|
$worker->logLevel = $logLevel;
|
||||||
|
fwrite(STDOUT, '*** Starting worker '.$worker."\n");
|
||||||
|
$worker->work($interval);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Start a single worker
|
||||||
|
else {
|
||||||
|
$queues = explode(',', $QUEUE);
|
||||||
|
$worker = new Resque_Worker($queues);
|
||||||
|
$worker->logLevel = $logLevel;
|
||||||
|
|
||||||
|
$PIDFILE = getenv('PIDFILE');
|
||||||
|
if ($PIDFILE) {
|
||||||
|
file_put_contents($PIDFILE, getmypid()) or
|
||||||
|
die('Could not write PID information to ' . $PIDFILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite(STDOUT, '*** Starting worker '.$worker."\n");
|
||||||
|
$worker->work($interval);
|
||||||
|
}
|
||||||
|
?>
|
21
vendor/clue/block-react/LICENSE
vendored
Normal file
21
vendor/clue/block-react/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Christian Lück
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
207
vendor/clue/block-react/src/functions.php
vendored
Normal file
207
vendor/clue/block-react/src/functions.php
vendored
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Block;
|
||||||
|
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
use React\Promise\CancellablePromiseInterface;
|
||||||
|
use UnderflowException;
|
||||||
|
use Exception;
|
||||||
|
use React\Promise;
|
||||||
|
use React\Promise\Timer;
|
||||||
|
use React\Promise\Timer\TimeoutException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wait/sleep for $time seconds
|
||||||
|
*
|
||||||
|
* The $time value will be used as a timer for the loop so that it keeps running
|
||||||
|
* until the timeout triggers.
|
||||||
|
* This implies that if you pass a really small (or negative) value, it will still
|
||||||
|
* start a timer and will thus trigger at the earliest possible time in the future.
|
||||||
|
*
|
||||||
|
* @param float $time
|
||||||
|
* @param LoopInterface $loop
|
||||||
|
*/
|
||||||
|
function sleep($time, LoopInterface $loop)
|
||||||
|
{
|
||||||
|
await(Timer\resolve($time, $loop), $loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* block waiting for the given $promise to resolve
|
||||||
|
*
|
||||||
|
* Once the promise is resolved, this will return whatever the promise resolves to.
|
||||||
|
*
|
||||||
|
* Once the promise is rejected, this will throw whatever the promise rejected with.
|
||||||
|
* If the promise did not reject with an `Exception`, then this function will
|
||||||
|
* throw an `UnexpectedValueException` instead.
|
||||||
|
*
|
||||||
|
* If no $timeout is given and the promise stays pending, then this will
|
||||||
|
* potentially wait/block forever until the promise is settled.
|
||||||
|
*
|
||||||
|
* If a $timeout is given and the promise is still pending once the timeout
|
||||||
|
* triggers, this will cancel() the promise and throw a `TimeoutException`.
|
||||||
|
* This implies that if you pass a really small (or negative) value, it will still
|
||||||
|
* start a timer and will thus trigger at the earliest possible time in the future.
|
||||||
|
*
|
||||||
|
* @param PromiseInterface $promise
|
||||||
|
* @param LoopInterface $loop
|
||||||
|
* @param null|float $timeout (optional) maximum timeout in seconds or null=wait forever
|
||||||
|
* @return mixed returns whatever the promise resolves to
|
||||||
|
* @throws Exception when the promise is rejected
|
||||||
|
* @throws TimeoutException if the $timeout is given and triggers
|
||||||
|
*/
|
||||||
|
function await(PromiseInterface $promise, LoopInterface $loop, $timeout = null)
|
||||||
|
{
|
||||||
|
$wait = true;
|
||||||
|
$resolved = null;
|
||||||
|
$exception = null;
|
||||||
|
$rejected = false;
|
||||||
|
|
||||||
|
if ($timeout !== null) {
|
||||||
|
$promise = Timer\timeout($promise, $timeout, $loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
$promise->then(
|
||||||
|
function ($c) use (&$resolved, &$wait, $loop) {
|
||||||
|
$resolved = $c;
|
||||||
|
$wait = false;
|
||||||
|
$loop->stop();
|
||||||
|
},
|
||||||
|
function ($error) use (&$exception, &$rejected, &$wait, $loop) {
|
||||||
|
$exception = $error;
|
||||||
|
$rejected = true;
|
||||||
|
$wait = false;
|
||||||
|
$loop->stop();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
while ($wait) {
|
||||||
|
$loop->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($rejected) {
|
||||||
|
if (!$exception instanceof \Exception) {
|
||||||
|
$exception = new \UnexpectedValueException(
|
||||||
|
'Promise rejected with unexpected value of type ' . (is_object(($exception) ? get_class($exception) : gettype($exception))),
|
||||||
|
0,
|
||||||
|
$exception instanceof \Throwable ? $exception : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wait for ANY of the given promises to resolve
|
||||||
|
*
|
||||||
|
* Once the first promise is resolved, this will try to cancel() all
|
||||||
|
* remaining promises and return whatever the first promise resolves to.
|
||||||
|
*
|
||||||
|
* If ALL promises fail to resolve, this will fail and throw an Exception.
|
||||||
|
*
|
||||||
|
* If no $timeout is given and either promise stays pending, then this will
|
||||||
|
* potentially wait/block forever until the last promise is settled.
|
||||||
|
*
|
||||||
|
* If a $timeout is given and either promise is still pending once the timeout
|
||||||
|
* triggers, this will cancel() all pending promises and throw a `TimeoutException`.
|
||||||
|
* This implies that if you pass a really small (or negative) value, it will still
|
||||||
|
* start a timer and will thus trigger at the earliest possible time in the future.
|
||||||
|
*
|
||||||
|
* @param array $promises
|
||||||
|
* @param LoopInterface $loop
|
||||||
|
* @param null|float $timeout (optional) maximum timeout in seconds or null=wait forever
|
||||||
|
* @return mixed returns whatever the first promise resolves to
|
||||||
|
* @throws Exception if ALL promises are rejected
|
||||||
|
* @throws TimeoutException if the $timeout is given and triggers
|
||||||
|
*/
|
||||||
|
function awaitAny(array $promises, LoopInterface $loop, $timeout = null)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Promise\any() does not cope with an empty input array, so reject this here
|
||||||
|
if (!$promises) {
|
||||||
|
throw new UnderflowException('Empty input array');
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = await(Promise\any($promises)->then(null, function () {
|
||||||
|
// rejects with an array of rejection reasons => reject with Exception instead
|
||||||
|
throw new Exception('All promises rejected');
|
||||||
|
}), $loop, $timeout);
|
||||||
|
} catch (TimeoutException $e) {
|
||||||
|
// the timeout fired
|
||||||
|
// => try to cancel all promises (rejected ones will be ignored anyway)
|
||||||
|
_cancelAllPromises($promises);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// if the above throws, then ALL promises are already rejected
|
||||||
|
// => try to cancel all promises (rejected ones will be ignored anyway)
|
||||||
|
_cancelAllPromises($promises);
|
||||||
|
|
||||||
|
throw new UnderflowException('No promise could resolve', 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we reach this, then ANY of the given promises resolved
|
||||||
|
// => try to cancel all promises (settled ones will be ignored anyway)
|
||||||
|
_cancelAllPromises($promises);
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wait for ALL of the given promises to resolve
|
||||||
|
*
|
||||||
|
* Once the last promise resolves, this will return an array with whatever
|
||||||
|
* each promise resolves to. Array keys will be left intact, i.e. they can
|
||||||
|
* be used to correlate the return array to the promises passed.
|
||||||
|
*
|
||||||
|
* If ANY promise fails to resolve, this will try to cancel() all
|
||||||
|
* remaining promises and throw an Exception.
|
||||||
|
* If the promise did not reject with an `Exception`, then this function will
|
||||||
|
* throw an `UnexpectedValueException` instead.
|
||||||
|
*
|
||||||
|
* If no $timeout is given and either promise stays pending, then this will
|
||||||
|
* potentially wait/block forever until the last promise is settled.
|
||||||
|
*
|
||||||
|
* If a $timeout is given and either promise is still pending once the timeout
|
||||||
|
* triggers, this will cancel() all pending promises and throw a `TimeoutException`.
|
||||||
|
* This implies that if you pass a really small (or negative) value, it will still
|
||||||
|
* start a timer and will thus trigger at the earliest possible time in the future.
|
||||||
|
*
|
||||||
|
* @param array $promises
|
||||||
|
* @param LoopInterface $loop
|
||||||
|
* @param null|float $timeout (optional) maximum timeout in seconds or null=wait forever
|
||||||
|
* @return array returns an array with whatever each promise resolves to
|
||||||
|
* @throws Exception when ANY promise is rejected
|
||||||
|
* @throws TimeoutException if the $timeout is given and triggers
|
||||||
|
*/
|
||||||
|
function awaitAll(array $promises, LoopInterface $loop, $timeout = null)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return await(Promise\all($promises), $loop, $timeout);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// ANY of the given promises rejected or the timeout fired
|
||||||
|
// => try to cancel all promises (rejected ones will be ignored anyway)
|
||||||
|
_cancelAllPromises($promises);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* internal helper function used to iterate over an array of Promise instances and cancel() each
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @param array $promises
|
||||||
|
*/
|
||||||
|
function _cancelAllPromises(array $promises)
|
||||||
|
{
|
||||||
|
foreach ($promises as $promise) {
|
||||||
|
if ($promise instanceof CancellablePromiseInterface) {
|
||||||
|
$promise->cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
vendor/clue/buzz-react/LICENSE
vendored
Normal file
21
vendor/clue/buzz-react/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Christian Lück
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
186
vendor/clue/buzz-react/src/Browser.php
vendored
Normal file
186
vendor/clue/buzz-react/src/Browser.php
vendored
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Buzz;
|
||||||
|
|
||||||
|
use Clue\React\Buzz\Io\Sender;
|
||||||
|
use Clue\React\Buzz\Io\Transaction;
|
||||||
|
use Clue\React\Buzz\Message\MessageFactory;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\UriInterface;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
|
||||||
|
class Browser
|
||||||
|
{
|
||||||
|
private $sender;
|
||||||
|
private $messageFactory;
|
||||||
|
private $baseUri = null;
|
||||||
|
private $options = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate the Browser
|
||||||
|
*
|
||||||
|
* @param LoopInterface $loop
|
||||||
|
* @param ConnectorInterface|null $connector [optional] Connector to use.
|
||||||
|
* Should be `null` in order to use default Connector.
|
||||||
|
*/
|
||||||
|
public function __construct(LoopInterface $loop, ConnectorInterface $connector = null)
|
||||||
|
{
|
||||||
|
$this->sender = Sender::createFromLoop($loop, $connector);
|
||||||
|
$this->messageFactory = new MessageFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|UriInterface $url URI for the request.
|
||||||
|
* @param array $headers
|
||||||
|
* @return PromiseInterface
|
||||||
|
*/
|
||||||
|
public function get($url, array $headers = array())
|
||||||
|
{
|
||||||
|
return $this->send($this->messageFactory->request('GET', $url, $headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|UriInterface $url URI for the request.
|
||||||
|
* @param array $headers
|
||||||
|
* @param string $content
|
||||||
|
* @return PromiseInterface
|
||||||
|
*/
|
||||||
|
public function post($url, array $headers = array(), $content = '')
|
||||||
|
{
|
||||||
|
return $this->send($this->messageFactory->request('POST', $url, $headers, $content));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|UriInterface $url URI for the request.
|
||||||
|
* @param array $headers
|
||||||
|
* @return PromiseInterface
|
||||||
|
*/
|
||||||
|
public function head($url, array $headers = array())
|
||||||
|
{
|
||||||
|
return $this->send($this->messageFactory->request('HEAD', $url, $headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|UriInterface $url URI for the request.
|
||||||
|
* @param array $headers
|
||||||
|
* @param string $content
|
||||||
|
* @return PromiseInterface
|
||||||
|
*/
|
||||||
|
public function patch($url, array $headers = array(), $content = '')
|
||||||
|
{
|
||||||
|
return $this->send($this->messageFactory->request('PATCH', $url , $headers, $content));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|UriInterface $url URI for the request.
|
||||||
|
* @param array $headers
|
||||||
|
* @param string $content
|
||||||
|
* @return PromiseInterface
|
||||||
|
*/
|
||||||
|
public function put($url, array $headers = array(), $content = '')
|
||||||
|
{
|
||||||
|
return $this->send($this->messageFactory->request('PUT', $url, $headers, $content));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|UriInterface $url URI for the request.
|
||||||
|
* @param array $headers
|
||||||
|
* @param string $content
|
||||||
|
* @return PromiseInterface
|
||||||
|
*/
|
||||||
|
public function delete($url, array $headers = array(), $content = '')
|
||||||
|
{
|
||||||
|
return $this->send($this->messageFactory->request('DELETE', $url, $headers, $content));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|UriInterface $url URI for the request.
|
||||||
|
* @param array $fields
|
||||||
|
* @param array $headers
|
||||||
|
* @param string $method
|
||||||
|
* @return PromiseInterface
|
||||||
|
*/
|
||||||
|
public function submit($url, array $fields, $headers = array(), $method = 'POST')
|
||||||
|
{
|
||||||
|
$headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||||
|
$content = http_build_query($fields);
|
||||||
|
|
||||||
|
return $this->send($this->messageFactory->request($method, $url, $headers, $content));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @return PromiseInterface
|
||||||
|
*/
|
||||||
|
public function send(RequestInterface $request)
|
||||||
|
{
|
||||||
|
if ($this->baseUri !== null) {
|
||||||
|
// ensure we're actually below the base URI
|
||||||
|
$request = $request->withUri($this->messageFactory->expandBase($request->getUri(), $this->baseUri));
|
||||||
|
}
|
||||||
|
|
||||||
|
$transaction = new Transaction($request, $this->sender, $this->options, $this->messageFactory);
|
||||||
|
|
||||||
|
return $transaction->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Browser instance with the given absolute base URI
|
||||||
|
*
|
||||||
|
* This is mostly useful for using (RESTful) HTTP APIs.
|
||||||
|
* Any relative URI passed to any of the request methods will simply be
|
||||||
|
* appended behind the given `$baseUri`.
|
||||||
|
*
|
||||||
|
* By definition of this library, a given base URI MUST always absolute and
|
||||||
|
* can not contain any placeholders.
|
||||||
|
*
|
||||||
|
* @param string|UriInterface $baseUri absolute base URI
|
||||||
|
* @return self
|
||||||
|
* @throws InvalidArgumentException if the given $baseUri is not a valid absolute URI
|
||||||
|
* @see self::withoutBase()
|
||||||
|
*/
|
||||||
|
public function withBase($baseUri)
|
||||||
|
{
|
||||||
|
$browser = clone $this;
|
||||||
|
$browser->baseUri = $this->messageFactory->uri($baseUri);
|
||||||
|
|
||||||
|
if ($browser->baseUri->getScheme() === '' || $browser->baseUri->getHost() === '') {
|
||||||
|
throw new \InvalidArgumentException('Base URI must be absolute');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Browser instance *without* a base URL
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
* @see self::withBase()
|
||||||
|
*/
|
||||||
|
public function withoutBase()
|
||||||
|
{
|
||||||
|
$browser = clone $this;
|
||||||
|
$browser->baseUri = null;
|
||||||
|
|
||||||
|
return $browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $options
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function withOptions(array $options)
|
||||||
|
{
|
||||||
|
$browser = clone $this;
|
||||||
|
|
||||||
|
// merge all options, but remove those explicitly assigned a null value
|
||||||
|
$browser->options = array_filter($options + $this->options, function ($value) {
|
||||||
|
return ($value !== null);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $browser;
|
||||||
|
}
|
||||||
|
}
|
153
vendor/clue/buzz-react/src/Io/Sender.php
vendored
Normal file
153
vendor/clue/buzz-react/src/Io/Sender.php
vendored
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Buzz\Io;
|
||||||
|
|
||||||
|
use Clue\React\Buzz\Message\MessageFactory;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\HttpClient\Client as HttpClient;
|
||||||
|
use React\HttpClient\Request as RequestStream;
|
||||||
|
use React\HttpClient\Response as ResponseStream;
|
||||||
|
use React\Promise;
|
||||||
|
use React\Promise\Deferred;
|
||||||
|
use React\Socket\Connector;
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
use React\Stream\ReadableStreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Internal] Sends requests and receives responses
|
||||||
|
*
|
||||||
|
* The `Sender` is responsible for passing the [`RequestInterface`](#requestinterface) objects to
|
||||||
|
* the underlying [`HttpClient`](https://github.com/reactphp/http-client) library
|
||||||
|
* and keeps track of its transmission and converts its reponses back to [`ResponseInterface`](#responseinterface) objects.
|
||||||
|
*
|
||||||
|
* It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage)
|
||||||
|
* and the default [`Connector`](https://github.com/reactphp/socket-client) and [DNS `Resolver`](https://github.com/reactphp/dns).
|
||||||
|
*
|
||||||
|
* The `Sender` class mostly exists in order to abstract changes on the underlying
|
||||||
|
* components away from this package in order to provide backwards and forwards
|
||||||
|
* compatibility.
|
||||||
|
*
|
||||||
|
* @internal You SHOULD NOT rely on this API, it is subject to change without prior notice!
|
||||||
|
* @see Browser
|
||||||
|
*/
|
||||||
|
class Sender
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* create a new default sender attached to the given event loop
|
||||||
|
*
|
||||||
|
* This method is used internally to create the "default sender".
|
||||||
|
*
|
||||||
|
* You may also use this method if you need custom DNS or connector
|
||||||
|
* settings. You can use this method manually like this:
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* $connector = new \React\Socket\Connector($loop);
|
||||||
|
* $sender = \Clue\React\Buzz\Io\Sender::createFromLoop($loop, $connector);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param LoopInterface $loop
|
||||||
|
* @param ConnectorInterface|null $connector
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function createFromLoop(LoopInterface $loop, ConnectorInterface $connector = null)
|
||||||
|
{
|
||||||
|
return new self(new HttpClient($loop, $connector));
|
||||||
|
}
|
||||||
|
|
||||||
|
private $http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [internal] Instantiate Sender
|
||||||
|
*
|
||||||
|
* @param HttpClient $http
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function __construct(HttpClient $http)
|
||||||
|
{
|
||||||
|
$this->http = $http;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param MessageFactory $messageFactory
|
||||||
|
* @return PromiseInterface Promise<ResponseInterface, Exception>
|
||||||
|
*/
|
||||||
|
public function send(RequestInterface $request, MessageFactory $messageFactory)
|
||||||
|
{
|
||||||
|
$uri = $request->getUri();
|
||||||
|
|
||||||
|
// URIs are required to be absolute for the HttpClient to work
|
||||||
|
if ($uri->getScheme() === '' || $uri->getHost() === '') {
|
||||||
|
return Promise\reject(new \InvalidArgumentException('Sending request requires absolute URI with scheme and host'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $request->getBody();
|
||||||
|
|
||||||
|
// automatically assign a Content-Length header if the body size is known
|
||||||
|
if ($body->getSize() !== null && $body->getSize() !== 0 && $request->hasHeader('Content-Length') !== null) {
|
||||||
|
$request = $request->withHeader('Content-Length', $body->getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($body instanceof ReadableStreamInterface && $body->isReadable() && !$request->hasHeader('Content-Length')) {
|
||||||
|
$request = $request->withHeader('Transfer-Encoding', 'chunked');
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers = array();
|
||||||
|
foreach ($request->getHeaders() as $name => $values) {
|
||||||
|
$headers[$name] = implode(', ', $values);
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestStream = $this->http->request($request->getMethod(), (string)$uri, $headers, $request->getProtocolVersion());
|
||||||
|
|
||||||
|
$deferred = new Deferred(function ($_, $reject) use ($requestStream) {
|
||||||
|
// close request stream if request is canceled
|
||||||
|
$reject(new \RuntimeException('Request canceled'));
|
||||||
|
$requestStream->close();
|
||||||
|
});
|
||||||
|
|
||||||
|
$requestStream->on('error', function($error) use ($deferred) {
|
||||||
|
$deferred->reject($error);
|
||||||
|
});
|
||||||
|
|
||||||
|
$requestStream->on('response', function (ResponseStream $responseStream) use ($deferred, $messageFactory) {
|
||||||
|
// apply response header values from response stream
|
||||||
|
$deferred->resolve($messageFactory->response(
|
||||||
|
$responseStream->getVersion(),
|
||||||
|
$responseStream->getCode(),
|
||||||
|
$responseStream->getReasonPhrase(),
|
||||||
|
$responseStream->getHeaders(),
|
||||||
|
$responseStream
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($body instanceof ReadableStreamInterface) {
|
||||||
|
if ($body->isReadable()) {
|
||||||
|
if ($request->hasHeader('Content-Length')) {
|
||||||
|
// length is known => just write to request
|
||||||
|
$body->pipe($requestStream);
|
||||||
|
} else {
|
||||||
|
// length unknown => apply chunked transfer-encoding
|
||||||
|
// this should be moved somewhere else obviously
|
||||||
|
$body->on('data', function ($data) use ($requestStream) {
|
||||||
|
$requestStream->write(dechex(strlen($data)) . "\r\n" . $data . "\r\n");
|
||||||
|
});
|
||||||
|
$body->on('end', function() use ($requestStream) {
|
||||||
|
$requestStream->end("0\r\n\r\n");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// stream is not readable => end request without body
|
||||||
|
$requestStream->end();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// body is fully buffered => write as one chunk
|
||||||
|
$requestStream->end((string)$body);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $deferred->promise();
|
||||||
|
}
|
||||||
|
}
|
188
vendor/clue/buzz-react/src/Io/Transaction.php
vendored
Normal file
188
vendor/clue/buzz-react/src/Io/Transaction.php
vendored
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Buzz\Io;
|
||||||
|
|
||||||
|
use Clue\React\Buzz\Browser;
|
||||||
|
use Clue\React\Buzz\Io\Sender;
|
||||||
|
use Clue\React\Buzz\Message\ResponseException;
|
||||||
|
use Clue\React\Buzz\Message\MessageFactory;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\UriInterface;
|
||||||
|
use React\Promise;
|
||||||
|
use React\Promise\Stream;
|
||||||
|
use React\Stream\ReadableStreamInterface;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class Transaction
|
||||||
|
{
|
||||||
|
private $browser;
|
||||||
|
private $request;
|
||||||
|
private $messageFactory;
|
||||||
|
|
||||||
|
private $numRequests = 0;
|
||||||
|
|
||||||
|
// context: http.follow_location
|
||||||
|
private $followRedirects = true;
|
||||||
|
|
||||||
|
// context: http.max_redirects
|
||||||
|
private $maxRedirects = 10;
|
||||||
|
|
||||||
|
// context: http.ignore_errors
|
||||||
|
private $obeySuccessCode = true;
|
||||||
|
|
||||||
|
private $streaming = false;
|
||||||
|
|
||||||
|
public function __construct(RequestInterface $request, Sender $sender, array $options = array(), MessageFactory $messageFactory)
|
||||||
|
{
|
||||||
|
foreach ($options as $name => $value) {
|
||||||
|
if (property_exists($this, $name)) {
|
||||||
|
$this->$name = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->request = $request;
|
||||||
|
$this->sender = $sender;
|
||||||
|
$this->messageFactory = $messageFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send()
|
||||||
|
{
|
||||||
|
return $this->next($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function next(RequestInterface $request)
|
||||||
|
{
|
||||||
|
$this->progress('request', array($request));
|
||||||
|
|
||||||
|
$that = $this;
|
||||||
|
++$this->numRequests;
|
||||||
|
|
||||||
|
$promise = $this->sender->send($request, $this->messageFactory);
|
||||||
|
|
||||||
|
if (!$this->streaming) {
|
||||||
|
$promise = $promise->then(array($that, 'bufferResponse'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $promise->then(
|
||||||
|
function (ResponseInterface $response) use ($request, $that) {
|
||||||
|
return $that->onResponse($response, $request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @return PromiseInterface Promise<ResponseInterface, Exception>
|
||||||
|
*/
|
||||||
|
public function bufferResponse(ResponseInterface $response)
|
||||||
|
{
|
||||||
|
$stream = $response->getBody();
|
||||||
|
|
||||||
|
// body is not streaming => already buffered
|
||||||
|
if (!$stream instanceof ReadableStreamInterface) {
|
||||||
|
return Promise\resolve($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// buffer stream and resolve with buffered body
|
||||||
|
$messageFactory = $this->messageFactory;
|
||||||
|
return Stream\buffer($stream)->then(
|
||||||
|
function ($body) use ($response, $messageFactory) {
|
||||||
|
return $response->withBody($messageFactory->body($body));
|
||||||
|
},
|
||||||
|
function ($e) use ($stream) {
|
||||||
|
// try to close stream if buffering fails (or is cancelled)
|
||||||
|
$stream->close();
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @throws ResponseException
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function onResponse(ResponseInterface $response, RequestInterface $request)
|
||||||
|
{
|
||||||
|
$this->progress('response', array($response, $request));
|
||||||
|
|
||||||
|
if ($this->followRedirects && ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400)) {
|
||||||
|
return $this->onResponseRedirect($response, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only status codes 200-399 are considered to be valid, reject otherwise
|
||||||
|
if ($this->obeySuccessCode && ($response->getStatusCode() < 200 || $response->getStatusCode() >= 400)) {
|
||||||
|
throw new ResponseException($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve our initial promise
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onResponseRedirect(ResponseInterface $response, RequestInterface $request)
|
||||||
|
{
|
||||||
|
// resolve location relative to last request URI
|
||||||
|
$location = $this->messageFactory->uriRelative($request->getUri(), $response->getHeaderLine('Location'));
|
||||||
|
|
||||||
|
$request = $this->makeRedirectRequest($request, $location);
|
||||||
|
$this->progress('redirect', array($request));
|
||||||
|
|
||||||
|
if ($this->numRequests >= $this->maxRedirects) {
|
||||||
|
throw new \RuntimeException('Maximum number of redirects (' . $this->maxRedirects . ') exceeded');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param UriInterface $location
|
||||||
|
* @return \Clue\React\Buzz\Message\RequestInterface
|
||||||
|
*/
|
||||||
|
private function makeRedirectRequest(RequestInterface $request, UriInterface $location)
|
||||||
|
{
|
||||||
|
$originalHost = $request->getUri()->getHost();
|
||||||
|
$request = $request
|
||||||
|
->withoutHeader('Host')
|
||||||
|
->withoutHeader('Content-Type')
|
||||||
|
->withoutHeader('Content-Length');
|
||||||
|
|
||||||
|
// Remove authorization if changing hostnames (but not if just changing ports or protocols).
|
||||||
|
if ($location->getHost() !== $originalHost) {
|
||||||
|
$request = $request->withoutHeader('Authentication');
|
||||||
|
}
|
||||||
|
|
||||||
|
// naïve approach..
|
||||||
|
$method = ($request->getMethod() === 'HEAD') ? 'HEAD' : 'GET';
|
||||||
|
|
||||||
|
return $this->messageFactory->request($method, $location, $request->getHeaders());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function progress($name, array $args = array())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
|
||||||
|
echo $name;
|
||||||
|
|
||||||
|
foreach ($args as $arg) {
|
||||||
|
echo ' ';
|
||||||
|
if ($arg instanceof ResponseInterface) {
|
||||||
|
echo 'HTTP/' . $arg->getProtocolVersion() . ' ' . $arg->getStatusCode() . ' ' . $arg->getReasonPhrase();
|
||||||
|
} elseif ($arg instanceof RequestInterface) {
|
||||||
|
echo $arg->getMethod() . ' ' . $arg->getRequestTarget() . ' HTTP/' . $arg->getProtocolVersion();
|
||||||
|
} else {
|
||||||
|
echo $arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
127
vendor/clue/buzz-react/src/Message/MessageFactory.php
vendored
Normal file
127
vendor/clue/buzz-react/src/Message/MessageFactory.php
vendored
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Buzz\Message;
|
||||||
|
|
||||||
|
use RingCentral\Psr7\Request;
|
||||||
|
use RingCentral\Psr7\Uri;
|
||||||
|
use RingCentral\Psr7\Response;
|
||||||
|
use Psr\Http\Message\UriInterface;
|
||||||
|
use RingCentral;
|
||||||
|
use React\Stream\ReadableStreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class MessageFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a new instance of RequestInterface for the given request parameters
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param string|UriInterface $uri
|
||||||
|
* @param array $headers
|
||||||
|
* @param string|ReadableStreamInterface $content
|
||||||
|
* @return RequestInterface
|
||||||
|
*/
|
||||||
|
public function request($method, $uri, $headers = array(), $content = '')
|
||||||
|
{
|
||||||
|
return new Request($method, $uri, $headers, $this->body($content), '1.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of ResponseInterface for the given response parameters
|
||||||
|
*
|
||||||
|
* @param string $version
|
||||||
|
* @param int $status
|
||||||
|
* @param string $reason
|
||||||
|
* @param array $headers
|
||||||
|
* @param ReadableStreamInterface|string $body
|
||||||
|
* @return ResponseInterface
|
||||||
|
* @uses self::body()
|
||||||
|
*/
|
||||||
|
public function response($version, $status, $reason, $headers = array(), $body = '')
|
||||||
|
{
|
||||||
|
return new Response($status, $headers, $this->body($body), $version, $reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of StreamInterface for the given body contents
|
||||||
|
*
|
||||||
|
* @param ReadableStreamInterface|string $body
|
||||||
|
* @return StreamInterface
|
||||||
|
*/
|
||||||
|
public function body($body)
|
||||||
|
{
|
||||||
|
if ($body instanceof ReadableStreamInterface) {
|
||||||
|
return new ReadableBodyStream($body);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RingCentral\Psr7\stream_for($body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of UriInterface for the given URI string or instance
|
||||||
|
*
|
||||||
|
* @param UriInterface|string $uri
|
||||||
|
* @return UriInterface
|
||||||
|
*/
|
||||||
|
public function uri($uri)
|
||||||
|
{
|
||||||
|
return new Uri($uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of UriInterface for the given URI string relative to the given base URI
|
||||||
|
*
|
||||||
|
* @param UriInterface $base
|
||||||
|
* @param string $uri
|
||||||
|
* @return UriInterface
|
||||||
|
*/
|
||||||
|
public function uriRelative(UriInterface $base, $uri)
|
||||||
|
{
|
||||||
|
return Uri::resolve($base, $uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the given relative or absolute $uri by appending it behind $this base URI
|
||||||
|
*
|
||||||
|
* The given $uri parameter can be either a relative or absolute URI and
|
||||||
|
* as such can not contain any URI template placeholders.
|
||||||
|
*
|
||||||
|
* As such, the outcome of this method represents a valid, absolute URI
|
||||||
|
* which will be returned as an instance implementing `UriInterface`.
|
||||||
|
*
|
||||||
|
* If the given $uri is a relative URI, it will simply be appended behind $base URI.
|
||||||
|
*
|
||||||
|
* If the given $uri is an absolute URI, it will simply be verified to
|
||||||
|
* be *below* the given $base URI.
|
||||||
|
*
|
||||||
|
* @param UriInterface $uri
|
||||||
|
* @param UriInterface $base
|
||||||
|
* @return UriInterface
|
||||||
|
* @throws \UnexpectedValueException
|
||||||
|
* @see Browser::resolve()
|
||||||
|
*/
|
||||||
|
public function expandBase(UriInterface $uri, UriInterface $base)
|
||||||
|
{
|
||||||
|
if ($uri->getScheme() !== '') {
|
||||||
|
if (strpos((string)$uri, (string)$base) !== 0) {
|
||||||
|
throw new \UnexpectedValueException('Invalid base, "' . $uri . '" does not appear to be below "' . $base . '"');
|
||||||
|
}
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri = (string)$uri;
|
||||||
|
$base = (string)$base;
|
||||||
|
|
||||||
|
if ($uri !== '' && substr($base, -1) !== '/' && substr($uri, 0, 1) !== '?') {
|
||||||
|
$base .= '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($uri[0]) && $uri[0] === '/') {
|
||||||
|
$uri = substr($uri, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->uri($base . $uri);
|
||||||
|
}
|
||||||
|
}
|
135
vendor/clue/buzz-react/src/Message/ReadableBodyStream.php
vendored
Normal file
135
vendor/clue/buzz-react/src/Message/ReadableBodyStream.php
vendored
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Buzz\Message;
|
||||||
|
|
||||||
|
use Evenement\EventEmitter;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use React\Stream\ReadableStreamInterface;
|
||||||
|
use React\Stream\Util;
|
||||||
|
use React\Stream\WritableStreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class ReadableBodyStream extends EventEmitter implements ReadableStreamInterface, StreamInterface
|
||||||
|
{
|
||||||
|
private $input;
|
||||||
|
private $closed = false;
|
||||||
|
|
||||||
|
public function __construct(ReadableStreamInterface $input)
|
||||||
|
{
|
||||||
|
$this->input = $input;
|
||||||
|
|
||||||
|
$that = $this;
|
||||||
|
$input->on('data', function ($data) use ($that) {
|
||||||
|
$that->emit('data', array($data));
|
||||||
|
});
|
||||||
|
$input->on('error', function ($error) use ($that) {
|
||||||
|
$that->emit('error', array($error));
|
||||||
|
$that->close();
|
||||||
|
});
|
||||||
|
$input->on('end', function () use ($that) {
|
||||||
|
$that->emit('end');
|
||||||
|
$that->close();
|
||||||
|
});
|
||||||
|
$input->on('close', array($that, 'close'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
if (!$this->closed) {
|
||||||
|
$this->closed = true;
|
||||||
|
$this->input->close();
|
||||||
|
|
||||||
|
$this->emit('close');
|
||||||
|
$this->removeAllListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isReadable()
|
||||||
|
{
|
||||||
|
return $this->input->isReadable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pause()
|
||||||
|
{
|
||||||
|
$this->input->pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resume()
|
||||||
|
{
|
||||||
|
$this->input->resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pipe(WritableStreamInterface $dest, array $options = array())
|
||||||
|
{
|
||||||
|
Util::pipe($this, $dest, $options);
|
||||||
|
|
||||||
|
return $dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eof()
|
||||||
|
{
|
||||||
|
return !$this->isReadable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function detach()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSize()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tell()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isSeekable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function seek($offset, $whence = SEEK_SET)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isWritable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($string)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read($length)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContents()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMetadata($key = null)
|
||||||
|
{
|
||||||
|
return ($key === null) ? array() : null;
|
||||||
|
}
|
||||||
|
}
|
39
vendor/clue/buzz-react/src/Message/ResponseException.php
vendored
Normal file
39
vendor/clue/buzz-react/src/Message/ResponseException.php
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Buzz\Message;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ResponseException will be returned for valid Response objects that use an HTTP error code
|
||||||
|
*
|
||||||
|
* You can access the original ResponseInterface object via its getter.
|
||||||
|
*/
|
||||||
|
class ResponseException extends RuntimeException
|
||||||
|
{
|
||||||
|
private $response;
|
||||||
|
|
||||||
|
public function __construct(ResponseInterface $response, $message = null, $code = null, $previous = null)
|
||||||
|
{
|
||||||
|
if ($message === null) {
|
||||||
|
$message = 'HTTP status code ' . $response->getStatusCode() . ' (' . $response->getReasonPhrase() . ')';
|
||||||
|
}
|
||||||
|
if ($code === null) {
|
||||||
|
$code = $response->getStatusCode();
|
||||||
|
}
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
|
||||||
|
$this->response = $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get Response message object
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function getResponse()
|
||||||
|
{
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
}
|
21
vendor/clue/connection-manager-extra/LICENSE
vendored
Normal file
21
vendor/clue/connection-manager-extra/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Christian Lück
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
30
vendor/clue/connection-manager-extra/src/ConnectionManagerDelay.php
vendored
Normal file
30
vendor/clue/connection-manager-extra/src/ConnectionManagerDelay.php
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ConnectionManager\Extra;
|
||||||
|
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\Promise\Timer;
|
||||||
|
|
||||||
|
class ConnectionManagerDelay implements ConnectorInterface
|
||||||
|
{
|
||||||
|
private $connectionManager;
|
||||||
|
private $delay;
|
||||||
|
private $loop;
|
||||||
|
|
||||||
|
public function __construct(ConnectorInterface $connectionManager, $delay, LoopInterface $loop)
|
||||||
|
{
|
||||||
|
$this->connectionManager = $connectionManager;
|
||||||
|
$this->delay = $delay;
|
||||||
|
$this->loop = $loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connect($uri)
|
||||||
|
{
|
||||||
|
$connectionManager = $this->connectionManager;
|
||||||
|
|
||||||
|
return Timer\resolve($this->delay, $this->loop)->then(function () use ($connectionManager, $uri) {
|
||||||
|
return $connectionManager->connect($uri);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
41
vendor/clue/connection-manager-extra/src/ConnectionManagerReject.php
vendored
Normal file
41
vendor/clue/connection-manager-extra/src/ConnectionManagerReject.php
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ConnectionManager\Extra;
|
||||||
|
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
use React\Promise;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
// a simple connection manager that rejects every single connection attempt
|
||||||
|
class ConnectionManagerReject implements ConnectorInterface
|
||||||
|
{
|
||||||
|
private $reason = 'Connection rejected';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param null|string|callable $reason
|
||||||
|
*/
|
||||||
|
public function __construct($reason = null)
|
||||||
|
{
|
||||||
|
if ($reason !== null) {
|
||||||
|
$this->reason = $reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connect($uri)
|
||||||
|
{
|
||||||
|
$reason = $this->reason;
|
||||||
|
if (!is_string($reason)) {
|
||||||
|
try {
|
||||||
|
$reason = $reason($uri);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$reason = $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$reason instanceof \Exception) {
|
||||||
|
$reason = new Exception($reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise\reject($reason);
|
||||||
|
}
|
||||||
|
}
|
52
vendor/clue/connection-manager-extra/src/ConnectionManagerRepeat.php
vendored
Normal file
52
vendor/clue/connection-manager-extra/src/ConnectionManagerRepeat.php
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ConnectionManager\Extra;
|
||||||
|
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Exception;
|
||||||
|
use React\Promise\Promise;
|
||||||
|
use React\Promise\CancellablePromiseInterface;
|
||||||
|
|
||||||
|
class ConnectionManagerRepeat implements ConnectorInterface
|
||||||
|
{
|
||||||
|
protected $connectionManager;
|
||||||
|
protected $maximumTries;
|
||||||
|
|
||||||
|
public function __construct(ConnectorInterface $connectionManager, $maximumTries)
|
||||||
|
{
|
||||||
|
if ($maximumTries < 1) {
|
||||||
|
throw new InvalidArgumentException('Maximum number of tries must be >= 1');
|
||||||
|
}
|
||||||
|
$this->connectionManager = $connectionManager;
|
||||||
|
$this->maximumTries = $maximumTries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connect($uri)
|
||||||
|
{
|
||||||
|
$tries = $this->maximumTries;
|
||||||
|
$connector = $this->connectionManager;
|
||||||
|
|
||||||
|
return new Promise(function ($resolve, $reject) use ($uri, &$pending, &$tries, $connector) {
|
||||||
|
$try = function ($error = null) use (&$try, &$pending, &$tries, $uri, $connector, $resolve, $reject) {
|
||||||
|
if ($tries > 0) {
|
||||||
|
--$tries;
|
||||||
|
$pending = $connector->connect($uri);
|
||||||
|
$pending->then($resolve, $try);
|
||||||
|
} else {
|
||||||
|
$reject(new Exception('Connection still fails even after retrying', 0, $error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$try();
|
||||||
|
}, function ($_, $reject) use (&$pending, &$tries) {
|
||||||
|
// stop retrying, reject results and cancel pending attempt
|
||||||
|
$tries = 0;
|
||||||
|
$reject(new \RuntimeException('Cancelled'));
|
||||||
|
|
||||||
|
if ($pending instanceof CancellablePromiseInterface) {
|
||||||
|
$pending->cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
26
vendor/clue/connection-manager-extra/src/ConnectionManagerSwappable.php
vendored
Normal file
26
vendor/clue/connection-manager-extra/src/ConnectionManagerSwappable.php
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ConnectionManager\Extra;
|
||||||
|
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
|
||||||
|
// connection manager decorator which simplifies exchanging the actual connection manager during runtime
|
||||||
|
class ConnectionManagerSwappable implements ConnectorInterface
|
||||||
|
{
|
||||||
|
protected $connectionManager;
|
||||||
|
|
||||||
|
public function __construct(ConnectorInterface $connectionManager)
|
||||||
|
{
|
||||||
|
$this->connectionManager = $connectionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connect($uri)
|
||||||
|
{
|
||||||
|
return $this->connectionManager->connect($uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setConnectionManager(ConnectorInterface $connectionManager)
|
||||||
|
{
|
||||||
|
$this->connectionManager = $connectionManager;
|
||||||
|
}
|
||||||
|
}
|
35
vendor/clue/connection-manager-extra/src/ConnectionManagerTimeout.php
vendored
Normal file
35
vendor/clue/connection-manager-extra/src/ConnectionManagerTimeout.php
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ConnectionManager\Extra;
|
||||||
|
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\Promise\Timer;
|
||||||
|
|
||||||
|
class ConnectionManagerTimeout implements ConnectorInterface
|
||||||
|
{
|
||||||
|
private $connectionManager;
|
||||||
|
private $timeout;
|
||||||
|
private $loop;
|
||||||
|
|
||||||
|
public function __construct(ConnectorInterface $connectionManager, $timeout, LoopInterface $loop)
|
||||||
|
{
|
||||||
|
$this->connectionManager = $connectionManager;
|
||||||
|
$this->timeout = $timeout;
|
||||||
|
$this->loop = $loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connect($uri)
|
||||||
|
{
|
||||||
|
$promise = $this->connectionManager->connect($uri);
|
||||||
|
|
||||||
|
return Timer\timeout($promise, $this->timeout, $this->loop)->then(null, function ($e) use ($promise) {
|
||||||
|
// connection successfully established but timeout already expired => close successful connection
|
||||||
|
$promise->then(function ($connection) {
|
||||||
|
$connection->end();
|
||||||
|
});
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
36
vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerConcurrent.php
vendored
Normal file
36
vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerConcurrent.php
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ConnectionManager\Extra\Multiple;
|
||||||
|
|
||||||
|
use ConnectionManager\Extra\Multiple\ConnectionManagerConsecutive;
|
||||||
|
use React\Promise;
|
||||||
|
use React\Promise\CancellablePromiseInterface;
|
||||||
|
|
||||||
|
class ConnectionManagerConcurrent extends ConnectionManagerConsecutive
|
||||||
|
{
|
||||||
|
public function connect($uri)
|
||||||
|
{
|
||||||
|
$all = array();
|
||||||
|
foreach ($this->managers as $connector) {
|
||||||
|
/* @var $connection Connector */
|
||||||
|
$all []= $connector->connect($uri);
|
||||||
|
}
|
||||||
|
return Promise\any($all)->then(function ($conn) use ($all) {
|
||||||
|
// a connection attempt succeeded
|
||||||
|
// => cancel all pending connection attempts
|
||||||
|
foreach ($all as $promise) {
|
||||||
|
if ($promise instanceof CancellablePromiseInterface) {
|
||||||
|
$promise->cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if promise resolves despite cancellation, immediately close stream
|
||||||
|
$promise->then(function ($stream) use ($conn) {
|
||||||
|
if ($stream !== $conn) {
|
||||||
|
$stream->close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return $conn;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
62
vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerConsecutive.php
vendored
Normal file
62
vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerConsecutive.php
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ConnectionManager\Extra\Multiple;
|
||||||
|
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
use React\Promise;
|
||||||
|
use UnderflowException;
|
||||||
|
use React\Promise\CancellablePromiseInterface;
|
||||||
|
|
||||||
|
class ConnectionManagerConsecutive implements ConnectorInterface
|
||||||
|
{
|
||||||
|
protected $managers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param ConnectorInterface[] $managers
|
||||||
|
*/
|
||||||
|
public function __construct(array $managers)
|
||||||
|
{
|
||||||
|
if (!$managers) {
|
||||||
|
throw new \InvalidArgumentException('List of connectors must not be empty');
|
||||||
|
}
|
||||||
|
$this->managers = $managers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connect($uri)
|
||||||
|
{
|
||||||
|
return $this->tryConnection($this->managers, $uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param ConnectorInterface[] $managers
|
||||||
|
* @param string $uri
|
||||||
|
* @return Promise
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function tryConnection(array $managers, $uri)
|
||||||
|
{
|
||||||
|
return new Promise\Promise(function ($resolve, $reject) use (&$managers, &$pending, $uri) {
|
||||||
|
$try = function () use (&$try, &$managers, $uri, $resolve, $reject, &$pending) {
|
||||||
|
if (!$managers) {
|
||||||
|
return $reject(new UnderflowException('No more managers to try to connect through'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$manager = array_shift($managers);
|
||||||
|
$pending = $manager->connect($uri);
|
||||||
|
$pending->then($resolve, $try);
|
||||||
|
};
|
||||||
|
|
||||||
|
$try();
|
||||||
|
}, function ($_, $reject) use (&$managers, &$pending) {
|
||||||
|
// stop retrying, reject results and cancel pending attempt
|
||||||
|
$managers = array();
|
||||||
|
$reject(new \RuntimeException('Cancelled'));
|
||||||
|
|
||||||
|
if ($pending instanceof CancellablePromiseInterface) {
|
||||||
|
$pending->cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
14
vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerRandom.php
vendored
Normal file
14
vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerRandom.php
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ConnectionManager\Extra\Multiple;
|
||||||
|
|
||||||
|
class ConnectionManagerRandom extends ConnectionManagerConsecutive
|
||||||
|
{
|
||||||
|
public function connect($uri)
|
||||||
|
{
|
||||||
|
$managers = $this->managers;
|
||||||
|
shuffle($managers);
|
||||||
|
|
||||||
|
return $this->tryConnection($managers, $uri);
|
||||||
|
}
|
||||||
|
}
|
111
vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerSelective.php
vendored
Normal file
111
vendor/clue/connection-manager-extra/src/Multiple/ConnectionManagerSelective.php
vendored
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ConnectionManager\Extra\Multiple;
|
||||||
|
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
use React\Promise;
|
||||||
|
use UnderflowException;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
class ConnectionManagerSelective implements ConnectorInterface
|
||||||
|
{
|
||||||
|
private $managers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param ConnectorInterface[] $managers
|
||||||
|
*/
|
||||||
|
public function __construct(array $managers)
|
||||||
|
{
|
||||||
|
foreach ($managers as $filter => $manager) {
|
||||||
|
$host = $filter;
|
||||||
|
$portMin = 0;
|
||||||
|
$portMax = 65535;
|
||||||
|
|
||||||
|
// search colon (either single one OR preceded by "]" due to IPv6)
|
||||||
|
$colon = strrpos($host, ':');
|
||||||
|
if ($colon !== false && (strpos($host, ':') === $colon || substr($host, $colon - 1, 1) === ']' )) {
|
||||||
|
if (!isset($host[$colon + 1])) {
|
||||||
|
throw new InvalidArgumentException('Entry "' . $filter . '" has no port after colon');
|
||||||
|
}
|
||||||
|
|
||||||
|
$minus = strpos($host, '-', $colon);
|
||||||
|
if ($minus === false) {
|
||||||
|
$portMin = $portMax = (int)substr($host, $colon + 1);
|
||||||
|
|
||||||
|
if (substr($host, $colon + 1) !== (string)$portMin) {
|
||||||
|
throw new InvalidArgumentException('Entry "' . $filter . '" has no valid port after colon');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$portMin = (int)substr($host, $colon + 1, ($minus - $colon));
|
||||||
|
$portMax = (int)substr($host, $minus + 1);
|
||||||
|
|
||||||
|
if (substr($host, $colon + 1) !== ($portMin . '-' . $portMax)) {
|
||||||
|
throw new InvalidArgumentException('Entry "' . $filter . '" has no valid port range after colon');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($portMin > $portMax) {
|
||||||
|
throw new InvalidArgumentException('Entry "' . $filter . '" has port range mixed up');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$host = substr($host, 0, $colon);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($host === '') {
|
||||||
|
throw new InvalidArgumentException('Entry "' . $filter . '" has an empty host');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$manager instanceof ConnectorInterface) {
|
||||||
|
throw new InvalidArgumentException('Entry "' . $filter . '" is not a valid connector');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->managers = $managers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connect($uri)
|
||||||
|
{
|
||||||
|
$parts = parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri);
|
||||||
|
if (!isset($parts) || !isset($parts['scheme'], $parts['host'], $parts['port'])) {
|
||||||
|
return Promise\reject(new InvalidArgumentException('Invalid URI'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$connector = $this->getConnectorForTarget(
|
||||||
|
trim($parts['host'], '[]'),
|
||||||
|
$parts['port']
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($connector === null) {
|
||||||
|
return Promise\reject(new UnderflowException('No connector for given target found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $connector->connect($uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getConnectorForTarget($targetHost, $targetPort)
|
||||||
|
{
|
||||||
|
foreach ($this->managers as $host => $connector) {
|
||||||
|
$portMin = 0;
|
||||||
|
$portMax = 65535;
|
||||||
|
|
||||||
|
// search colon (either single one OR preceded by "]" due to IPv6)
|
||||||
|
$colon = strrpos($host, ':');
|
||||||
|
if ($colon !== false && (strpos($host, ':') === $colon || substr($host, $colon - 1, 1) === ']' )) {
|
||||||
|
$minus = strpos($host, '-', $colon);
|
||||||
|
if ($minus === false) {
|
||||||
|
$portMin = $portMax = (int)substr($host, $colon + 1);
|
||||||
|
} else {
|
||||||
|
$portMin = (int)substr($host, $colon + 1, ($minus - $colon));
|
||||||
|
$portMax = (int)substr($host, $minus + 1);
|
||||||
|
}
|
||||||
|
$host = trim(substr($host, 0, $colon), '[]');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($targetPort >= $portMin && $targetPort <= $portMax && fnmatch($host, $targetHost)) {
|
||||||
|
return $connector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
21
vendor/clue/http-proxy-react/LICENSE
vendored
Normal file
21
vendor/clue/http-proxy-react/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Christian Lück
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
213
vendor/clue/http-proxy-react/src/ProxyConnector.php
vendored
Normal file
213
vendor/clue/http-proxy-react/src/ProxyConnector.php
vendored
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\HttpProxy;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use RuntimeException;
|
||||||
|
use RingCentral\Psr7;
|
||||||
|
use React\Promise;
|
||||||
|
use React\Promise\Deferred;
|
||||||
|
use React\Socket\ConnectionInterface;
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
use React\Socket\FixedUriConnector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple Connector that uses an HTTP CONNECT proxy to create plain TCP/IP connections to any destination
|
||||||
|
*
|
||||||
|
* [you] -> [proxy] -> [destination]
|
||||||
|
*
|
||||||
|
* This is most frequently used to issue HTTPS requests to your destination.
|
||||||
|
* However, this is actually performed on a higher protocol layer and this
|
||||||
|
* connector is actually inherently a general-purpose plain TCP/IP connector.
|
||||||
|
*
|
||||||
|
* Note that HTTP CONNECT proxies often restrict which ports one may connect to.
|
||||||
|
* Many (public) proxy servers do in fact limit this to HTTPS (443) only.
|
||||||
|
*
|
||||||
|
* If you want to establish a TLS connection (such as HTTPS) between you and
|
||||||
|
* your destination, you may want to wrap this connector in a SecureConnector
|
||||||
|
* instance.
|
||||||
|
*
|
||||||
|
* Note that communication between the client and the proxy is usually via an
|
||||||
|
* unencrypted, plain TCP/IP HTTP connection. Note that this is the most common
|
||||||
|
* setup, because you can still establish a TLS connection between you and the
|
||||||
|
* destination host as above.
|
||||||
|
*
|
||||||
|
* If you want to connect to a (rather rare) HTTPS proxy, you may want use its
|
||||||
|
* HTTPS port (443) and use a SecureConnector instance to create a secure
|
||||||
|
* connection to the proxy.
|
||||||
|
*
|
||||||
|
* @link https://tools.ietf.org/html/rfc7231#section-4.3.6
|
||||||
|
*/
|
||||||
|
class ProxyConnector implements ConnectorInterface
|
||||||
|
{
|
||||||
|
private $connector;
|
||||||
|
private $proxyUri;
|
||||||
|
private $proxyAuth = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a new ProxyConnector which uses the given $proxyUrl
|
||||||
|
*
|
||||||
|
* @param string $proxyUrl The proxy URL may or may not contain a scheme and
|
||||||
|
* port definition. The default port will be `80` for HTTP (or `443` for
|
||||||
|
* HTTPS), but many common HTTP proxy servers use custom ports.
|
||||||
|
* @param ConnectorInterface $connector In its most simple form, the given
|
||||||
|
* connector will be a \React\Socket\Connector if you want to connect to
|
||||||
|
* a given IP address.
|
||||||
|
* @throws InvalidArgumentException if the proxy URL is invalid
|
||||||
|
*/
|
||||||
|
public function __construct($proxyUrl, ConnectorInterface $connector)
|
||||||
|
{
|
||||||
|
// support `http+unix://` scheme for Unix domain socket (UDS) paths
|
||||||
|
if (preg_match('/^http\+unix:\/\/(.*?@)?(.+?)$/', $proxyUrl, $match)) {
|
||||||
|
// rewrite URI to parse authentication from dummy host
|
||||||
|
$proxyUrl = 'http://' . $match[1] . 'localhost';
|
||||||
|
|
||||||
|
// connector uses Unix transport scheme and explicit path given
|
||||||
|
$connector = new FixedUriConnector(
|
||||||
|
'unix://' . $match[2],
|
||||||
|
$connector
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($proxyUrl, '://') === false) {
|
||||||
|
$proxyUrl = 'http://' . $proxyUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = parse_url($proxyUrl);
|
||||||
|
if (!$parts || !isset($parts['scheme'], $parts['host']) || ($parts['scheme'] !== 'http' && $parts['scheme'] !== 'https')) {
|
||||||
|
throw new InvalidArgumentException('Invalid proxy URL "' . $proxyUrl . '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply default port and TCP/TLS transport for given scheme
|
||||||
|
if (!isset($parts['port'])) {
|
||||||
|
$parts['port'] = $parts['scheme'] === 'https' ? 443 : 80;
|
||||||
|
}
|
||||||
|
$parts['scheme'] = $parts['scheme'] === 'https' ? 'tls' : 'tcp';
|
||||||
|
|
||||||
|
$this->connector = $connector;
|
||||||
|
$this->proxyUri = $parts['scheme'] . '://' . $parts['host'] . ':' . $parts['port'];
|
||||||
|
|
||||||
|
// prepare Proxy-Authorization header if URI contains username/password
|
||||||
|
if (isset($parts['user']) || isset($parts['pass'])) {
|
||||||
|
$this->proxyAuth = 'Proxy-Authorization: Basic ' . base64_encode(
|
||||||
|
rawurldecode($parts['user'] . ':' . (isset($parts['pass']) ? $parts['pass'] : ''))
|
||||||
|
) . "\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connect($uri)
|
||||||
|
{
|
||||||
|
if (strpos($uri, '://') === false) {
|
||||||
|
$uri = 'tcp://' . $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = parse_url($uri);
|
||||||
|
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
|
||||||
|
return Promise\reject(new InvalidArgumentException('Invalid target URI specified'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = trim($parts['host'], '[]');
|
||||||
|
$port = $parts['port'];
|
||||||
|
|
||||||
|
// construct URI to HTTP CONNECT proxy server to connect to
|
||||||
|
$proxyUri = $this->proxyUri;
|
||||||
|
|
||||||
|
// append path from URI if given
|
||||||
|
if (isset($parts['path'])) {
|
||||||
|
$proxyUri .= $parts['path'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse query args
|
||||||
|
$args = array();
|
||||||
|
if (isset($parts['query'])) {
|
||||||
|
parse_str($parts['query'], $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append hostname from URI to query string unless explicitly given
|
||||||
|
if (!isset($args['hostname'])) {
|
||||||
|
$args['hostname'] = $parts['host'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// append query string
|
||||||
|
$proxyUri .= '?' . http_build_query($args, '', '&');;
|
||||||
|
|
||||||
|
// append fragment from URI if given
|
||||||
|
if (isset($parts['fragment'])) {
|
||||||
|
$proxyUri .= '#' . $parts['fragment'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$auth = $this->proxyAuth;
|
||||||
|
|
||||||
|
return $this->connector->connect($proxyUri)->then(function (ConnectionInterface $stream) use ($host, $port, $auth) {
|
||||||
|
$deferred = new Deferred(function ($_, $reject) use ($stream) {
|
||||||
|
$reject(new RuntimeException('Connection canceled while waiting for response from proxy (ECONNABORTED)', defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103));
|
||||||
|
$stream->close();
|
||||||
|
});
|
||||||
|
|
||||||
|
// keep buffering data until headers are complete
|
||||||
|
$buffer = '';
|
||||||
|
$fn = function ($chunk) use (&$buffer, $deferred, $stream) {
|
||||||
|
$buffer .= $chunk;
|
||||||
|
|
||||||
|
$pos = strpos($buffer, "\r\n\r\n");
|
||||||
|
if ($pos !== false) {
|
||||||
|
// try to parse headers as response message
|
||||||
|
try {
|
||||||
|
$response = Psr7\parse_response(substr($buffer, 0, $pos));
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$deferred->reject(new RuntimeException('Invalid response received from proxy (EBADMSG)', defined('SOCKET_EBADMSG') ? SOCKET_EBADMSG: 71, $e));
|
||||||
|
$stream->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($response->getStatusCode() === 407) {
|
||||||
|
// map status code 407 (Proxy Authentication Required) to EACCES
|
||||||
|
$deferred->reject(new RuntimeException('Proxy denied connection due to invalid authentication ' . $response->getStatusCode() . ' (' . $response->getReasonPhrase() . ') (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13));
|
||||||
|
return $stream->close();
|
||||||
|
} elseif ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
|
||||||
|
// map non-2xx status code to ECONNREFUSED
|
||||||
|
$deferred->reject(new RuntimeException('Proxy refused connection with HTTP error code ' . $response->getStatusCode() . ' (' . $response->getReasonPhrase() . ') (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111));
|
||||||
|
return $stream->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// all okay, resolve with stream instance
|
||||||
|
$deferred->resolve($stream);
|
||||||
|
|
||||||
|
// emit remaining incoming as data event
|
||||||
|
$buffer = (string)substr($buffer, $pos + 4);
|
||||||
|
if ($buffer !== '') {
|
||||||
|
$stream->emit('data', array($buffer));
|
||||||
|
$buffer = '';
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop buffering when 8 KiB have been read
|
||||||
|
if (isset($buffer[8192])) {
|
||||||
|
$deferred->reject(new RuntimeException('Proxy must not send more than 8 KiB of headers (EMSGSIZE)', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90));
|
||||||
|
$stream->close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$stream->on('data', $fn);
|
||||||
|
|
||||||
|
$stream->on('error', function (Exception $e) use ($deferred) {
|
||||||
|
$deferred->reject(new RuntimeException('Stream error while waiting for response from proxy (EIO)', defined('SOCKET_EIO') ? SOCKET_EIO : 5, $e));
|
||||||
|
});
|
||||||
|
|
||||||
|
$stream->on('close', function () use ($deferred) {
|
||||||
|
$deferred->reject(new RuntimeException('Connection to proxy lost while waiting for response (ECONNRESET)', defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 104));
|
||||||
|
});
|
||||||
|
|
||||||
|
$stream->write("CONNECT " . $host . ":" . $port . " HTTP/1.1\r\nHost: " . $host . ":" . $port . "\r\n" . $auth . "\r\n");
|
||||||
|
|
||||||
|
return $deferred->promise()->then(function (ConnectionInterface $stream) use ($fn) {
|
||||||
|
// Stop buffering when connection has been established.
|
||||||
|
$stream->removeListener('data', $fn);
|
||||||
|
return new Promise\FulfilledPromise($stream);
|
||||||
|
});
|
||||||
|
}, function (Exception $e) use ($proxyUri) {
|
||||||
|
throw new RuntimeException('Unable to connect to proxy (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
21
vendor/clue/mq-react/LICENSE
vendored
Normal file
21
vendor/clue/mq-react/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 Christian Lück
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
331
vendor/clue/mq-react/src/Queue.php
vendored
Normal file
331
vendor/clue/mq-react/src/Queue.php
vendored
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Mq;
|
||||||
|
|
||||||
|
use React\Promise;
|
||||||
|
use React\Promise\CancellablePromiseInterface;
|
||||||
|
use React\Promise\Deferred;
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `Queue` is responsible for managing your operations and ensuring not too
|
||||||
|
* many operations are executed at once. It's a very simple and lightweight
|
||||||
|
* in-memory implementation of the
|
||||||
|
* [leaky bucket](https://en.wikipedia.org/wiki/Leaky_bucket#As_a_queue) algorithm.
|
||||||
|
*
|
||||||
|
* This means that you control how many operations can be executed concurrently.
|
||||||
|
* If you add a job to the queue and it still below the limit, it will be executed
|
||||||
|
* immediately. If you keep adding new jobs to the queue and its concurrency limit
|
||||||
|
* is reached, it will not start a new operation and instead queue this for future
|
||||||
|
* execution. Once one of the pending operations complete, it will pick the next
|
||||||
|
* job from the qeueue and execute this operation.
|
||||||
|
*/
|
||||||
|
class Queue implements \Countable
|
||||||
|
{
|
||||||
|
private $concurrency;
|
||||||
|
private $limit;
|
||||||
|
private $handler;
|
||||||
|
|
||||||
|
private $pending = 0;
|
||||||
|
private $queue = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concurrently process all given jobs through the given `$handler`.
|
||||||
|
*
|
||||||
|
* This is a convenience method which uses the `Queue` internally to
|
||||||
|
* schedule all jobs while limiting concurrency to ensure no more than
|
||||||
|
* `$concurrency` jobs ever run at once. It will return a promise which
|
||||||
|
* resolves with the results of all jobs on success.
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* $loop = React\EventLoop\Factory::create();
|
||||||
|
* $browser = new Clue\React\Buzz\Browser($loop);
|
||||||
|
*
|
||||||
|
* $promise = Queue:all(3, $urls, function ($url) use ($browser) {
|
||||||
|
* return $browser->get($url);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* $promise->then(function (array $responses) {
|
||||||
|
* echo 'All ' . count($responses) . ' successful!' . PHP_EOL;
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* If either of the jobs fail, it will reject the resulting promise and will
|
||||||
|
* try to cancel all outstanding jobs. Similarly, calling `cancel()` on the
|
||||||
|
* resulting promise will try to cancel all outstanding jobs. See
|
||||||
|
* [promises](#promises) and [cancellation](#cancellation) for details.
|
||||||
|
*
|
||||||
|
* The `$concurrency` parameter sets a new soft limit for the maximum number
|
||||||
|
* of jobs to handle concurrently. Finding a good concurrency limit depends
|
||||||
|
* on your particular use case. It's common to limit concurrency to a rather
|
||||||
|
* small value, as doing more than a dozen of things at once may easily
|
||||||
|
* overwhelm the receiving side. Using a `1` value will ensure that all jobs
|
||||||
|
* are processed one after another, effectively creating a "waterfall" of
|
||||||
|
* jobs. Using a value less than 1 will reject with an
|
||||||
|
* `InvalidArgumentException` without processing any jobs.
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* // handle up to 10 jobs concurrently
|
||||||
|
* $promise = Queue:all(10, $jobs, $handler);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* // handle each job after another without concurrency (waterfall)
|
||||||
|
* $promise = Queue:all(1, $jobs, $handler);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The `$jobs` parameter must be an array with all jobs to process. Each
|
||||||
|
* value in this array will be passed to the `$handler` to start one job.
|
||||||
|
* The array keys will be preserved in the resulting array, while the array
|
||||||
|
* values will be replaced with the job results as returned by the
|
||||||
|
* `$handler`. If this array is empty, this method will resolve with an
|
||||||
|
* empty array without processing any jobs.
|
||||||
|
*
|
||||||
|
* The `$handler` parameter must be a valid callable that accepts your job
|
||||||
|
* parameters, invokes the appropriate operation and returns a Promise as a
|
||||||
|
* placeholder for its future result. If the given argument is not a valid
|
||||||
|
* callable, this method will reject with an `InvalidArgumentExceptionn`
|
||||||
|
* without processing any jobs.
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* // using a Closure as handler is usually recommended
|
||||||
|
* $promise = Queue::all(10, $jobs, function ($url) use ($browser) {
|
||||||
|
* return $browser->get($url);
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* // accepts any callable, so PHP's array notation is also supported
|
||||||
|
* $promise = Queue:all(10, $jobs, array($browser, 'get'));
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* > Keep in mind that returning an array of response messages means that
|
||||||
|
* the whole response body has to be kept in memory.
|
||||||
|
*
|
||||||
|
* @param int $concurrency concurrency soft limit
|
||||||
|
* @param array $jobs
|
||||||
|
* @param callable $handler
|
||||||
|
* @return PromiseInterface Returns a Promise<mixed[]> which resolves with an array of all resolution values
|
||||||
|
* or rejects when any of the operations reject.
|
||||||
|
*/
|
||||||
|
public static function all($concurrency, array $jobs, $handler)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// limit number of concurrent operations
|
||||||
|
$q = new self($concurrency, null, $handler);
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
// reject if $concurrency or $handler is invalid
|
||||||
|
return Promise\reject($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// try invoking all operations and automatically queue excessive ones
|
||||||
|
$promises = array_map($q, $jobs);
|
||||||
|
|
||||||
|
return new Promise\Promise(function ($resolve, $reject) use ($promises) {
|
||||||
|
Promise\all($promises)->then($resolve, function ($e) use ($promises, $reject) {
|
||||||
|
// cancel all pending promises if a single promise fails
|
||||||
|
foreach (array_reverse($promises) as $promise) {
|
||||||
|
if ($promise instanceof CancellablePromiseInterface) {
|
||||||
|
$promise->cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reject with original rejection message
|
||||||
|
$reject($e);
|
||||||
|
});
|
||||||
|
}, function () use ($promises) {
|
||||||
|
// cancel all pending promises on cancellation
|
||||||
|
foreach (array_reverse($promises) as $promise) {
|
||||||
|
if ($promise instanceof CancellablePromiseInterface) {
|
||||||
|
$promise->cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new queue object.
|
||||||
|
*
|
||||||
|
* You can create any number of queues, for example when you want to apply
|
||||||
|
* different limits to different kind of operations.
|
||||||
|
*
|
||||||
|
* The `$concurrency` parameter sets a new soft limit for the maximum number
|
||||||
|
* of jobs to handle concurrently. Finding a good concurrency limit depends
|
||||||
|
* on your particular use case. It's common to limit concurrency to a rather
|
||||||
|
* small value, as doing more than a dozen of things at once may easily
|
||||||
|
* overwhelm the receiving side.
|
||||||
|
*
|
||||||
|
* The `$limit` parameter sets a new hard limit on how many jobs may be
|
||||||
|
* outstanding (kept in memory) at once. Depending on your particular use
|
||||||
|
* case, it's usually safe to keep a few hundreds or thousands of jobs in
|
||||||
|
* memory. If you do not want to apply an upper limit, you can pass a `null`
|
||||||
|
* value which is semantically more meaningful than passing a big number.
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* // handle up to 10 jobs concurrently, but keep no more than 1000 in memory
|
||||||
|
* $q = new Queue(10, 1000, $handler);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* // handle up to 10 jobs concurrently, do not limit queue size
|
||||||
|
* $q = new Queue(10, null, $handler);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* // handle up to 10 jobs concurrently, reject all further jobs
|
||||||
|
* $q = new Queue(10, 10, $handler);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The `$handler` parameter must be a valid callable that accepts your job
|
||||||
|
* parameters, invokes the appropriate operation and returns a Promise as a
|
||||||
|
* placeholder for its future result.
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* // using a Closure as handler is usually recommended
|
||||||
|
* $q = new Queue(10, null, function ($url) use ($browser) {
|
||||||
|
* return $browser->get($url);
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* // PHP's array callable as handler is also supported
|
||||||
|
* $q = new Queue(10, null, array($browser, 'get'));
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param int $concurrency concurrency soft limit
|
||||||
|
* @param int|null $limit queue hard limit or NULL=unlimited
|
||||||
|
* @param callable $handler
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function __construct($concurrency, $limit, $handler)
|
||||||
|
{
|
||||||
|
if ($concurrency < 1 || ($limit !== null && ($limit < 1 || $concurrency > $limit))) {
|
||||||
|
throw new \InvalidArgumentException('Invalid limit given');
|
||||||
|
}
|
||||||
|
if (!is_callable($handler)) {
|
||||||
|
throw new \InvalidArgumentException('Invalid handler given');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->concurrency = $concurrency;
|
||||||
|
$this->limit = $limit;
|
||||||
|
$this->handler = $handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Queue instance is invokable, so that invoking `$q(...$args)` will
|
||||||
|
* actually be forwarded as `$handler(...$args)` as given in the
|
||||||
|
* `$handler` argument when concurrency is still below limits.
|
||||||
|
*
|
||||||
|
* Each operation may take some time to complete, but due to its async nature you
|
||||||
|
* can actually start any number of (queued) operations. Once the concurrency limit
|
||||||
|
* is reached, this invocation will simply be queued and this will return a pending
|
||||||
|
* promise which will start the actual operation once another operation is
|
||||||
|
* completed. This means that this is handled entirely transparently and you do not
|
||||||
|
* need to worry about this concurrency limit yourself.
|
||||||
|
*
|
||||||
|
* @return \React\Promise\PromiseInterface
|
||||||
|
*/
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
// happy path: simply invoke handler if we're below concurrency limit
|
||||||
|
if ($this->pending < $this->concurrency) {
|
||||||
|
++$this->pending;
|
||||||
|
|
||||||
|
// invoke handler and await its resolution before invoking next queued job
|
||||||
|
return $this->await(
|
||||||
|
call_user_func_array($this->handler, func_get_args())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're currently above concurreny limit, make sure we do not exceed maximum queue limit
|
||||||
|
if ($this->limit !== null && $this->count() >= $this->limit) {
|
||||||
|
return Promise\reject(new \OverflowException('Maximum queue limit of ' . $this->limit . ' exceeded'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we reach this point, then this job will need to be queued
|
||||||
|
// get next queue position
|
||||||
|
$queue =& $this->queue;
|
||||||
|
$queue[] = null;
|
||||||
|
end($queue);
|
||||||
|
$id = key($queue);
|
||||||
|
|
||||||
|
$deferred = new Deferred(function ($_, $reject) use (&$queue, $id, &$deferred) {
|
||||||
|
// forward cancellation to pending operation if it is currently executing
|
||||||
|
if (isset($deferred->pending) && $deferred->pending instanceof CancellablePromiseInterface) {
|
||||||
|
$deferred->pending->cancel();
|
||||||
|
}
|
||||||
|
unset($deferred->pending);
|
||||||
|
|
||||||
|
if (isset($deferred->args)) {
|
||||||
|
// queued promise cancelled before its handler is invoked
|
||||||
|
// remove from queue and reject explicitly
|
||||||
|
unset($queue[$id], $deferred->args);
|
||||||
|
$reject(new \RuntimeException('Cancelled queued job before processing started'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// queue job to process if number of pending jobs is below concurrency limit again
|
||||||
|
$deferred->args = func_get_args();
|
||||||
|
$queue[$id] = $deferred;
|
||||||
|
|
||||||
|
return $deferred->promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
return $this->pending + count($this->queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function await(PromiseInterface $promise)
|
||||||
|
{
|
||||||
|
$that = $this;
|
||||||
|
|
||||||
|
return $promise->then(function ($result) use ($that) {
|
||||||
|
$that->processQueue();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}, function ($error) use ($that) {
|
||||||
|
$that->processQueue();
|
||||||
|
|
||||||
|
return Promise\reject($error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function processQueue()
|
||||||
|
{
|
||||||
|
// skip if we're still above concurrency limit or there's no queued job waiting
|
||||||
|
if (--$this->pending >= $this->concurrency || !$this->queue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @var $deferred Deferred */
|
||||||
|
$deferred = reset($this->queue);
|
||||||
|
unset($this->queue[key($this->queue)]);
|
||||||
|
|
||||||
|
// once number of pending jobs is below concurrency limit again:
|
||||||
|
// await this situation, invoke handler and await its resolution before invoking next queued job
|
||||||
|
++$this->pending;
|
||||||
|
|
||||||
|
$promise = call_user_func_array($this->handler, $deferred->args);
|
||||||
|
$deferred->pending = $promise;
|
||||||
|
unset($deferred->args);
|
||||||
|
|
||||||
|
// invoke handler and await its resolution before invoking next queued job
|
||||||
|
$this->await($promise)->then(
|
||||||
|
function ($result) use ($deferred) {
|
||||||
|
unset($deferred->pending);
|
||||||
|
$deferred->resolve($result);
|
||||||
|
},
|
||||||
|
function ($e) use ($deferred) {
|
||||||
|
unset($deferred->pending);
|
||||||
|
$deferred->reject($e);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
51
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Factory.php
vendored
Normal file
51
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Factory.php
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol;
|
||||||
|
|
||||||
|
use Clue\Redis\Protocol\Parser\ParserInterface;
|
||||||
|
use Clue\Redis\Protocol\Parser\ResponseParser;
|
||||||
|
use Clue\Redis\Protocol\Serializer\SerializerInterface;
|
||||||
|
use Clue\Redis\Protocol\Serializer\RecursiveSerializer;
|
||||||
|
use Clue\Redis\Protocol\Parser\RequestParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides factory methods used to instantiate the best available protocol implementation
|
||||||
|
*/
|
||||||
|
class Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* instantiate the best available protocol response parser implementation
|
||||||
|
*
|
||||||
|
* This is the parser every redis client implementation should use in order
|
||||||
|
* to parse incoming response messages from a redis server.
|
||||||
|
*
|
||||||
|
* @return ParserInterface
|
||||||
|
*/
|
||||||
|
public function createResponseParser()
|
||||||
|
{
|
||||||
|
return new ResponseParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* instantiate the best available protocol request parser implementation
|
||||||
|
*
|
||||||
|
* This is most useful for a redis server implementation which needs to
|
||||||
|
* process client requests.
|
||||||
|
*
|
||||||
|
* @return ParserInterface
|
||||||
|
*/
|
||||||
|
public function createRequestParser()
|
||||||
|
{
|
||||||
|
return new RequestParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* instantiate the best available protocol serializer implementation
|
||||||
|
*
|
||||||
|
* @return SerializerInterface
|
||||||
|
*/
|
||||||
|
public function createSerializer()
|
||||||
|
{
|
||||||
|
return new RecursiveSerializer();
|
||||||
|
}
|
||||||
|
}
|
34
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/BulkReply.php
vendored
Normal file
34
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/BulkReply.php
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Model;
|
||||||
|
|
||||||
|
use Clue\Redis\Protocol\Model\ModelInterface;
|
||||||
|
use Clue\Redis\Protocol\Serializer\SerializerInterface;
|
||||||
|
|
||||||
|
class BulkReply implements ModelInterface
|
||||||
|
{
|
||||||
|
private $value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create bulk reply (string reply)
|
||||||
|
*
|
||||||
|
* @param string|null $data
|
||||||
|
*/
|
||||||
|
public function __construct($value)
|
||||||
|
{
|
||||||
|
if ($value !== null) {
|
||||||
|
$value = (string)$value;
|
||||||
|
}
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValueNative()
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessageSerialized(SerializerInterface $serializer)
|
||||||
|
{
|
||||||
|
return $serializer->getBulkMessage($this->value);
|
||||||
|
}
|
||||||
|
}
|
34
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/ErrorReply.php
vendored
Normal file
34
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/ErrorReply.php
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Model;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Clue\Redis\Protocol\Serializer\SerializerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @link http://redis.io/topics/protocol#status-reply
|
||||||
|
*/
|
||||||
|
class ErrorReply extends Exception implements ModelInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* create error status reply (single line error message)
|
||||||
|
*
|
||||||
|
* @param string|ErrorReplyException $message
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __construct($message, $code = 0, $previous = null)
|
||||||
|
{
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValueNative()
|
||||||
|
{
|
||||||
|
return $this->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessageSerialized(SerializerInterface $serializer)
|
||||||
|
{
|
||||||
|
return $serializer->getErrorMessage($this->getMessage());
|
||||||
|
}
|
||||||
|
}
|
31
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/IntegerReply.php
vendored
Normal file
31
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/IntegerReply.php
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Model;
|
||||||
|
|
||||||
|
use Clue\Redis\Protocol\Model\ModelInterface;
|
||||||
|
use Clue\Redis\Protocol\Serializer\SerializerInterface;
|
||||||
|
|
||||||
|
class IntegerReply implements ModelInterface
|
||||||
|
{
|
||||||
|
private $value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create integer reply
|
||||||
|
*
|
||||||
|
* @param int $data
|
||||||
|
*/
|
||||||
|
public function __construct($value)
|
||||||
|
{
|
||||||
|
$this->value = (int)$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValueNative()
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessageSerialized(SerializerInterface $serializer)
|
||||||
|
{
|
||||||
|
return $serializer->getIntegerMessage($this->value);
|
||||||
|
}
|
||||||
|
}
|
23
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/ModelInterface.php
vendored
Normal file
23
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/ModelInterface.php
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Model;
|
||||||
|
|
||||||
|
use Clue\Redis\Protocol\Serializer\SerializerInterface;
|
||||||
|
|
||||||
|
interface ModelInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns value of this model as a native representation for PHP
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getValueNative();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the serialized representation of this protocol message
|
||||||
|
*
|
||||||
|
* @param SerializerInterface $serializer;
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMessageSerialized(SerializerInterface $serializer);
|
||||||
|
}
|
100
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/MultiBulkReply.php
vendored
Normal file
100
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/MultiBulkReply.php
vendored
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Model;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
use Clue\Redis\Protocol\Serializer\SerializerInterface;
|
||||||
|
|
||||||
|
class MultiBulkReply implements ModelInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array|null
|
||||||
|
*/
|
||||||
|
private $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create multi bulk reply (an array of other replies, usually bulk replies)
|
||||||
|
*
|
||||||
|
* @param array|null $data
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function __construct(array $data = null)
|
||||||
|
{
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValueNative()
|
||||||
|
{
|
||||||
|
if ($this->data === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = array();
|
||||||
|
foreach ($this->data as $one) {
|
||||||
|
if ($one instanceof ModelInterface) {
|
||||||
|
$ret []= $one->getValueNative();
|
||||||
|
} else {
|
||||||
|
$ret []= $one;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessageSerialized(SerializerInterface $serializer)
|
||||||
|
{
|
||||||
|
return $serializer->getMultiBulkMessage($this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this model represents a valid unified request protocol message
|
||||||
|
*
|
||||||
|
* The new unified protocol was introduced in Redis 1.2, but it became the
|
||||||
|
* standard way for talking with the Redis server in Redis 2.0. The unified
|
||||||
|
* request protocol is what Redis already uses in replies in order to send
|
||||||
|
* list of items to clients, and is called a Multi Bulk Reply.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
* @link http://redis.io/topics/protocol
|
||||||
|
*/
|
||||||
|
public function isRequest()
|
||||||
|
{
|
||||||
|
if (!$this->data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->data as $one) {
|
||||||
|
if (!($one instanceof BulkReply) && !is_string($one)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestModel()
|
||||||
|
{
|
||||||
|
if (!$this->data) {
|
||||||
|
throw new UnexpectedValueException('Null-multi-bulk message can not be represented as a request, must contain string/bulk values');
|
||||||
|
}
|
||||||
|
|
||||||
|
$command = null;
|
||||||
|
$args = array();
|
||||||
|
|
||||||
|
foreach ($this->data as $one) {
|
||||||
|
if ($one instanceof BulkReply) {
|
||||||
|
$one = $one->getValueNative();
|
||||||
|
} elseif (!is_string($one)) {
|
||||||
|
throw new UnexpectedValueException('Message can not be represented as a request, must only contain string/bulk values');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($command === null) {
|
||||||
|
$command = $one;
|
||||||
|
} else {
|
||||||
|
$args []= $one;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Request($command, $args);
|
||||||
|
}
|
||||||
|
}
|
53
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/Request.php
vendored
Normal file
53
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/Request.php
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Model;
|
||||||
|
|
||||||
|
use Clue\Redis\Protocol\Model\ModelInterface;
|
||||||
|
use Clue\Redis\Protocol\Model\BulkReply;
|
||||||
|
use Clue\Redis\Protocol\Model\MultiBulkReply;
|
||||||
|
use Clue\Redis\Protocol\Serializer\SerializerInterface;
|
||||||
|
|
||||||
|
class Request implements ModelInterface
|
||||||
|
{
|
||||||
|
private $command;
|
||||||
|
private $args;
|
||||||
|
|
||||||
|
public function __construct($command, array $args = array())
|
||||||
|
{
|
||||||
|
$this->command = $command;
|
||||||
|
$this->args = $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommand()
|
||||||
|
{
|
||||||
|
return $this->command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArgs()
|
||||||
|
{
|
||||||
|
return $this->args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReplyModel()
|
||||||
|
{
|
||||||
|
$models = array(new BulkReply($this->command));
|
||||||
|
foreach ($this->args as $arg) {
|
||||||
|
$models []= new BulkReply($arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MultiBulkReply($models);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValueNative()
|
||||||
|
{
|
||||||
|
$ret = $this->args;
|
||||||
|
array_unshift($ret, $this->command);
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessageSerialized(SerializerInterface $serializer)
|
||||||
|
{
|
||||||
|
return $serializer->getRequestMessage($this->command, $this->args);
|
||||||
|
}
|
||||||
|
}
|
34
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/StatusReply.php
vendored
Normal file
34
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Model/StatusReply.php
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Model;
|
||||||
|
|
||||||
|
use Clue\Redis\Protocol\Serializer\SerializerInterface;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @link http://redis.io/topics/protocol#status-reply
|
||||||
|
*/
|
||||||
|
class StatusReply implements ModelInterface
|
||||||
|
{
|
||||||
|
private $message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create status reply (single line message)
|
||||||
|
*
|
||||||
|
* @param string|Status $message
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __construct($message)
|
||||||
|
{
|
||||||
|
$this->message = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValueNative()
|
||||||
|
{
|
||||||
|
return $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessageSerialized(SerializerInterface $serializer)
|
||||||
|
{
|
||||||
|
return $serializer->getStatusMessage($this->message);
|
||||||
|
}
|
||||||
|
}
|
40
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Parser/MessageBuffer.php
vendored
Normal file
40
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Parser/MessageBuffer.php
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Parser;
|
||||||
|
|
||||||
|
use UnderflowException;
|
||||||
|
|
||||||
|
class MessageBuffer implements ParserInterface
|
||||||
|
{
|
||||||
|
private $parser;
|
||||||
|
private $incomingQueue = array();
|
||||||
|
|
||||||
|
public function __construct(ParserInterface $parser)
|
||||||
|
{
|
||||||
|
$this->parser = $parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function popIncomingModel()
|
||||||
|
{
|
||||||
|
if (!$this->incomingQueue) {
|
||||||
|
throw new UnderflowException('Incoming message queue is empty');
|
||||||
|
}
|
||||||
|
return array_shift($this->incomingQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasIncomingModel()
|
||||||
|
{
|
||||||
|
return ($this->incomingQueue) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pushIncoming($data)
|
||||||
|
{
|
||||||
|
$ret = $this->parser->pushIncoming($data);
|
||||||
|
|
||||||
|
foreach ($ret as $one) {
|
||||||
|
$this->incomingQueue []= $one;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
}
|
10
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Parser/ParserException.php
vendored
Normal file
10
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Parser/ParserException.php
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Parser;
|
||||||
|
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
class ParserException extends UnexpectedValueException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
28
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Parser/ParserInterface.php
vendored
Normal file
28
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Parser/ParserInterface.php
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Parser;
|
||||||
|
|
||||||
|
use Clue\Redis\Protocol\Model\ModelInterface;
|
||||||
|
use Clue\Redis\Protocol\Parser\ParserException;
|
||||||
|
|
||||||
|
interface ParserInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* push a chunk of the redis protocol message into the buffer and parse
|
||||||
|
*
|
||||||
|
* You can push any number of bytes of a redis protocol message into the
|
||||||
|
* parser and it will try to parse messages from its data stream. So you can
|
||||||
|
* pass data directly from your socket stream and the parser will return the
|
||||||
|
* right amount of message model objects for you.
|
||||||
|
*
|
||||||
|
* If you pass an incomplete message, expect it to return an empty array. If
|
||||||
|
* your incomplete message is split to across multiple chunks, the parsed
|
||||||
|
* message model will be returned once the parser has sufficient data.
|
||||||
|
*
|
||||||
|
* @param string $dataChunk
|
||||||
|
* @return ModelInterface[] 0+ message models
|
||||||
|
* @throws ParserException if the message can not be parsed
|
||||||
|
* @see self::popIncomingModel()
|
||||||
|
*/
|
||||||
|
public function pushIncoming($dataChunk);
|
||||||
|
}
|
125
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Parser/RequestParser.php
vendored
Normal file
125
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Parser/RequestParser.php
vendored
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Parser;
|
||||||
|
|
||||||
|
use Clue\Redis\Protocol\Parser\ParserException;
|
||||||
|
use Clue\Redis\Protocol\Model\Request;
|
||||||
|
|
||||||
|
class RequestParser implements ParserInterface
|
||||||
|
{
|
||||||
|
const CRLF = "\r\n";
|
||||||
|
|
||||||
|
private $incomingBuffer = '';
|
||||||
|
private $incomingOffset = 0;
|
||||||
|
|
||||||
|
public function pushIncoming($dataChunk)
|
||||||
|
{
|
||||||
|
$this->incomingBuffer .= $dataChunk;
|
||||||
|
|
||||||
|
$parsed = array();
|
||||||
|
|
||||||
|
do {
|
||||||
|
$saved = $this->incomingOffset;
|
||||||
|
$message = $this->readRequest();
|
||||||
|
if ($message === null) {
|
||||||
|
// restore previous position for next parsing attempt
|
||||||
|
$this->incomingOffset = $saved;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($message !== false) {
|
||||||
|
$parsed []= $message;
|
||||||
|
}
|
||||||
|
} while($this->incomingBuffer !== '');
|
||||||
|
|
||||||
|
if ($this->incomingOffset !== 0) {
|
||||||
|
$this->incomingBuffer = (string)substr($this->incomingBuffer, $this->incomingOffset);
|
||||||
|
$this->incomingOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* try to parse request from incoming buffer
|
||||||
|
*
|
||||||
|
* @throws ParserException if the incoming buffer is invalid
|
||||||
|
* @return Request|null
|
||||||
|
*/
|
||||||
|
private function readRequest()
|
||||||
|
{
|
||||||
|
$crlf = strpos($this->incomingBuffer, "\r\n", $this->incomingOffset);
|
||||||
|
if ($crlf === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// line starts with a multi-bulk header "*"
|
||||||
|
if (isset($this->incomingBuffer[$this->incomingOffset]) && $this->incomingBuffer[$this->incomingOffset] === '*') {
|
||||||
|
$line = substr($this->incomingBuffer, $this->incomingOffset + 1, $crlf - $this->incomingOffset + 1);
|
||||||
|
$this->incomingOffset = $crlf + 2;
|
||||||
|
$count = (int)$line;
|
||||||
|
|
||||||
|
if ($count <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$command = null;
|
||||||
|
$args = array();
|
||||||
|
for ($i = 0; $i < $count; ++$i) {
|
||||||
|
$sub = $this->readBulk();
|
||||||
|
if ($sub === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ($command === null) {
|
||||||
|
$command = $sub;
|
||||||
|
} else {
|
||||||
|
$args []= $sub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Request($command, $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse an old inline request instead
|
||||||
|
$line = substr($this->incomingBuffer, $this->incomingOffset, $crlf - $this->incomingOffset);
|
||||||
|
$this->incomingOffset = $crlf + 2;
|
||||||
|
|
||||||
|
$args = preg_split('/ +/', trim($line, ' '));
|
||||||
|
$command = array_shift($args);
|
||||||
|
|
||||||
|
if ($command === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Request($command, $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function readBulk()
|
||||||
|
{
|
||||||
|
$crlf = strpos($this->incomingBuffer, "\r\n", $this->incomingOffset);
|
||||||
|
if ($crlf === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// line has to start with a bulk header "$"
|
||||||
|
if (!isset($this->incomingBuffer[$this->incomingOffset]) || $this->incomingBuffer[$this->incomingOffset] !== '$') {
|
||||||
|
throw new ParserException('ERR Protocol error: expected \'$\', got \'' . substr($this->incomingBuffer, $this->incomingOffset, 1) . '\'');
|
||||||
|
}
|
||||||
|
|
||||||
|
$line = substr($this->incomingBuffer, $this->incomingOffset + 1, $crlf - $this->incomingOffset + 1);
|
||||||
|
$this->incomingOffset = $crlf + 2;
|
||||||
|
$size = (int)$line;
|
||||||
|
|
||||||
|
if ($size < 0) {
|
||||||
|
throw new ParserException('ERR Protocol error: invalid bulk length');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($this->incomingBuffer[$this->incomingOffset + $size + 1])) {
|
||||||
|
// check enough bytes + crlf are buffered
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = substr($this->incomingBuffer, $this->incomingOffset, $size);
|
||||||
|
$this->incomingOffset += $size + 2;
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
}
|
151
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Parser/ResponseParser.php
vendored
Normal file
151
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Parser/ResponseParser.php
vendored
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Parser;
|
||||||
|
|
||||||
|
use Clue\Redis\Protocol\Parser\ParserInterface;
|
||||||
|
use Clue\Redis\Protocol\Model\ModelInterface;
|
||||||
|
use Clue\Redis\Protocol\Model\BulkReply;
|
||||||
|
use Clue\Redis\Protocol\Model\ErrorReply;
|
||||||
|
use Clue\Redis\Protocol\Model\IntegerReply;
|
||||||
|
use Clue\Redis\Protocol\Model\MultiBulkReply;
|
||||||
|
use Clue\Redis\Protocol\Model\StatusReply;
|
||||||
|
use Clue\Redis\Protocol\Parser\ParserException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple recursive redis wire protocol parser
|
||||||
|
*
|
||||||
|
* Heavily influenced by blocking parser implementation from jpd/redisent.
|
||||||
|
*
|
||||||
|
* @link https://github.com/jdp/redisent
|
||||||
|
* @link http://redis.io/topics/protocol
|
||||||
|
*/
|
||||||
|
class ResponseParser implements ParserInterface
|
||||||
|
{
|
||||||
|
const CRLF = "\r\n";
|
||||||
|
|
||||||
|
private $incomingBuffer = '';
|
||||||
|
private $incomingOffset = 0;
|
||||||
|
|
||||||
|
public function pushIncoming($dataChunk)
|
||||||
|
{
|
||||||
|
$this->incomingBuffer .= $dataChunk;
|
||||||
|
|
||||||
|
return $this->tryParsingIncomingMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tryParsingIncomingMessages()
|
||||||
|
{
|
||||||
|
$messages = array();
|
||||||
|
|
||||||
|
do {
|
||||||
|
$message = $this->readResponse();
|
||||||
|
if ($message === null) {
|
||||||
|
// restore previous position for next parsing attempt
|
||||||
|
$this->incomingOffset = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$messages []= $message;
|
||||||
|
|
||||||
|
$this->incomingBuffer = (string)substr($this->incomingBuffer, $this->incomingOffset);
|
||||||
|
$this->incomingOffset = 0;
|
||||||
|
} while($this->incomingBuffer !== '');
|
||||||
|
|
||||||
|
return $messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function readLine()
|
||||||
|
{
|
||||||
|
$pos = strpos($this->incomingBuffer, "\r\n", $this->incomingOffset);
|
||||||
|
|
||||||
|
if ($pos === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = (string)substr($this->incomingBuffer, $this->incomingOffset, $pos - $this->incomingOffset);
|
||||||
|
$this->incomingOffset = $pos + 2;
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function readLength($len)
|
||||||
|
{
|
||||||
|
$ret = substr($this->incomingBuffer, $this->incomingOffset, $len);
|
||||||
|
if (strlen($ret) !== $len) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->incomingOffset += $len;
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* try to parse response from incoming buffer
|
||||||
|
*
|
||||||
|
* ripped from jdp/redisent, with some minor modifications to read from
|
||||||
|
* the incoming buffer instead of issuing a blocking fread on a stream
|
||||||
|
*
|
||||||
|
* @throws ParserException if the incoming buffer is invalid
|
||||||
|
* @return ModelInterface|null
|
||||||
|
* @link https://github.com/jdp/redisent
|
||||||
|
*/
|
||||||
|
private function readResponse()
|
||||||
|
{
|
||||||
|
/* Parse the response based on the reply identifier */
|
||||||
|
$reply = $this->readLine();
|
||||||
|
if ($reply === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (substr($reply, 0, 1)) {
|
||||||
|
/* Error reply */
|
||||||
|
case '-':
|
||||||
|
$response = new ErrorReply(substr($reply, 1));
|
||||||
|
break;
|
||||||
|
/* Inline reply */
|
||||||
|
case '+':
|
||||||
|
$response = new StatusReply(substr($reply, 1));
|
||||||
|
break;
|
||||||
|
/* Bulk reply */
|
||||||
|
case '$':
|
||||||
|
$size = (int)substr($reply, 1);
|
||||||
|
if ($size === -1) {
|
||||||
|
return new BulkReply(null);
|
||||||
|
}
|
||||||
|
$data = $this->readLength($size);
|
||||||
|
if ($data === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ($this->readLength(2) === null) { /* discard crlf */
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$response = new BulkReply($data);
|
||||||
|
break;
|
||||||
|
/* Multi-bulk reply */
|
||||||
|
case '*':
|
||||||
|
$count = (int)substr($reply, 1);
|
||||||
|
if ($count === -1) {
|
||||||
|
return new MultiBulkReply(null);
|
||||||
|
}
|
||||||
|
$response = array();
|
||||||
|
for ($i = 0; $i < $count; $i++) {
|
||||||
|
$sub = $this->readResponse();
|
||||||
|
if ($sub === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$response []= $sub;
|
||||||
|
}
|
||||||
|
$response = new MultiBulkReply($response);
|
||||||
|
break;
|
||||||
|
/* Integer reply */
|
||||||
|
case ':':
|
||||||
|
$response = new IntegerReply(substr($reply, 1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ParserException('Invalid message can not be parsed: "' . $reply . '"');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Party on */
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
111
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Serializer/RecursiveSerializer.php
vendored
Normal file
111
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Serializer/RecursiveSerializer.php
vendored
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Serializer;
|
||||||
|
|
||||||
|
use Clue\Redis\Protocol\Model\StatusReply;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Exception;
|
||||||
|
use Clue\Redis\Protocol\Model\BulkReply;
|
||||||
|
use Clue\Redis\Protocol\Model\IntegerReply;
|
||||||
|
use Clue\Redis\Protocol\Model\ErrorReply;
|
||||||
|
use Clue\Redis\Protocol\Model\MultiBulkReply;
|
||||||
|
use Clue\Redis\Protocol\Model\ModelInterface;
|
||||||
|
use Clue\Redis\Protocol\Model\Request;
|
||||||
|
|
||||||
|
class RecursiveSerializer implements SerializerInterface
|
||||||
|
{
|
||||||
|
const CRLF = "\r\n";
|
||||||
|
|
||||||
|
public function getRequestMessage($command, array $args = array())
|
||||||
|
{
|
||||||
|
$data = '*' . (count($args) + 1) . "\r\n$" . strlen($command) . "\r\n" . $command . "\r\n";
|
||||||
|
foreach ($args as $arg) {
|
||||||
|
$data .= '$' . strlen($arg) . "\r\n" . $arg . "\r\n";
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createRequestModel($command, array $args = array())
|
||||||
|
{
|
||||||
|
return new Request($command, $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReplyMessage($data)
|
||||||
|
{
|
||||||
|
if (is_string($data) || $data === null) {
|
||||||
|
return $this->getBulkMessage($data);
|
||||||
|
} else if (is_int($data) || is_float($data) || is_bool($data)) {
|
||||||
|
return $this->getIntegerMessage($data);
|
||||||
|
} else if ($data instanceof Exception) {
|
||||||
|
return $this->getErrorMessage($data->getMessage());
|
||||||
|
} else if (is_array($data)) {
|
||||||
|
return $this->getMultiBulkMessage($data);
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException('Invalid data type passed for serialization');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createReplyModel($data)
|
||||||
|
{
|
||||||
|
if (is_string($data) || $data === null) {
|
||||||
|
return new BulkReply($data);
|
||||||
|
} else if (is_int($data) || is_float($data) || is_bool($data)) {
|
||||||
|
return new IntegerReply($data);
|
||||||
|
} else if ($data instanceof Exception) {
|
||||||
|
return new ErrorReply($data->getMessage());
|
||||||
|
} else if (is_array($data)) {
|
||||||
|
$models = array();
|
||||||
|
foreach ($data as $one) {
|
||||||
|
$models []= $this->createReplyModel($one);
|
||||||
|
}
|
||||||
|
return new MultiBulkReply($models);
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException('Invalid data type passed for serialization');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBulkMessage($data)
|
||||||
|
{
|
||||||
|
if ($data === null) {
|
||||||
|
/* null bulk reply */
|
||||||
|
return '$-1' . self::CRLF;
|
||||||
|
}
|
||||||
|
/* bulk reply */
|
||||||
|
return '$' . strlen($data) . self::CRLF . $data . self::CRLF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getErrorMessage($data)
|
||||||
|
{
|
||||||
|
/* error status reply */
|
||||||
|
return '-' . $data . self::CRLF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIntegerMessage($data)
|
||||||
|
{
|
||||||
|
return ':' . (int)$data . self::CRLF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMultiBulkMessage($data)
|
||||||
|
{
|
||||||
|
if ($data === null) {
|
||||||
|
/* null multi bulk reply */
|
||||||
|
return '*-1' . self::CRLF;
|
||||||
|
}
|
||||||
|
/* multi bulk reply */
|
||||||
|
$ret = '*' . count($data) . self::CRLF;
|
||||||
|
foreach ($data as $one) {
|
||||||
|
if ($one instanceof ModelInterface) {
|
||||||
|
$ret .= $one->getMessageSerialized($this);
|
||||||
|
} else {
|
||||||
|
$ret .= $this->getReplyMessage($one);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatusMessage($data)
|
||||||
|
{
|
||||||
|
/* status reply */
|
||||||
|
return '+' . $data . self::CRLF;
|
||||||
|
}
|
||||||
|
}
|
83
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Serializer/SerializerInterface.php
vendored
Normal file
83
vendor/clue/redis-protocol/src/Clue/Redis/Protocol/Serializer/SerializerInterface.php
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\Redis\Protocol\Serializer;
|
||||||
|
|
||||||
|
use Clue\Redis\Protocol\Model\ErrorReplyException;
|
||||||
|
use Clue\Redis\Protocol\Model\ModelInterface;
|
||||||
|
use Clue\Redis\Protocol\Model\MultiBulkReply;
|
||||||
|
|
||||||
|
interface SerializerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* create a serialized unified request protocol message
|
||||||
|
*
|
||||||
|
* This is the *one* method most redis client libraries will likely want to
|
||||||
|
* use in order to send a serialized message (a request) over the* wire to
|
||||||
|
* your redis server instance.
|
||||||
|
*
|
||||||
|
* This method should be used in favor of constructing a request model and
|
||||||
|
* then serializing it. While its effect might be equivalent, this method
|
||||||
|
* is likely to (i.e. it /could/) provide a faster implementation.
|
||||||
|
*
|
||||||
|
* @param string $command
|
||||||
|
* @param array $args
|
||||||
|
* @return string
|
||||||
|
* @see self::createRequestMessage()
|
||||||
|
*/
|
||||||
|
public function getRequestMessage($command, array $args = array());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a unified request protocol message model
|
||||||
|
*
|
||||||
|
* @param string $command
|
||||||
|
* @param array $args
|
||||||
|
* @return MultiBulkReply
|
||||||
|
*/
|
||||||
|
public function createRequestModel($command, array $args = array());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a serialized unified protocol reply message
|
||||||
|
*
|
||||||
|
* This is most useful for a redis server implementation which needs to
|
||||||
|
* process client requests and send resulting reply messages.
|
||||||
|
*
|
||||||
|
* This method does its best to guess to right reply type and then returns
|
||||||
|
* a serialized version of the message. It follows the "redis to lua
|
||||||
|
* conversion table" (see link) which means most basic types can be mapped
|
||||||
|
* as is.
|
||||||
|
*
|
||||||
|
* This method should be used in favor of constructing a reply model and
|
||||||
|
* then serializing it. While its effect might be equivalent, this method
|
||||||
|
* is likely to (i.e. it /could/) provide a faster implementation.
|
||||||
|
*
|
||||||
|
* Note however, you may still want to explicitly create a nested reply
|
||||||
|
* model hierarchy if you need more control over the serialized message. For
|
||||||
|
* instance, a null value will always be returned as a Null-Bulk-Reply, so
|
||||||
|
* there's no way to express a Null-Multi-Bulk-Reply, unless you construct
|
||||||
|
* it explicitly.
|
||||||
|
*
|
||||||
|
* @param mixed $data
|
||||||
|
* @return string
|
||||||
|
* @see self::createReplyModel()
|
||||||
|
* @link http://redis.io/commands/eval
|
||||||
|
*/
|
||||||
|
public function getReplyMessage($data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create response message by determining datatype from given argument
|
||||||
|
*
|
||||||
|
* @param mixed $data
|
||||||
|
* @return ModelInterface
|
||||||
|
*/
|
||||||
|
public function createReplyModel($data);
|
||||||
|
|
||||||
|
public function getBulkMessage($data);
|
||||||
|
|
||||||
|
public function getErrorMessage($data);
|
||||||
|
|
||||||
|
public function getIntegerMessage($data);
|
||||||
|
|
||||||
|
public function getMultiBulkMessage($data);
|
||||||
|
|
||||||
|
public function getStatusMessage($data);
|
||||||
|
}
|
21
vendor/clue/redis-react/LICENSE
vendored
Normal file
21
vendor/clue/redis-react/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Christian Lück
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
52
vendor/clue/redis-react/src/Client.php
vendored
Normal file
52
vendor/clue/redis-react/src/Client.php
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Redis;
|
||||||
|
|
||||||
|
use Evenement\EventEmitterInterface;
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple interface for executing redis commands
|
||||||
|
*
|
||||||
|
* @event error(Exception $error)
|
||||||
|
* @event close()
|
||||||
|
*
|
||||||
|
* @event message($channel, $message)
|
||||||
|
* @event subscribe($channel, $numberOfChannels)
|
||||||
|
* @event unsubscribe($channel, $numberOfChannels)
|
||||||
|
*
|
||||||
|
* @event pmessage($pattern, $channel, $message)
|
||||||
|
* @event psubscribe($channel, $numberOfChannels)
|
||||||
|
* @event punsubscribe($channel, $numberOfChannels)
|
||||||
|
*/
|
||||||
|
interface Client extends EventEmitterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Invoke the given command and return a Promise that will be resolved when the request has been replied to
|
||||||
|
*
|
||||||
|
* This is a magic method that will be invoked when calling any redis
|
||||||
|
* command on this instance.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string[] $args
|
||||||
|
* @return PromiseInterface Promise<mixed, Exception>
|
||||||
|
*/
|
||||||
|
public function __call($name, $args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* end connection once all pending requests have been replied to
|
||||||
|
*
|
||||||
|
* @uses self::close() once all replies have been received
|
||||||
|
* @see self::close() for closing the connection immediately
|
||||||
|
*/
|
||||||
|
public function end();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* close connection immediately
|
||||||
|
*
|
||||||
|
* This will emit the "close" event.
|
||||||
|
*
|
||||||
|
* @see self::end() for closing the connection once the client is idle
|
||||||
|
*/
|
||||||
|
public function close();
|
||||||
|
}
|
144
vendor/clue/redis-react/src/Factory.php
vendored
Normal file
144
vendor/clue/redis-react/src/Factory.php
vendored
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Redis;
|
||||||
|
|
||||||
|
use Clue\React\Redis\StreamingClient;
|
||||||
|
use Clue\Redis\Protocol\Factory as ProtocolFactory;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\Promise;
|
||||||
|
use React\Socket\ConnectionInterface;
|
||||||
|
use React\Socket\Connector;
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
class Factory
|
||||||
|
{
|
||||||
|
private $connector;
|
||||||
|
private $protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LoopInterface $loop
|
||||||
|
* @param ConnectorInterface|null $connector [optional] Connector to use.
|
||||||
|
* Should be `null` in order to use default Connector.
|
||||||
|
* @param ProtocolFactory|null $protocol
|
||||||
|
*/
|
||||||
|
public function __construct(LoopInterface $loop, ConnectorInterface $connector = null, ProtocolFactory $protocol = null)
|
||||||
|
{
|
||||||
|
if ($connector === null) {
|
||||||
|
$connector = new Connector($loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($protocol === null) {
|
||||||
|
$protocol = new ProtocolFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->connector = $connector;
|
||||||
|
$this->protocol = $protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create redis client connected to address of given redis instance
|
||||||
|
*
|
||||||
|
* @param string $target Redis server URI to connect to
|
||||||
|
* @return \React\Promise\PromiseInterface resolves with Client or rejects with \Exception
|
||||||
|
*/
|
||||||
|
public function createClient($target)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$parts = $this->parseUrl($target);
|
||||||
|
} catch (InvalidArgumentException $e) {
|
||||||
|
return Promise\reject($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol = $this->protocol;
|
||||||
|
|
||||||
|
$promise = $this->connector->connect($parts['authority'])->then(function (ConnectionInterface $stream) use ($protocol) {
|
||||||
|
return new StreamingClient($stream, $protocol->createResponseParser(), $protocol->createSerializer());
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isset($parts['auth'])) {
|
||||||
|
$promise = $promise->then(function (StreamingClient $client) use ($parts) {
|
||||||
|
return $client->auth($parts['auth'])->then(
|
||||||
|
function () use ($client) {
|
||||||
|
return $client;
|
||||||
|
},
|
||||||
|
function ($error) use ($client) {
|
||||||
|
$client->close();
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($parts['db'])) {
|
||||||
|
$promise = $promise->then(function (StreamingClient $client) use ($parts) {
|
||||||
|
return $client->select($parts['db'])->then(
|
||||||
|
function () use ($client) {
|
||||||
|
return $client;
|
||||||
|
},
|
||||||
|
function ($error) use ($client) {
|
||||||
|
$client->close();
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $target
|
||||||
|
* @return array with keys authority, auth and db
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
private function parseUrl($target)
|
||||||
|
{
|
||||||
|
$ret = array();
|
||||||
|
// support `redis+unix://` scheme for Unix domain socket (UDS) paths
|
||||||
|
if (preg_match('/^redis\+unix:\/\/([^:]*:[^@]*@)?(.+?)(\?.*)?$/', $target, $match)) {
|
||||||
|
$ret['authority'] = 'unix://' . $match[2];
|
||||||
|
$target = 'redis://' . (isset($match[1]) ? $match[1] : '') . 'localhost' . (isset($match[3]) ? $match[3] : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($target, '://') === false) {
|
||||||
|
$target = 'redis://' . $target;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = parse_url($target);
|
||||||
|
if ($parts === false || !isset($parts['scheme'], $parts['host']) || !in_array($parts['scheme'], array('redis', 'rediss'))) {
|
||||||
|
throw new InvalidArgumentException('Given URL can not be parsed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($parts['pass'])) {
|
||||||
|
$ret['auth'] = rawurldecode($parts['pass']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($parts['path']) && $parts['path'] !== '') {
|
||||||
|
// skip first slash
|
||||||
|
$ret['db'] = substr($parts['path'], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($ret['authority'])) {
|
||||||
|
$ret['authority'] =
|
||||||
|
($parts['scheme'] === 'rediss' ? 'tls://' : '') .
|
||||||
|
$parts['host'] . ':' .
|
||||||
|
(isset($parts['port']) ? $parts['port'] : 6379);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($parts['query'])) {
|
||||||
|
$args = array();
|
||||||
|
parse_str($parts['query'], $args);
|
||||||
|
|
||||||
|
if (isset($args['password'])) {
|
||||||
|
$ret['auth'] = $args['password'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($args['db'])) {
|
||||||
|
$ret['db'] = $args['db'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
}
|
180
vendor/clue/redis-react/src/StreamingClient.php
vendored
Normal file
180
vendor/clue/redis-react/src/StreamingClient.php
vendored
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Redis;
|
||||||
|
|
||||||
|
use Evenement\EventEmitter;
|
||||||
|
use Clue\Redis\Protocol\Parser\ParserInterface;
|
||||||
|
use Clue\Redis\Protocol\Parser\ParserException;
|
||||||
|
use Clue\Redis\Protocol\Serializer\SerializerInterface;
|
||||||
|
use Clue\Redis\Protocol\Factory as ProtocolFactory;
|
||||||
|
use UnderflowException;
|
||||||
|
use RuntimeException;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use React\Promise\Deferred;
|
||||||
|
use Clue\Redis\Protocol\Model\ErrorReply;
|
||||||
|
use Clue\Redis\Protocol\Model\ModelInterface;
|
||||||
|
use Clue\Redis\Protocol\Model\MultiBulkReply;
|
||||||
|
use React\Stream\DuplexStreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class StreamingClient extends EventEmitter implements Client
|
||||||
|
{
|
||||||
|
private $stream;
|
||||||
|
private $parser;
|
||||||
|
private $serializer;
|
||||||
|
private $requests = array();
|
||||||
|
private $ending = false;
|
||||||
|
private $closed = false;
|
||||||
|
|
||||||
|
private $subscribed = 0;
|
||||||
|
private $psubscribed = 0;
|
||||||
|
|
||||||
|
public function __construct(DuplexStreamInterface $stream, ParserInterface $parser = null, SerializerInterface $serializer = null)
|
||||||
|
{
|
||||||
|
if ($parser === null || $serializer === null) {
|
||||||
|
$factory = new ProtocolFactory();
|
||||||
|
if ($parser === null) {
|
||||||
|
$parser = $factory->createResponseParser();
|
||||||
|
}
|
||||||
|
if ($serializer === null) {
|
||||||
|
$serializer = $factory->createSerializer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$that = $this;
|
||||||
|
$stream->on('data', function($chunk) use ($parser, $that) {
|
||||||
|
try {
|
||||||
|
$models = $parser->pushIncoming($chunk);
|
||||||
|
}
|
||||||
|
catch (ParserException $error) {
|
||||||
|
$that->emit('error', array($error));
|
||||||
|
$that->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($models as $data) {
|
||||||
|
try {
|
||||||
|
$that->handleMessage($data);
|
||||||
|
}
|
||||||
|
catch (UnderflowException $error) {
|
||||||
|
$that->emit('error', array($error));
|
||||||
|
$that->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$stream->on('close', array($this, 'close'));
|
||||||
|
|
||||||
|
$this->stream = $stream;
|
||||||
|
$this->parser = $parser;
|
||||||
|
$this->serializer = $serializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($name, $args)
|
||||||
|
{
|
||||||
|
$request = new Deferred();
|
||||||
|
$promise = $request->promise();
|
||||||
|
|
||||||
|
$name = strtolower($name);
|
||||||
|
|
||||||
|
// special (p)(un)subscribe commands only accept a single parameter and have custom response logic applied
|
||||||
|
static $pubsubs = array('subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe');
|
||||||
|
|
||||||
|
if ($this->ending) {
|
||||||
|
$request->reject(new RuntimeException('Connection closed'));
|
||||||
|
} elseif (count($args) !== 1 && in_array($name, $pubsubs)) {
|
||||||
|
$request->reject(new InvalidArgumentException('PubSub commands limited to single argument'));
|
||||||
|
} elseif ($name === 'monitor') {
|
||||||
|
$request->reject(new \BadMethodCallException('MONITOR command explicitly not supported'));
|
||||||
|
} else {
|
||||||
|
$this->stream->write($this->serializer->getRequestMessage($name, $args));
|
||||||
|
$this->requests []= $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($name, $pubsubs)) {
|
||||||
|
$that = $this;
|
||||||
|
$subscribed =& $this->subscribed;
|
||||||
|
$psubscribed =& $this->psubscribed;
|
||||||
|
|
||||||
|
$promise->then(function ($array) use ($that, &$subscribed, &$psubscribed) {
|
||||||
|
$first = array_shift($array);
|
||||||
|
|
||||||
|
// (p)(un)subscribe messages are to be forwarded
|
||||||
|
$that->emit($first, $array);
|
||||||
|
|
||||||
|
// remember number of (p)subscribe topics
|
||||||
|
if ($first === 'subscribe' || $first === 'unsubscribe') {
|
||||||
|
$subscribed = $array[1];
|
||||||
|
} else {
|
||||||
|
$psubscribed = $array[1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleMessage(ModelInterface $message)
|
||||||
|
{
|
||||||
|
if (($this->subscribed !== 0 || $this->psubscribed !== 0) && $message instanceof MultiBulkReply) {
|
||||||
|
$array = $message->getValueNative();
|
||||||
|
$first = array_shift($array);
|
||||||
|
|
||||||
|
// pub/sub messages are to be forwarded and should not be processed as request responses
|
||||||
|
if (in_array($first, array('message', 'pmessage'))) {
|
||||||
|
$this->emit($first, $array);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->requests) {
|
||||||
|
throw new UnderflowException('Unexpected reply received, no matching request found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$request = array_shift($this->requests);
|
||||||
|
/* @var $request Deferred */
|
||||||
|
|
||||||
|
if ($message instanceof ErrorReply) {
|
||||||
|
$request->reject($message);
|
||||||
|
} else {
|
||||||
|
$request->resolve($message->getValueNative());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->ending && !$this->requests) {
|
||||||
|
$this->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function end()
|
||||||
|
{
|
||||||
|
$this->ending = true;
|
||||||
|
|
||||||
|
if (!$this->requests) {
|
||||||
|
$this->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
if ($this->closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->ending = true;
|
||||||
|
$this->closed = true;
|
||||||
|
|
||||||
|
$this->stream->close();
|
||||||
|
|
||||||
|
$this->emit('close');
|
||||||
|
|
||||||
|
// reject all remaining requests in the queue
|
||||||
|
while($this->requests) {
|
||||||
|
$request = array_shift($this->requests);
|
||||||
|
/* @var $request Request */
|
||||||
|
$request->reject(new RuntimeException('Connection closing'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
vendor/clue/soap-react/LICENSE
vendored
Normal file
21
vendor/clue/soap-react/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Christian Lück
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
105
vendor/clue/soap-react/src/Client.php
vendored
Normal file
105
vendor/clue/soap-react/src/Client.php
vendored
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Soap;
|
||||||
|
|
||||||
|
use Clue\React\Buzz\Browser;
|
||||||
|
use Exception;
|
||||||
|
use Clue\React\Soap\Protocol\ClientEncoder;
|
||||||
|
use Clue\React\Soap\Protocol\ClientDecoder;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use React\Promise\Deferred;
|
||||||
|
|
||||||
|
class Client
|
||||||
|
{
|
||||||
|
private $wsdl;
|
||||||
|
private $browser;
|
||||||
|
private $encoder;
|
||||||
|
private $decoder;
|
||||||
|
|
||||||
|
public function __construct($wsdl, Browser $browser, ClientEncoder $encoder = null, ClientDecoder $decoder = null)
|
||||||
|
{
|
||||||
|
if ($encoder === null) {
|
||||||
|
$encoder = new ClientEncoder($wsdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($decoder === null) {
|
||||||
|
$decoder = new ClientDecoder($wsdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->wsdl = $wsdl;
|
||||||
|
$this->browser = $browser;
|
||||||
|
$this->encoder = $encoder;
|
||||||
|
$this->decoder = $decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function soapCall($name, $args)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$request = $this->encoder->encode($name, $args);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$deferred = new Deferred();
|
||||||
|
$deferred->reject($e);
|
||||||
|
return $deferred->promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->browser->send($request)->then(
|
||||||
|
array($this, 'handleResponse'),
|
||||||
|
array($this, 'handleError')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleResponse(ResponseInterface $response)
|
||||||
|
{
|
||||||
|
return $this->decoder->decode((string)$response->getBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleError(Exception $error)
|
||||||
|
{
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFunctions()
|
||||||
|
{
|
||||||
|
return $this->encoder->__getFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTypes()
|
||||||
|
{
|
||||||
|
return $this->encoder->__getTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get location (URI) for given function name or number
|
||||||
|
*
|
||||||
|
* Note that this is not to be confused with the WSDL file location.
|
||||||
|
* A WSDL file can contain any number of function definitions.
|
||||||
|
* It's very common that all of these functions use the same location definition.
|
||||||
|
* However, technically each function can potentially use a different location.
|
||||||
|
*
|
||||||
|
* The `$function` parameter should be a string with the the SOAP function name.
|
||||||
|
* See also `getFunctions()` for a list of all available functions.
|
||||||
|
*
|
||||||
|
* For easier access, this function also accepts a numeric function index.
|
||||||
|
* It then uses `getFunctions()` internally to get the function
|
||||||
|
* name for the given index.
|
||||||
|
* This is particularly useful for the very common case where all functions use the
|
||||||
|
* same location and accessing the first location is sufficient.
|
||||||
|
*
|
||||||
|
* @param string|int $function
|
||||||
|
* @return string
|
||||||
|
* @throws SoapFault if given function does not exist
|
||||||
|
* @see self::getFunctions()
|
||||||
|
*/
|
||||||
|
public function getLocation($function)
|
||||||
|
{
|
||||||
|
if (is_int($function)) {
|
||||||
|
$functions = $this->getFunctions();
|
||||||
|
if (isset($functions[$function]) && preg_match('/^\w+ (\w+)\(/', $functions[$function], $match)) {
|
||||||
|
$function = $match[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode request for given $function
|
||||||
|
return (string)$this->encoder->encode($function, array())->getUri();
|
||||||
|
}
|
||||||
|
}
|
39
vendor/clue/soap-react/src/Factory.php
vendored
Normal file
39
vendor/clue/soap-react/src/Factory.php
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Soap;
|
||||||
|
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use Clue\React\Buzz\Browser;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
class Factory
|
||||||
|
{
|
||||||
|
private $loop;
|
||||||
|
private $browser;
|
||||||
|
|
||||||
|
public function __construct(LoopInterface $loop, Browser $browser = null)
|
||||||
|
{
|
||||||
|
if ($browser === null) {
|
||||||
|
$browser = new Browser($loop);
|
||||||
|
}
|
||||||
|
$this->loop = $loop;
|
||||||
|
$this->browser = $browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createClient($wsdl)
|
||||||
|
{
|
||||||
|
$that = $this;
|
||||||
|
|
||||||
|
return $this->browser->get($wsdl)->then(function (ResponseInterface $response) use ($that) {
|
||||||
|
return $that->createClientFromWsdl((string)$response->getBody());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createClientFromWsdl($wsdlContents)
|
||||||
|
{
|
||||||
|
$browser = $this->browser;
|
||||||
|
$url = 'data://text/plain;base64,' . base64_encode($wsdlContents);
|
||||||
|
|
||||||
|
return new Client($url, $browser);
|
||||||
|
}
|
||||||
|
}
|
37
vendor/clue/soap-react/src/Protocol/ClientDecoder.php
vendored
Normal file
37
vendor/clue/soap-react/src/Protocol/ClientDecoder.php
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Soap\Protocol;
|
||||||
|
|
||||||
|
use \SoapClient;
|
||||||
|
|
||||||
|
class ClientDecoder extends SoapClient
|
||||||
|
{
|
||||||
|
private $response = null;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
// to not pass actual WSDL to parent constructor
|
||||||
|
// use faked non-wsdl-mode to let every method call pass through (pseudoCall)
|
||||||
|
parent::__construct(null, array('location' => '1', 'uri' => '2'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decode($response)
|
||||||
|
{
|
||||||
|
// temporarily save response internally for further processing
|
||||||
|
$this->response = $response;
|
||||||
|
|
||||||
|
// pretend we just invoked a SOAP function.
|
||||||
|
// internally, use the injected response to parse its results
|
||||||
|
$ret = $this->pseudoCall();
|
||||||
|
$this->response = null;
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __doRequest($request, $location, $action, $version, $one_way = 0)
|
||||||
|
{
|
||||||
|
// the actual result doesn't actually matter, just return the given result
|
||||||
|
// this will be processed internally and will return the parsed result
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
}
|
38
vendor/clue/soap-react/src/Protocol/ClientEncoder.php
vendored
Normal file
38
vendor/clue/soap-react/src/Protocol/ClientEncoder.php
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Soap\Protocol;
|
||||||
|
|
||||||
|
use \SoapClient;
|
||||||
|
use RingCentral\Psr7\Request;
|
||||||
|
|
||||||
|
class ClientEncoder extends SoapClient
|
||||||
|
{
|
||||||
|
private $request = null;
|
||||||
|
|
||||||
|
public function encode($name, $args)
|
||||||
|
{
|
||||||
|
$this->__soapCall($name, $args);
|
||||||
|
|
||||||
|
$request = $this->request;
|
||||||
|
$this->request = null;
|
||||||
|
|
||||||
|
return $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __doRequest($request, $location, $action, $version, $one_way = 0)
|
||||||
|
{
|
||||||
|
$this->request = new Request(
|
||||||
|
'POST',
|
||||||
|
(string)$location,
|
||||||
|
array(
|
||||||
|
'SOAPAction' => (string)$action,
|
||||||
|
'Content-Type' => 'text/xml; charset=utf-8',
|
||||||
|
'Content-Length' => strlen($request)
|
||||||
|
),
|
||||||
|
(string)$request
|
||||||
|
);
|
||||||
|
|
||||||
|
// do not actually block here, just pretend we're done...
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
16
vendor/clue/soap-react/src/Proxy.php
vendored
Normal file
16
vendor/clue/soap-react/src/Proxy.php
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Soap;
|
||||||
|
|
||||||
|
class Proxy
|
||||||
|
{
|
||||||
|
public function __construct(Client $client)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($name, $args)
|
||||||
|
{
|
||||||
|
return $this->client->soapCall($name, $args);
|
||||||
|
}
|
||||||
|
}
|
21
vendor/clue/socket-raw/LICENSE
vendored
Normal file
21
vendor/clue/socket-raw/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Christian Lück
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
80
vendor/clue/socket-raw/src/Exception.php
vendored
Normal file
80
vendor/clue/socket-raw/src/Exception.php
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socket\Raw;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
class Exception extends RuntimeException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create an Exception after a socket operation on the given $resource failed
|
||||||
|
*
|
||||||
|
* @param resource $resource
|
||||||
|
* @param string $messagePrefix
|
||||||
|
* @return self
|
||||||
|
* @uses socket_last_error() to get last socket error code
|
||||||
|
* @uses socket_clear_error() to clear socket error code
|
||||||
|
* @uses self::createFromCode() to automatically construct exception with full error message
|
||||||
|
*/
|
||||||
|
public static function createFromSocketResource($resource, $messagePrefix = 'Socket operation failed')
|
||||||
|
{
|
||||||
|
$code = socket_last_error($resource);
|
||||||
|
socket_clear_error($resource);
|
||||||
|
|
||||||
|
return self::createFromCode($code, $messagePrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an Exception after a global socket operation failed (like socket creation)
|
||||||
|
*
|
||||||
|
* @param string $messagePrefix
|
||||||
|
* @return self
|
||||||
|
* @uses socket_last_error() to get last global error code
|
||||||
|
* @uses socket_clear_error() to clear global error code
|
||||||
|
* @uses self::createFromCode() to automatically construct exception with full error message
|
||||||
|
*/
|
||||||
|
public static function createFromGlobalSocketOperation($messagePrefix = 'Socket operation failed')
|
||||||
|
{
|
||||||
|
$code = socket_last_error();
|
||||||
|
socket_clear_error();
|
||||||
|
|
||||||
|
return self::createFromCode($code, $messagePrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an Exception for given error $code
|
||||||
|
*
|
||||||
|
* @param int $code
|
||||||
|
* @param string $messagePrefix
|
||||||
|
* @return self
|
||||||
|
* @throws Exception if given $val is boolean false
|
||||||
|
* @uses self::getErrorMessage() to translate error code to error message
|
||||||
|
*/
|
||||||
|
public static function createFromCode($code, $messagePrefix = 'Socket error')
|
||||||
|
{
|
||||||
|
return new self($messagePrefix . ': ' . self::getErrorMessage($code), $code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get error message for given error code
|
||||||
|
*
|
||||||
|
* @param int $code error code
|
||||||
|
* @return string
|
||||||
|
* @uses socket_strerror() to translate error code to error message
|
||||||
|
* @uses get_defined_constants() to check for related error constant
|
||||||
|
*/
|
||||||
|
protected static function getErrorMessage($code)
|
||||||
|
{
|
||||||
|
$string = socket_strerror($code);
|
||||||
|
|
||||||
|
// search constant starting with SOCKET_ for this error code
|
||||||
|
foreach (get_defined_constants() as $key => $value) {
|
||||||
|
if($value === $code && strpos($key, 'SOCKET_') === 0) {
|
||||||
|
$string .= ' (' . $key . ')';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
}
|
273
vendor/clue/socket-raw/src/Factory.php
vendored
Normal file
273
vendor/clue/socket-raw/src/Factory.php
vendored
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socket\Raw;
|
||||||
|
|
||||||
|
use \InvalidArgumentException;
|
||||||
|
|
||||||
|
class Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* create client socket connected to given target address
|
||||||
|
*
|
||||||
|
* @param string $address target address to connect to
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws InvalidArgumentException if given address is invalid
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::createFromString()
|
||||||
|
* @uses Socket::connect()
|
||||||
|
*/
|
||||||
|
public function createClient($address)
|
||||||
|
{
|
||||||
|
$socket = $this->createFromString($address, $scheme);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$socket->connect($address);
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
|
$socket->close();
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create server socket bound to given address (and start listening for streaming clients to connect to this stream socket)
|
||||||
|
*
|
||||||
|
* @param string $address address to bind socket to
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::createFromString()
|
||||||
|
* @uses Socket::bind()
|
||||||
|
* @uses Socket::listen() only for stream sockets (TCP/UNIX)
|
||||||
|
*/
|
||||||
|
public function createServer($address)
|
||||||
|
{
|
||||||
|
$socket = $this->createFromString($address, $scheme);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$socket->bind($address);
|
||||||
|
|
||||||
|
if ($socket->getType() === SOCK_STREAM) {
|
||||||
|
$socket->listen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
|
$socket->close();
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create TCP/IPv4 stream socket
|
||||||
|
*
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::create()
|
||||||
|
*/
|
||||||
|
public function createTcp4()
|
||||||
|
{
|
||||||
|
return $this->create(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create TCP/IPv6 stream socket
|
||||||
|
*
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::create()
|
||||||
|
*/
|
||||||
|
public function createTcp6()
|
||||||
|
{
|
||||||
|
return $this->create(AF_INET6, SOCK_STREAM, SOL_TCP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create UDP/IPv4 datagram socket
|
||||||
|
*
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::create()
|
||||||
|
*/
|
||||||
|
public function createUdp4()
|
||||||
|
{
|
||||||
|
return $this->create(AF_INET, SOCK_DGRAM, SOL_UDP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create UDP/IPv6 datagram socket
|
||||||
|
*
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::create()
|
||||||
|
*/
|
||||||
|
public function createUdp6()
|
||||||
|
{
|
||||||
|
return $this->create(AF_INET6, SOCK_DGRAM, SOL_UDP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create local UNIX stream socket
|
||||||
|
*
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::create()
|
||||||
|
*/
|
||||||
|
public function createUnix()
|
||||||
|
{
|
||||||
|
return $this->create(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create local UNIX datagram socket (UDG)
|
||||||
|
*
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::create()
|
||||||
|
*/
|
||||||
|
public function createUdg()
|
||||||
|
{
|
||||||
|
return $this->create(AF_UNIX, SOCK_DGRAM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create raw ICMP/IPv4 datagram socket (requires root!)
|
||||||
|
*
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::create()
|
||||||
|
*/
|
||||||
|
public function createIcmp4()
|
||||||
|
{
|
||||||
|
return $this->create(AF_INET, SOCK_RAW, getprotobyname('icmp'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create raw ICMPv6 (IPv6) datagram socket (requires root!)
|
||||||
|
*
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::create()
|
||||||
|
*/
|
||||||
|
public function createIcmp6()
|
||||||
|
{
|
||||||
|
return $this->create(AF_INET6, SOCK_RAW, 58 /*getprotobyname('icmp')*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create low level socket with given arguments
|
||||||
|
*
|
||||||
|
* @param int $domain
|
||||||
|
* @param int $type
|
||||||
|
* @param int $protocol
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws Exception if creating socket fails
|
||||||
|
* @uses socket_create()
|
||||||
|
*/
|
||||||
|
public function create($domain, $type, $protocol)
|
||||||
|
{
|
||||||
|
$sock = @socket_create($domain, $type, $protocol);
|
||||||
|
if ($sock === false) {
|
||||||
|
throw Exception::createFromGlobalSocketOperation('Unable to create socket');
|
||||||
|
}
|
||||||
|
return new Socket($sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a pair of indistinguishable sockets (commonly used in IPC)
|
||||||
|
*
|
||||||
|
* @param int $domain
|
||||||
|
* @param int $type
|
||||||
|
* @param int $protocol
|
||||||
|
* @return \Socket\Raw\Socket[]
|
||||||
|
* @throws Exception if creating pair of sockets fails
|
||||||
|
* @uses socket_create_pair()
|
||||||
|
*/
|
||||||
|
public function createPair($domain, $type, $protocol)
|
||||||
|
{
|
||||||
|
$ret = @socket_create_pair($domain, $type, $protocol, $pair);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromGlobalSocketOperation('Unable to create pair of sockets');
|
||||||
|
}
|
||||||
|
return array(new Socket($pair[0]), new Socket($pair[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create TCP/IPv4 stream socket and listen for new connections
|
||||||
|
*
|
||||||
|
* @param int $port
|
||||||
|
* @param int $backlog
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws Exception if creating listening socket fails
|
||||||
|
* @uses socket_create_listen()
|
||||||
|
* @see self::createServer() as an alternative to bind to specific IP, IPv6, UDP, UNIX, UGP
|
||||||
|
*/
|
||||||
|
public function createListen($port, $backlog = 128)
|
||||||
|
{
|
||||||
|
$sock = @socket_create_listen($port, $backlog);
|
||||||
|
if ($sock === false) {
|
||||||
|
throw Exception::createFromGlobalSocketOperation('Unable to create listening socket');
|
||||||
|
}
|
||||||
|
return new Socket($sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create socket for given address
|
||||||
|
*
|
||||||
|
* @param string $address (passed by reference in order to remove scheme, if present)
|
||||||
|
* @param string $scheme default scheme to use, defaults to TCP (passed by reference in order to update with actual scheme used)
|
||||||
|
* @return \Socket\Raw\Socket
|
||||||
|
* @throws InvalidArgumentException if given address is invalid
|
||||||
|
* @throws Exception in case creating socket failed
|
||||||
|
* @uses self::createTcp4() etc.
|
||||||
|
*/
|
||||||
|
public function createFromString(&$address, &$scheme)
|
||||||
|
{
|
||||||
|
if ($scheme === null) {
|
||||||
|
$scheme = 'tcp';
|
||||||
|
}
|
||||||
|
|
||||||
|
$hasScheme = false;
|
||||||
|
|
||||||
|
$pos = strpos($address, '://');
|
||||||
|
if ($pos !== false) {
|
||||||
|
$scheme = substr($address, 0, $pos);
|
||||||
|
$address = substr($address, $pos + 3);
|
||||||
|
$hasScheme = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($address, ':') !== strrpos($address, ':') && in_array($scheme, array('tcp', 'udp', 'icmp'))) {
|
||||||
|
// TCP/UDP/ICMP address with several colons => must be IPv6
|
||||||
|
$scheme .= '6';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($scheme === 'tcp') {
|
||||||
|
$socket = $this->createTcp4();
|
||||||
|
} elseif ($scheme === 'udp') {
|
||||||
|
$socket = $this->createUdp4();
|
||||||
|
} elseif ($scheme === 'tcp6') {
|
||||||
|
$socket = $this->createTcp6();
|
||||||
|
} elseif ($scheme === 'udp6') {
|
||||||
|
$socket = $this->createUdp6();
|
||||||
|
} elseif ($scheme === 'unix') {
|
||||||
|
$socket = $this->createUnix();
|
||||||
|
} elseif ($scheme === 'udg') {
|
||||||
|
$socket = $this->createUdg();
|
||||||
|
} elseif ($scheme === 'icmp') {
|
||||||
|
$socket = $this->createIcmp4();
|
||||||
|
} elseif ($scheme === 'icmp6') {
|
||||||
|
$socket = $this->createIcmp6();
|
||||||
|
if ($hasScheme) {
|
||||||
|
// scheme was stripped from address, resulting IPv6 must not
|
||||||
|
// have a port (due to ICMP) and thus must not be enclosed in
|
||||||
|
// square brackets
|
||||||
|
$address = trim($address, '[]');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException('Invalid address scheme given');
|
||||||
|
}
|
||||||
|
return $socket;
|
||||||
|
}
|
||||||
|
}
|
531
vendor/clue/socket-raw/src/Socket.php
vendored
Normal file
531
vendor/clue/socket-raw/src/Socket.php
vendored
Normal file
@ -0,0 +1,531 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socket\Raw;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* simple and lightweight OOP wrapper for the low level sockets extension (ext-sockets)
|
||||||
|
*
|
||||||
|
* @author clue
|
||||||
|
* @link https://github.com/clue/socket-raw
|
||||||
|
*/
|
||||||
|
class Socket
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* reference to actual socket resource
|
||||||
|
*
|
||||||
|
* @var resource
|
||||||
|
*/
|
||||||
|
private $resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* instanciate socket wrapper for given socket resource
|
||||||
|
*
|
||||||
|
* should usually not be called manually, see Factory
|
||||||
|
*
|
||||||
|
* @param resource $resource
|
||||||
|
* @see Factory as the preferred (and simplest) way to construct socket instances
|
||||||
|
*/
|
||||||
|
public function __construct($resource)
|
||||||
|
{
|
||||||
|
$this->resource = $resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get actual socket resource
|
||||||
|
*
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
|
public function getResource()
|
||||||
|
{
|
||||||
|
return $this->resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* accept an incomming connection on this listening socket
|
||||||
|
*
|
||||||
|
* @return \Socket\Raw\Socket new connected socket used for communication
|
||||||
|
* @throws Exception on error, if this is not a listening socket or there's no connection pending
|
||||||
|
* @see self::selectRead() to check if this listening socket can accept()
|
||||||
|
* @see Factory::createServer() to create a listening socket
|
||||||
|
* @see self::listen() has to be called first
|
||||||
|
* @uses socket_accept()
|
||||||
|
*/
|
||||||
|
public function accept()
|
||||||
|
{
|
||||||
|
$resource = @socket_accept($this->resource);
|
||||||
|
if ($resource === false) {
|
||||||
|
throw Exception::createFromGlobalSocketOperation();
|
||||||
|
}
|
||||||
|
return new Socket($resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* binds a name/address/path to this socket
|
||||||
|
*
|
||||||
|
* has to be called before issuing connect() or listen()
|
||||||
|
*
|
||||||
|
* @param string $address either of IPv4:port, hostname:port, [IPv6]:port, unix-path
|
||||||
|
* @return self $this (chainable)
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses socket_bind()
|
||||||
|
*/
|
||||||
|
public function bind($address)
|
||||||
|
{
|
||||||
|
$ret = @socket_bind($this->resource, $this->unformatAddress($address, $port), $port);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* close this socket
|
||||||
|
*
|
||||||
|
* ATTENTION: make sure to NOT re-use this socket instance after closing it!
|
||||||
|
* its socket resource remains closed and most further operations will fail!
|
||||||
|
*
|
||||||
|
* @return self $this (chainable)
|
||||||
|
* @see self::shutdown() should be called before closing socket
|
||||||
|
* @uses socket_close()
|
||||||
|
*/
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
socket_close($this->resource);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initiate a connection to given address
|
||||||
|
*
|
||||||
|
* @param string $address either of IPv4:port, hostname:port, [IPv6]:port, unix-path
|
||||||
|
* @return self $this (chainable)
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses socket_connect()
|
||||||
|
*/
|
||||||
|
public function connect($address)
|
||||||
|
{
|
||||||
|
$ret = @socket_connect($this->resource, $this->unformatAddress($address, $port), $port);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a new connection to given address, wait for up to $timeout seconds
|
||||||
|
*
|
||||||
|
* The given $timeout parameter is an upper bound, a maximum time to wait
|
||||||
|
* for the connection to be either accepted or rejected.
|
||||||
|
*
|
||||||
|
* The resulting socket resource will be set to non-blocking mode,
|
||||||
|
* regardless of its previous state and whether this method succedes or
|
||||||
|
* if it fails. Make sure to reset with `setBlocking(true)` if you want to
|
||||||
|
* continue using blocking calls.
|
||||||
|
*
|
||||||
|
* @param string $address either of IPv4:port, hostname:port, [IPv6]:port, unix-path
|
||||||
|
* @param float $timeout maximum time to wait (in seconds)
|
||||||
|
* @return self $this (chainable)
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::setBlocking() to enable non-blocking mode
|
||||||
|
* @uses self::connect() to initiate the connection
|
||||||
|
* @uses self::selectWrite() to wait for the connection to complete
|
||||||
|
* @uses self::assertAlive() to check connection state
|
||||||
|
*/
|
||||||
|
public function connectTimeout($address, $timeout)
|
||||||
|
{
|
||||||
|
$this->setBlocking(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// socket is non-blocking, so connect should emit EINPROGRESS
|
||||||
|
$this->connect($address);
|
||||||
|
|
||||||
|
// socket is already connected immediately?
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
|
// non-blocking connect() should be EINPROGRESS => otherwise re-throw
|
||||||
|
if ($e->getCode() !== SOCKET_EINPROGRESS) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// connection should be completed (or rejected) within timeout
|
||||||
|
if ($this->selectWrite($timeout) === false) {
|
||||||
|
throw new Exception('Timed out while waiting for connection', SOCKET_ETIMEDOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// confirm connection success (or fail if connected has been rejected)
|
||||||
|
$this->assertAlive();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get socket option
|
||||||
|
*
|
||||||
|
* @param int $level
|
||||||
|
* @param int $optname
|
||||||
|
* @return mixed
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses socket_get_option()
|
||||||
|
*/
|
||||||
|
public function getOption($level, $optname)
|
||||||
|
{
|
||||||
|
$value = @socket_get_option($this->resource, $level, $optname);
|
||||||
|
if ($value === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get remote side's address/path
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses socket_getpeername()
|
||||||
|
*/
|
||||||
|
public function getPeerName()
|
||||||
|
{
|
||||||
|
$ret = @socket_getpeername($this->resource, $address, $port);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $this->formatAddress($address, $port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get local side's address/path
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses socket_getsockname()
|
||||||
|
*/
|
||||||
|
public function getSockName()
|
||||||
|
{
|
||||||
|
$ret = @socket_getsockname($this->resource, $address, $port);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $this->formatAddress($address, $port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start listen for incoming connections
|
||||||
|
*
|
||||||
|
* @param int $backlog maximum number of incoming connections to be queued
|
||||||
|
* @return self $this (chainable)
|
||||||
|
* @throws Exception on error
|
||||||
|
* @see self::bind() has to be called first to bind name to socket
|
||||||
|
* @uses socket_listen()
|
||||||
|
*/
|
||||||
|
public function listen($backlog = 0)
|
||||||
|
{
|
||||||
|
$ret = @socket_listen($this->resource, $backlog);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read up to $length bytes from connect()ed / accept()ed socket
|
||||||
|
*
|
||||||
|
* The $type parameter specifies if this should use either binary safe reading
|
||||||
|
* (PHP_BINARY_READ, the default) or stop at CR or LF characters (PHP_NORMAL_READ)
|
||||||
|
*
|
||||||
|
* @param int $length maximum length to read
|
||||||
|
* @param int $type either of PHP_BINARY_READ (the default) or PHP_NORMAL_READ
|
||||||
|
* @return string
|
||||||
|
* @throws Exception on error
|
||||||
|
* @see self::recv() if you need to pass flags
|
||||||
|
* @uses socket_read()
|
||||||
|
*/
|
||||||
|
public function read($length, $type = PHP_BINARY_READ)
|
||||||
|
{
|
||||||
|
$data = @socket_read($this->resource, $length, $type);
|
||||||
|
if ($data === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* receive up to $length bytes from connect()ed / accept()ed socket
|
||||||
|
*
|
||||||
|
* @param int $length maximum length to read
|
||||||
|
* @param int $flags
|
||||||
|
* @return string
|
||||||
|
* @throws Exception on error
|
||||||
|
* @see self::read() if you do not need to pass $flags
|
||||||
|
* @see self::recvFrom() if your socket is not connect()ed
|
||||||
|
* @uses socket_recv()
|
||||||
|
*/
|
||||||
|
public function recv($length, $flags)
|
||||||
|
{
|
||||||
|
$ret = @socket_recv($this->resource, $buffer, $length, $flags);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* receive up to $length bytes from socket
|
||||||
|
*
|
||||||
|
* @param int $length maximum length to read
|
||||||
|
* @param int $flags
|
||||||
|
* @param string $remote reference will be filled with remote/peer address/path
|
||||||
|
* @return string
|
||||||
|
* @throws Exception on error
|
||||||
|
* @see self::recv() if your socket is connect()ed
|
||||||
|
* @uses socket_recvfrom()
|
||||||
|
*/
|
||||||
|
public function recvFrom($length, $flags, &$remote)
|
||||||
|
{
|
||||||
|
$ret = @socket_recvfrom($this->resource, $buffer, $length, $flags, $address, $port);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
$remote = $this->formatAddress($address, $port);
|
||||||
|
return $buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check socket to see if a read/recv/revFrom will not block
|
||||||
|
*
|
||||||
|
* @param float|NULL $sec maximum time to wait (in seconds), 0 = immediate polling, null = no limit
|
||||||
|
* @return boolean true = socket ready (read will not block), false = timeout expired, socket is not ready
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses socket_select()
|
||||||
|
*/
|
||||||
|
public function selectRead($sec = 0)
|
||||||
|
{
|
||||||
|
$usec = $sec === null ? null : (($sec - floor($sec)) * 1000000);
|
||||||
|
$r = array($this->resource);
|
||||||
|
$ret = @socket_select($r, $x, $x, $sec, $usec);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromGlobalSocketOperation('Failed to select socket for reading');
|
||||||
|
}
|
||||||
|
return !!$ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check socket to see if a write/send/sendTo will not block
|
||||||
|
*
|
||||||
|
* @param float|NULL $sec maximum time to wait (in seconds), 0 = immediate polling, null = no limit
|
||||||
|
* @return boolean true = socket ready (write will not block), false = timeout expired, socket is not ready
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses socket_select()
|
||||||
|
*/
|
||||||
|
public function selectWrite($sec = 0)
|
||||||
|
{
|
||||||
|
$usec = $sec === null ? null : (($sec - floor($sec)) * 1000000);
|
||||||
|
$w = array($this->resource);
|
||||||
|
$ret = @socket_select($x, $w, $x, $sec, $usec);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromGlobalSocketOperation('Failed to select socket for writing');
|
||||||
|
}
|
||||||
|
return !!$ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* send given $buffer to connect()ed / accept()ed socket
|
||||||
|
*
|
||||||
|
* @param string $buffer
|
||||||
|
* @param int $flags
|
||||||
|
* @return int number of bytes actually written (make sure to check against given buffer length!)
|
||||||
|
* @throws Exception on error
|
||||||
|
* @see self::write() if you do not need to pass $flags
|
||||||
|
* @see self::sendTo() if your socket is not connect()ed
|
||||||
|
* @uses socket_send()
|
||||||
|
*/
|
||||||
|
public function send($buffer, $flags)
|
||||||
|
{
|
||||||
|
$ret = @socket_send($this->resource, $buffer, strlen($buffer), $flags);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* send given $buffer to socket
|
||||||
|
*
|
||||||
|
* @param string $buffer
|
||||||
|
* @param int $flags
|
||||||
|
* @param string $remote remote/peer address/path
|
||||||
|
* @return int number of bytes actually written
|
||||||
|
* @throws Exception on error
|
||||||
|
* @see self::send() if your socket is connect()ed
|
||||||
|
* @uses socket_sendto()
|
||||||
|
*/
|
||||||
|
public function sendTo($buffer, $flags, $remote)
|
||||||
|
{
|
||||||
|
$ret = @socket_sendto($this->resource, $buffer, strlen($buffer), $flags, $this->unformatAddress($remote, $port), $port);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enable/disable blocking/nonblocking mode (O_NONBLOCK flag)
|
||||||
|
*
|
||||||
|
* @param boolean $toggle
|
||||||
|
* @return self $this (chainable)
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses socket_set_block()
|
||||||
|
* @uses socket_set_nonblock()
|
||||||
|
*/
|
||||||
|
public function setBlocking($toggle = true)
|
||||||
|
{
|
||||||
|
$ret = $toggle ? @socket_set_block($this->resource) : @socket_set_nonblock($this->resource);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set socket option
|
||||||
|
*
|
||||||
|
* @param int $level
|
||||||
|
* @param int $optname
|
||||||
|
* @param mixed $optval
|
||||||
|
* @return self $this (chainable)
|
||||||
|
* @throws Exception on error
|
||||||
|
* @see self::getOption()
|
||||||
|
* @uses socket_set_option()
|
||||||
|
*/
|
||||||
|
public function setOption($level, $optname, $optval)
|
||||||
|
{
|
||||||
|
$ret = @socket_set_option($this->resource, $level, $optname, $optval);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shuts down socket for receiving, sending or both
|
||||||
|
*
|
||||||
|
* @param int $how 0 = shutdown reading, 1 = shutdown writing, 2 = shutdown reading and writing
|
||||||
|
* @return self $this (chainable)
|
||||||
|
* @throws Exception on error
|
||||||
|
* @see self::close()
|
||||||
|
* @uses socket_shutdown()
|
||||||
|
*/
|
||||||
|
public function shutdown($how = 2)
|
||||||
|
{
|
||||||
|
$ret = @socket_shutdown($this->resource, $how);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write $buffer to connect()ed / accept()ed socket
|
||||||
|
*
|
||||||
|
* @param string $buffer
|
||||||
|
* @return int number of bytes actually written
|
||||||
|
* @throws Exception on error
|
||||||
|
* @see self::send() if you need to pass flags
|
||||||
|
* @uses socket_write()
|
||||||
|
*/
|
||||||
|
public function write($buffer)
|
||||||
|
{
|
||||||
|
$ret = @socket_write($this->resource, $buffer);
|
||||||
|
if ($ret === false) {
|
||||||
|
throw Exception::createFromSocketResource($this->resource);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get socket type as passed to socket_create()
|
||||||
|
*
|
||||||
|
* @return int usually either SOCK_STREAM or SOCK_DGRAM
|
||||||
|
* @throws Exception on error
|
||||||
|
* @uses self::getOption()
|
||||||
|
*/
|
||||||
|
public function getType()
|
||||||
|
{
|
||||||
|
return $this->getOption(SOL_SOCKET, SO_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* assert that this socket is alive and its error code is 0
|
||||||
|
*
|
||||||
|
* This will fetch and reset the current socket error code from the
|
||||||
|
* socket and options and will throw an Exception along with error
|
||||||
|
* message and code if the code is not 0, i.e. if it does indicate
|
||||||
|
* an error situation.
|
||||||
|
*
|
||||||
|
* Calling this method should not be needed in most cases and is
|
||||||
|
* likely to not throw an Exception. Each socket operation like
|
||||||
|
* connect(), send(), etc. will throw a dedicated Exception in case
|
||||||
|
* of an error anyway.
|
||||||
|
*
|
||||||
|
* @return self $this (chainable)
|
||||||
|
* @throws Exception if error code is not 0
|
||||||
|
* @uses self::getOption() to retrieve and clear current error code
|
||||||
|
* @uses self::getErrorMessage() to translate error code to
|
||||||
|
*/
|
||||||
|
public function assertAlive()
|
||||||
|
{
|
||||||
|
$code = $this->getOption(SOL_SOCKET, SO_ERROR);
|
||||||
|
if ($code !== 0) {
|
||||||
|
throw Exception::createFromCode($code, 'Socket error');
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* format given address/host/path and port
|
||||||
|
*
|
||||||
|
* @param string $address
|
||||||
|
* @param int $port
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function formatAddress($address, $port)
|
||||||
|
{
|
||||||
|
if ($port !== 0) {
|
||||||
|
if (strpos($address, ':') !== false) {
|
||||||
|
$address = '[' . $address . ']';
|
||||||
|
}
|
||||||
|
$address .= ':' . $port;
|
||||||
|
}
|
||||||
|
return $address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* format given address by splitting it into returned address and port set by reference
|
||||||
|
*
|
||||||
|
* @param string $address
|
||||||
|
* @param int $port
|
||||||
|
* @return string address with port removed
|
||||||
|
*/
|
||||||
|
protected function unformatAddress($address, &$port)
|
||||||
|
{
|
||||||
|
// [::1]:2 => ::1 2
|
||||||
|
// test:2 => test 2
|
||||||
|
// ::1 => ::1
|
||||||
|
// test => test
|
||||||
|
|
||||||
|
$colon = strrpos($address, ':');
|
||||||
|
|
||||||
|
// there is a colon and this is the only colon or there's a closing IPv6 bracket right before it
|
||||||
|
if ($colon !== false && (strpos($address, ':') === $colon || strpos($address, ']') === ($colon - 1))) {
|
||||||
|
$port = (int)substr($address, $colon + 1);
|
||||||
|
$address = substr($address, 0, $colon);
|
||||||
|
|
||||||
|
// remove IPv6 square brackets
|
||||||
|
if (substr($address, 0, 1) === '[') {
|
||||||
|
$address = substr($address, 1, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $address;
|
||||||
|
}
|
||||||
|
}
|
21
vendor/clue/socks-react/LICENSE
vendored
Normal file
21
vendor/clue/socks-react/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2011 Christian Lück
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
382
vendor/clue/socks-react/src/Client.php
vendored
Normal file
382
vendor/clue/socks-react/src/Client.php
vendored
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Socks;
|
||||||
|
|
||||||
|
use React\Promise;
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
use React\Promise\Deferred;
|
||||||
|
use React\Socket\ConnectionInterface;
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
use React\Socket\FixedUriConnector;
|
||||||
|
use \Exception;
|
||||||
|
use \InvalidArgumentException;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
class Client implements ConnectorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var ConnectorInterface
|
||||||
|
*/
|
||||||
|
private $connector;
|
||||||
|
|
||||||
|
private $socksUri;
|
||||||
|
|
||||||
|
private $protocolVersion = null;
|
||||||
|
|
||||||
|
private $auth = null;
|
||||||
|
|
||||||
|
public function __construct($socksUri, ConnectorInterface $connector)
|
||||||
|
{
|
||||||
|
// support `sockss://` scheme for SOCKS over TLS
|
||||||
|
// support `socks+unix://` scheme for Unix domain socket (UDS) paths
|
||||||
|
if (preg_match('/^(socks(?:5|4|4a)?)(s|\+unix):\/\/(.*?@)?(.+?)$/', $socksUri, $match)) {
|
||||||
|
// rewrite URI to parse SOCKS scheme, authentication and dummy host
|
||||||
|
$socksUri = $match[1] . '://' . $match[3] . 'localhost';
|
||||||
|
|
||||||
|
// connector uses appropriate transport scheme and explicit host given
|
||||||
|
$connector = new FixedUriConnector(
|
||||||
|
($match[2] === 's' ? 'tls://' : 'unix://') . $match[4],
|
||||||
|
$connector
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// assume default scheme if none is given
|
||||||
|
if (strpos($socksUri, '://') === false) {
|
||||||
|
$socksUri = 'socks://' . $socksUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse URI into individual parts
|
||||||
|
$parts = parse_url($socksUri);
|
||||||
|
if (!$parts || !isset($parts['scheme'], $parts['host'])) {
|
||||||
|
throw new \InvalidArgumentException('Invalid SOCKS server URI "' . $socksUri . '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
// assume default port
|
||||||
|
if (!isset($parts['port'])) {
|
||||||
|
$parts['port'] = 1080;
|
||||||
|
}
|
||||||
|
|
||||||
|
// user or password in URI => SOCKS5 authentication
|
||||||
|
if (isset($parts['user']) || isset($parts['pass'])) {
|
||||||
|
if ($parts['scheme'] === 'socks') {
|
||||||
|
// default to using SOCKS5 if not given explicitly
|
||||||
|
$parts['scheme'] = 'socks5';
|
||||||
|
} elseif ($parts['scheme'] !== 'socks5') {
|
||||||
|
// fail if any other protocol version given explicitly
|
||||||
|
throw new InvalidArgumentException('Authentication requires SOCKS5. Consider using protocol version 5 or waive authentication');
|
||||||
|
}
|
||||||
|
$parts += array('user' => '', 'pass' => '');
|
||||||
|
$this->setAuth(rawurldecode($parts['user']), rawurldecode($parts['pass']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for valid protocol version from URI scheme
|
||||||
|
$this->setProtocolVersionFromScheme($parts['scheme']);
|
||||||
|
|
||||||
|
$this->socksUri = $parts['host'] . ':' . $parts['port'];
|
||||||
|
$this->connector = $connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setProtocolVersionFromScheme($scheme)
|
||||||
|
{
|
||||||
|
if ($scheme === 'socks' || $scheme === 'socks4a') {
|
||||||
|
$this->protocolVersion = '4a';
|
||||||
|
} elseif ($scheme === 'socks5') {
|
||||||
|
$this->protocolVersion = '5';
|
||||||
|
} elseif ($scheme === 'socks4') {
|
||||||
|
$this->protocolVersion = '4';
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException('Invalid protocol version given "' . $scheme . '://"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set login data for username/password authentication method (RFC1929)
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @param string $password
|
||||||
|
* @link http://tools.ietf.org/html/rfc1929
|
||||||
|
*/
|
||||||
|
private function setAuth($username, $password)
|
||||||
|
{
|
||||||
|
if (strlen($username) > 255 || strlen($password) > 255) {
|
||||||
|
throw new InvalidArgumentException('Both username and password MUST NOT exceed a length of 255 bytes each');
|
||||||
|
}
|
||||||
|
$this->auth = pack('C2', 0x01, strlen($username)) . $username . pack('C', strlen($password)) . $password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establish a TCP/IP connection to the given target URI through the SOCKS server
|
||||||
|
*
|
||||||
|
* Many higher-level networking protocols build on top of TCP. It you're dealing
|
||||||
|
* with one such client implementation, it probably uses/accepts an instance
|
||||||
|
* implementing React's `ConnectorInterface` (and usually its default `Connector`
|
||||||
|
* instance). In this case you can also pass this `Connector` instance instead
|
||||||
|
* to make this client implementation SOCKS-aware. That's it.
|
||||||
|
*
|
||||||
|
* @param string $uri
|
||||||
|
* @return PromiseInterface Promise<ConnectionInterface,Exception>
|
||||||
|
*/
|
||||||
|
public function connect($uri)
|
||||||
|
{
|
||||||
|
if (strpos($uri, '://') === false) {
|
||||||
|
$uri = 'tcp://' . $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = parse_url($uri);
|
||||||
|
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
|
||||||
|
return Promise\reject(new InvalidArgumentException('Invalid target URI specified'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = trim($parts['host'], '[]');
|
||||||
|
$port = $parts['port'];
|
||||||
|
|
||||||
|
if ($this->protocolVersion === '4' && false === filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||||
|
return Promise\reject(new InvalidArgumentException('Requires an IPv4 address for SOCKS4'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($host) > 255 || $port > 65535 || $port < 0 || (string)$port !== (string)(int)$port) {
|
||||||
|
return Promise\reject(new InvalidArgumentException('Invalid target specified'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct URI to SOCKS server to connect to
|
||||||
|
$socksUri = $this->socksUri;
|
||||||
|
|
||||||
|
// append path from URI if given
|
||||||
|
if (isset($parts['path'])) {
|
||||||
|
$socksUri .= $parts['path'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse query args
|
||||||
|
$args = array();
|
||||||
|
if (isset($parts['query'])) {
|
||||||
|
parse_str($parts['query'], $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append hostname from URI to query string unless explicitly given
|
||||||
|
if (!isset($args['hostname'])) {
|
||||||
|
$args['hostname'] = $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
// append query string
|
||||||
|
$socksUri .= '?' . http_build_query($args, '', '&');
|
||||||
|
|
||||||
|
// append fragment from URI if given
|
||||||
|
if (isset($parts['fragment'])) {
|
||||||
|
$socksUri .= '#' . $parts['fragment'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$that = $this;
|
||||||
|
|
||||||
|
// start TCP/IP connection to SOCKS server and then
|
||||||
|
// handle SOCKS protocol once connection is ready
|
||||||
|
// resolve plain connection once SOCKS protocol is completed
|
||||||
|
return $this->connector->connect($socksUri)->then(
|
||||||
|
function (ConnectionInterface $stream) use ($that, $host, $port) {
|
||||||
|
return $that->handleConnectedSocks($stream, $host, $port);
|
||||||
|
},
|
||||||
|
function (Exception $e) {
|
||||||
|
throw new RuntimeException('Unable to connect to proxy (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $e);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal helper used to handle the communication with the SOCKS server
|
||||||
|
*
|
||||||
|
* @param ConnectionInterface $stream
|
||||||
|
* @param string $host
|
||||||
|
* @param int $port
|
||||||
|
* @return Promise Promise<ConnectionInterface, Exception>
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function handleConnectedSocks(ConnectionInterface $stream, $host, $port)
|
||||||
|
{
|
||||||
|
$deferred = new Deferred(function ($_, $reject) {
|
||||||
|
$reject(new RuntimeException('Connection canceled while establishing SOCKS session (ECONNABORTED)', defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103));
|
||||||
|
});
|
||||||
|
|
||||||
|
$reader = new StreamReader();
|
||||||
|
$stream->on('data', array($reader, 'write'));
|
||||||
|
|
||||||
|
$stream->on('error', $onError = function (Exception $e) use ($deferred) {
|
||||||
|
$deferred->reject(new RuntimeException('Stream error while waiting for response from proxy (EIO)', defined('SOCKET_EIO') ? SOCKET_EIO : 5, $e));
|
||||||
|
});
|
||||||
|
|
||||||
|
$stream->on('close', $onClose = function () use ($deferred) {
|
||||||
|
$deferred->reject(new RuntimeException('Connection to proxy lost while waiting for response (ECONNRESET)', defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 104));
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($this->protocolVersion === '5') {
|
||||||
|
$promise = $this->handleSocks5($stream, $host, $port, $reader);
|
||||||
|
} else {
|
||||||
|
$promise = $this->handleSocks4($stream, $host, $port, $reader);
|
||||||
|
}
|
||||||
|
$promise->then(function () use ($deferred, $stream) {
|
||||||
|
$deferred->resolve($stream);
|
||||||
|
}, function (Exception $error) use ($deferred) {
|
||||||
|
// pass custom RuntimeException through as-is, otherwise wrap in protocol error
|
||||||
|
if (!$error instanceof RuntimeException) {
|
||||||
|
$error = new RuntimeException('Invalid response received from proxy (EBADMSG)', defined('SOCKET_EBADMSG') ? SOCKET_EBADMSG: 71, $error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$deferred->reject($error);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $deferred->promise()->then(
|
||||||
|
function (ConnectionInterface $stream) use ($reader, $onError, $onClose) {
|
||||||
|
$stream->removeListener('data', array($reader, 'write'));
|
||||||
|
$stream->removeListener('error', $onError);
|
||||||
|
$stream->removeListener('close', $onClose);
|
||||||
|
|
||||||
|
return $stream;
|
||||||
|
},
|
||||||
|
function ($error) use ($stream, $onClose) {
|
||||||
|
$stream->removeListener('close', $onClose);
|
||||||
|
$stream->close();
|
||||||
|
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleSocks4(ConnectionInterface $stream, $host, $port, StreamReader $reader)
|
||||||
|
{
|
||||||
|
// do not resolve hostname. only try to convert to IP
|
||||||
|
$ip = ip2long($host);
|
||||||
|
|
||||||
|
// send IP or (0.0.0.1) if invalid
|
||||||
|
$data = pack('C2nNC', 0x04, 0x01, $port, $ip === false ? 1 : $ip, 0x00);
|
||||||
|
|
||||||
|
if ($ip === false) {
|
||||||
|
// host is not a valid IP => send along hostname (SOCKS4a)
|
||||||
|
$data .= $host . pack('C', 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stream->write($data);
|
||||||
|
|
||||||
|
return $reader->readBinary(array(
|
||||||
|
'null' => 'C',
|
||||||
|
'status' => 'C',
|
||||||
|
'port' => 'n',
|
||||||
|
'ip' => 'N'
|
||||||
|
))->then(function ($data) {
|
||||||
|
if ($data['null'] !== 0x00) {
|
||||||
|
throw new Exception('Invalid SOCKS response');
|
||||||
|
}
|
||||||
|
if ($data['status'] !== 0x5a) {
|
||||||
|
throw new RuntimeException('Proxy refused connection with SOCKS error code ' . sprintf('0x%02X', $data['status']) . ' (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleSocks5(ConnectionInterface $stream, $host, $port, StreamReader $reader)
|
||||||
|
{
|
||||||
|
// protocol version 5
|
||||||
|
$data = pack('C', 0x05);
|
||||||
|
|
||||||
|
$auth = $this->auth;
|
||||||
|
if ($auth === null) {
|
||||||
|
// one method, no authentication
|
||||||
|
$data .= pack('C2', 0x01, 0x00);
|
||||||
|
} else {
|
||||||
|
// two methods, username/password and no authentication
|
||||||
|
$data .= pack('C3', 0x02, 0x02, 0x00);
|
||||||
|
}
|
||||||
|
$stream->write($data);
|
||||||
|
|
||||||
|
$that = $this;
|
||||||
|
|
||||||
|
return $reader->readBinary(array(
|
||||||
|
'version' => 'C',
|
||||||
|
'method' => 'C'
|
||||||
|
))->then(function ($data) use ($auth, $stream, $reader) {
|
||||||
|
if ($data['version'] !== 0x05) {
|
||||||
|
throw new Exception('Version/Protocol mismatch');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($data['method'] === 0x02 && $auth !== null) {
|
||||||
|
// username/password authentication requested and provided
|
||||||
|
$stream->write($auth);
|
||||||
|
|
||||||
|
return $reader->readBinary(array(
|
||||||
|
'version' => 'C',
|
||||||
|
'status' => 'C'
|
||||||
|
))->then(function ($data) {
|
||||||
|
if ($data['version'] !== 0x01 || $data['status'] !== 0x00) {
|
||||||
|
throw new RuntimeException('Username/Password authentication failed (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ($data['method'] !== 0x00) {
|
||||||
|
// any other method than "no authentication"
|
||||||
|
throw new RuntimeException('No acceptable authentication method found (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13);
|
||||||
|
}
|
||||||
|
})->then(function () use ($stream, $reader, $host, $port) {
|
||||||
|
// do not resolve hostname. only try to convert to (binary/packed) IP
|
||||||
|
$ip = @inet_pton($host);
|
||||||
|
|
||||||
|
$data = pack('C3', 0x05, 0x01, 0x00);
|
||||||
|
if ($ip === false) {
|
||||||
|
// not an IP, send as hostname
|
||||||
|
$data .= pack('C2', 0x03, strlen($host)) . $host;
|
||||||
|
} else {
|
||||||
|
// send as IPv4 / IPv6
|
||||||
|
$data .= pack('C', (strpos($host, ':') === false) ? 0x01 : 0x04) . $ip;
|
||||||
|
}
|
||||||
|
$data .= pack('n', $port);
|
||||||
|
|
||||||
|
$stream->write($data);
|
||||||
|
|
||||||
|
return $reader->readBinary(array(
|
||||||
|
'version' => 'C',
|
||||||
|
'status' => 'C',
|
||||||
|
'null' => 'C',
|
||||||
|
'type' => 'C'
|
||||||
|
));
|
||||||
|
})->then(function ($data) use ($reader) {
|
||||||
|
if ($data['version'] !== 0x05 || $data['null'] !== 0x00) {
|
||||||
|
throw new Exception('Invalid SOCKS response');
|
||||||
|
}
|
||||||
|
if ($data['status'] !== 0x00) {
|
||||||
|
// map limited list of SOCKS error codes to common socket error conditions
|
||||||
|
// @link https://tools.ietf.org/html/rfc1928#section-6
|
||||||
|
if ($data['status'] === Server::ERROR_GENERAL) {
|
||||||
|
throw new RuntimeException('SOCKS server reported a general server failure (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111);
|
||||||
|
} elseif ($data['status'] === Server::ERROR_NOT_ALLOWED_BY_RULESET) {
|
||||||
|
throw new RuntimeException('SOCKS server reported connection is not allowed by ruleset (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13);
|
||||||
|
} elseif ($data['status'] === Server::ERROR_NETWORK_UNREACHABLE) {
|
||||||
|
throw new RuntimeException('SOCKS server reported network unreachable (ENETUNREACH)', defined('SOCKET_ENETUNREACH') ? SOCKET_ENETUNREACH : 101);
|
||||||
|
} elseif ($data['status'] === Server::ERROR_HOST_UNREACHABLE) {
|
||||||
|
throw new RuntimeException('SOCKS server reported host unreachable (EHOSTUNREACH)', defined('SOCKET_EHOSTUNREACH') ? SOCKET_EHOSTUNREACH : 113);
|
||||||
|
} elseif ($data['status'] === Server::ERROR_CONNECTION_REFUSED) {
|
||||||
|
throw new RuntimeException('SOCKS server reported connection refused (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111);
|
||||||
|
} elseif ($data['status'] === Server::ERROR_TTL) {
|
||||||
|
throw new RuntimeException('SOCKS server reported TTL/timeout expired (ETIMEDOUT)', defined('SOCKET_ETIMEDOUT') ? SOCKET_ETIMEDOUT : 110);
|
||||||
|
} elseif ($data['status'] === Server::ERROR_COMMAND_UNSUPPORTED) {
|
||||||
|
throw new RuntimeException('SOCKS server does not support the CONNECT command (EPROTO)', defined('SOCKET_EPROTO') ? SOCKET_EPROTO : 71);
|
||||||
|
} elseif ($data['status'] === Server::ERROR_ADDRESS_UNSUPPORTED) {
|
||||||
|
throw new RuntimeException('SOCKS server does not support this address type (EPROTO)', defined('SOCKET_EPROTO') ? SOCKET_EPROTO : 71);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException('SOCKS server reported an unassigned error code ' . sprintf('0x%02X', $data['status']) . ' (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111);
|
||||||
|
}
|
||||||
|
if ($data['type'] === 0x01) {
|
||||||
|
// IPv4 address => skip IP and port
|
||||||
|
return $reader->readLength(6);
|
||||||
|
} elseif ($data['type'] === 0x03) {
|
||||||
|
// domain name => read domain name length
|
||||||
|
return $reader->readBinary(array(
|
||||||
|
'length' => 'C'
|
||||||
|
))->then(function ($data) use ($reader) {
|
||||||
|
// skip domain name and port
|
||||||
|
return $reader->readLength($data['length'] + 2);
|
||||||
|
});
|
||||||
|
} elseif ($data['type'] === 0x04) {
|
||||||
|
// IPv6 address => skip IP and port
|
||||||
|
return $reader->readLength(18);
|
||||||
|
} else {
|
||||||
|
throw new Exception('Invalid SOCKS reponse: Invalid address type');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
422
vendor/clue/socks-react/src/Server.php
vendored
Normal file
422
vendor/clue/socks-react/src/Server.php
vendored
Normal file
@ -0,0 +1,422 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Socks;
|
||||||
|
|
||||||
|
use Evenement\EventEmitter;
|
||||||
|
use React\Socket\ServerInterface;
|
||||||
|
use React\Promise;
|
||||||
|
use React\Promise\Deferred;
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
use React\Socket\ConnectorInterface;
|
||||||
|
use React\Socket\Connector;
|
||||||
|
use React\Socket\ConnectionInterface;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use \UnexpectedValueException;
|
||||||
|
use \InvalidArgumentException;
|
||||||
|
use \Exception;
|
||||||
|
use React\Promise\Timer\TimeoutException;
|
||||||
|
|
||||||
|
class Server extends EventEmitter
|
||||||
|
{
|
||||||
|
// the following error codes are only used for SOCKS5 only
|
||||||
|
/** @internal */
|
||||||
|
const ERROR_GENERAL = 0x01;
|
||||||
|
/** @internal */
|
||||||
|
const ERROR_NOT_ALLOWED_BY_RULESET = 0x02;
|
||||||
|
/** @internal */
|
||||||
|
const ERROR_NETWORK_UNREACHABLE = 0x03;
|
||||||
|
/** @internal */
|
||||||
|
const ERROR_HOST_UNREACHABLE = 0x04;
|
||||||
|
/** @internal */
|
||||||
|
const ERROR_CONNECTION_REFUSED = 0x05;
|
||||||
|
/** @internal */
|
||||||
|
const ERROR_TTL = 0x06;
|
||||||
|
/** @internal */
|
||||||
|
const ERROR_COMMAND_UNSUPPORTED = 0x07;
|
||||||
|
/** @internal */
|
||||||
|
const ERROR_ADDRESS_UNSUPPORTED = 0x08;
|
||||||
|
|
||||||
|
protected $loop;
|
||||||
|
|
||||||
|
private $connector;
|
||||||
|
|
||||||
|
private $auth = null;
|
||||||
|
|
||||||
|
private $protocolVersion = null;
|
||||||
|
|
||||||
|
public function __construct(LoopInterface $loop, ServerInterface $serverInterface, ConnectorInterface $connector = null)
|
||||||
|
{
|
||||||
|
if ($connector === null) {
|
||||||
|
$connector = new Connector($loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->loop = $loop;
|
||||||
|
$this->connector = $connector;
|
||||||
|
|
||||||
|
$that = $this;
|
||||||
|
$serverInterface->on('connection', function ($connection) use ($that) {
|
||||||
|
$that->emit('connection', array($connection));
|
||||||
|
$that->onConnection($connection);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setProtocolVersion($version)
|
||||||
|
{
|
||||||
|
if ($version !== null) {
|
||||||
|
$version = (string)$version;
|
||||||
|
if (!in_array($version, array('4', '4a', '5'), true)) {
|
||||||
|
throw new InvalidArgumentException('Invalid protocol version given');
|
||||||
|
}
|
||||||
|
if ($version !== '5' && $this->auth !== null){
|
||||||
|
throw new UnexpectedValueException('Unable to change protocol version to anything but SOCKS5 while authentication is used. Consider removing authentication info or sticking to SOCKS5');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->protocolVersion = $version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAuth($auth)
|
||||||
|
{
|
||||||
|
if (!is_callable($auth)) {
|
||||||
|
throw new InvalidArgumentException('Given authenticator is not a valid callable');
|
||||||
|
}
|
||||||
|
if ($this->protocolVersion !== null && $this->protocolVersion !== '5') {
|
||||||
|
throw new UnexpectedValueException('Authentication requires SOCKS5. Consider using protocol version 5 or waive authentication');
|
||||||
|
}
|
||||||
|
// wrap authentication callback in order to cast its return value to a promise
|
||||||
|
$this->auth = function($username, $password, $remote) use ($auth) {
|
||||||
|
$ret = call_user_func($auth, $username, $password, $remote);
|
||||||
|
if ($ret instanceof PromiseInterface) {
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
$deferred = new Deferred();
|
||||||
|
$ret ? $deferred->resolve() : $deferred->reject();
|
||||||
|
return $deferred->promise();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAuthArray(array $login)
|
||||||
|
{
|
||||||
|
$this->setAuth(function ($username, $password) use ($login) {
|
||||||
|
return (isset($login[$username]) && (string)$login[$username] === $password);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unsetAuth()
|
||||||
|
{
|
||||||
|
$this->auth = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onConnection(ConnectionInterface $connection)
|
||||||
|
{
|
||||||
|
$that = $this;
|
||||||
|
$handling = $this->handleSocks($connection)->then(function($remote) use ($connection){
|
||||||
|
$connection->emit('ready',array($remote));
|
||||||
|
}, function ($error) use ($connection, $that) {
|
||||||
|
if (!($error instanceof \Exception)) {
|
||||||
|
$error = new \Exception($error);
|
||||||
|
}
|
||||||
|
$connection->emit('error', array($error));
|
||||||
|
$that->endConnection($connection);
|
||||||
|
});
|
||||||
|
|
||||||
|
$connection->on('close', function () use ($handling) {
|
||||||
|
$handling->cancel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gracefully shutdown connection by flushing all remaining data and closing stream
|
||||||
|
*/
|
||||||
|
public function endConnection(ConnectionInterface $stream)
|
||||||
|
{
|
||||||
|
$tid = true;
|
||||||
|
$loop = $this->loop;
|
||||||
|
|
||||||
|
// cancel below timer in case connection is closed in time
|
||||||
|
$stream->once('close', function () use (&$tid, $loop) {
|
||||||
|
// close event called before the timer was set up, so everything is okay
|
||||||
|
if ($tid === true) {
|
||||||
|
// make sure to not start a useless timer
|
||||||
|
$tid = false;
|
||||||
|
} else {
|
||||||
|
$loop->cancelTimer($tid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// shut down connection by pausing input data, flushing outgoing buffer and then exit
|
||||||
|
$stream->pause();
|
||||||
|
$stream->end();
|
||||||
|
|
||||||
|
// check if connection is not already closed
|
||||||
|
if ($tid === true) {
|
||||||
|
// fall back to forcefully close connection in 3 seconds if buffer can not be flushed
|
||||||
|
$tid = $loop->addTimer(3.0, array($stream,'close'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleSocks(ConnectionInterface $stream)
|
||||||
|
{
|
||||||
|
$reader = new StreamReader();
|
||||||
|
$stream->on('data', array($reader, 'write'));
|
||||||
|
|
||||||
|
$that = $this;
|
||||||
|
$that = $this;
|
||||||
|
|
||||||
|
$auth = $this->auth;
|
||||||
|
$protocolVersion = $this->protocolVersion;
|
||||||
|
|
||||||
|
// authentication requires SOCKS5
|
||||||
|
if ($auth !== null) {
|
||||||
|
$protocolVersion = '5';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $reader->readByte()->then(function ($version) use ($stream, $that, $protocolVersion, $auth, $reader){
|
||||||
|
if ($version === 0x04) {
|
||||||
|
if ($protocolVersion === '5') {
|
||||||
|
throw new UnexpectedValueException('SOCKS4 not allowed due to configuration');
|
||||||
|
}
|
||||||
|
return $that->handleSocks4($stream, $protocolVersion, $reader);
|
||||||
|
} else if ($version === 0x05) {
|
||||||
|
if ($protocolVersion !== null && $protocolVersion !== '5') {
|
||||||
|
throw new UnexpectedValueException('SOCKS5 not allowed due to configuration');
|
||||||
|
}
|
||||||
|
return $that->handleSocks5($stream, $auth, $reader);
|
||||||
|
}
|
||||||
|
throw new UnexpectedValueException('Unexpected/unknown version number');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleSocks4(ConnectionInterface $stream, $protocolVersion, StreamReader $reader)
|
||||||
|
{
|
||||||
|
// suppliying hostnames is only allowed for SOCKS4a (or automatically detected version)
|
||||||
|
$supportsHostname = ($protocolVersion === null || $protocolVersion === '4a');
|
||||||
|
|
||||||
|
$remote = $stream->getRemoteAddress();
|
||||||
|
if ($remote !== null) {
|
||||||
|
// remove transport scheme and prefix socks4:// instead
|
||||||
|
$secure = strpos($remote, 'tls://') === 0;
|
||||||
|
if (($pos = strpos($remote, '://')) !== false) {
|
||||||
|
$remote = substr($remote, $pos + 3);
|
||||||
|
}
|
||||||
|
$remote = 'socks4' . ($secure ? 's' : '') . '://' . $remote;
|
||||||
|
}
|
||||||
|
|
||||||
|
$that = $this;
|
||||||
|
return $reader->readByteAssert(0x01)->then(function () use ($reader) {
|
||||||
|
return $reader->readBinary(array(
|
||||||
|
'port' => 'n',
|
||||||
|
'ipLong' => 'N',
|
||||||
|
'null' => 'C'
|
||||||
|
));
|
||||||
|
})->then(function ($data) use ($reader, $supportsHostname, $remote) {
|
||||||
|
if ($data['null'] !== 0x00) {
|
||||||
|
throw new Exception('Not a null byte');
|
||||||
|
}
|
||||||
|
if ($data['ipLong'] === 0) {
|
||||||
|
throw new Exception('Invalid IP');
|
||||||
|
}
|
||||||
|
if ($data['port'] === 0) {
|
||||||
|
throw new Exception('Invalid port');
|
||||||
|
}
|
||||||
|
if ($data['ipLong'] < 256 && $supportsHostname) {
|
||||||
|
// invalid IP => probably a SOCKS4a request which appends the hostname
|
||||||
|
return $reader->readStringNull()->then(function ($string) use ($data, $remote){
|
||||||
|
return array($string, $data['port'], $remote);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$ip = long2ip($data['ipLong']);
|
||||||
|
return array($ip, $data['port'], $remote);
|
||||||
|
}
|
||||||
|
})->then(function ($target) use ($stream, $that) {
|
||||||
|
return $that->connectTarget($stream, $target)->then(function (ConnectionInterface $remote) use ($stream){
|
||||||
|
$stream->write(pack('C8', 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
|
||||||
|
|
||||||
|
return $remote;
|
||||||
|
}, function($error) use ($stream){
|
||||||
|
$stream->end(pack('C8', 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
|
||||||
|
|
||||||
|
throw $error;
|
||||||
|
});
|
||||||
|
}, function($error) {
|
||||||
|
throw new UnexpectedValueException('SOCKS4 protocol error',0,$error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleSocks5(ConnectionInterface $stream, $auth=null, StreamReader $reader)
|
||||||
|
{
|
||||||
|
$remote = $stream->getRemoteAddress();
|
||||||
|
if ($remote !== null) {
|
||||||
|
// remove transport scheme and prefix socks5:// instead
|
||||||
|
$secure = strpos($remote, 'tls://') === 0;
|
||||||
|
if (($pos = strpos($remote, '://')) !== false) {
|
||||||
|
$remote = substr($remote, $pos + 3);
|
||||||
|
}
|
||||||
|
$remote = 'socks5' . ($secure ? 's' : '') . '://' . $remote;
|
||||||
|
}
|
||||||
|
|
||||||
|
$that = $this;
|
||||||
|
return $reader->readByte()->then(function ($num) use ($reader) {
|
||||||
|
// $num different authentication mechanisms offered
|
||||||
|
return $reader->readLength($num);
|
||||||
|
})->then(function ($methods) use ($reader, $stream, $auth, &$remote) {
|
||||||
|
if ($auth === null && strpos($methods,"\x00") !== false) {
|
||||||
|
// accept "no authentication"
|
||||||
|
$stream->write(pack('C2', 0x05, 0x00));
|
||||||
|
|
||||||
|
return 0x00;
|
||||||
|
} else if ($auth !== null && strpos($methods,"\x02") !== false) {
|
||||||
|
// username/password authentication (RFC 1929) sub negotiation
|
||||||
|
$stream->write(pack('C2', 0x05, 0x02));
|
||||||
|
return $reader->readByteAssert(0x01)->then(function () use ($reader) {
|
||||||
|
return $reader->readByte();
|
||||||
|
})->then(function ($length) use ($reader) {
|
||||||
|
return $reader->readLength($length);
|
||||||
|
})->then(function ($username) use ($reader, $auth, $stream, &$remote) {
|
||||||
|
return $reader->readByte()->then(function ($length) use ($reader) {
|
||||||
|
return $reader->readLength($length);
|
||||||
|
})->then(function ($password) use ($username, $auth, $stream, &$remote) {
|
||||||
|
// username and password given => authenticate
|
||||||
|
|
||||||
|
// prefix username/password to remote URI
|
||||||
|
if ($remote !== null) {
|
||||||
|
$remote = str_replace('://', '://' . rawurlencode($username) . ':' . rawurlencode($password) . '@', $remote);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $auth($username, $password, $remote)->then(function () use ($stream, $username) {
|
||||||
|
// accept
|
||||||
|
$stream->emit('auth', array($username));
|
||||||
|
$stream->write(pack('C2', 0x01, 0x00));
|
||||||
|
}, function() use ($stream) {
|
||||||
|
// reject => send any code but 0x00
|
||||||
|
$stream->end(pack('C2', 0x01, 0xFF));
|
||||||
|
throw new UnexpectedValueException('Unable to authenticate');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// reject all offered authentication methods
|
||||||
|
$stream->write(pack('C2', 0x05, 0xFF));
|
||||||
|
throw new UnexpectedValueException('No acceptable authentication mechanism found');
|
||||||
|
}
|
||||||
|
})->then(function ($method) use ($reader, $stream) {
|
||||||
|
return $reader->readBinary(array(
|
||||||
|
'version' => 'C',
|
||||||
|
'command' => 'C',
|
||||||
|
'null' => 'C',
|
||||||
|
'type' => 'C'
|
||||||
|
));
|
||||||
|
})->then(function ($data) use ($reader) {
|
||||||
|
if ($data['version'] !== 0x05) {
|
||||||
|
throw new UnexpectedValueException('Invalid SOCKS version');
|
||||||
|
}
|
||||||
|
if ($data['command'] !== 0x01) {
|
||||||
|
throw new UnexpectedValueException('Only CONNECT requests supported', Server::ERROR_COMMAND_UNSUPPORTED);
|
||||||
|
}
|
||||||
|
// if ($data['null'] !== 0x00) {
|
||||||
|
// throw new UnexpectedValueException('Reserved byte has to be NULL');
|
||||||
|
// }
|
||||||
|
if ($data['type'] === 0x03) {
|
||||||
|
// target hostname string
|
||||||
|
return $reader->readByte()->then(function ($len) use ($reader) {
|
||||||
|
return $reader->readLength($len);
|
||||||
|
});
|
||||||
|
} else if ($data['type'] === 0x01) {
|
||||||
|
// target IPv4
|
||||||
|
return $reader->readLength(4)->then(function ($addr) {
|
||||||
|
return inet_ntop($addr);
|
||||||
|
});
|
||||||
|
} else if ($data['type'] === 0x04) {
|
||||||
|
// target IPv6
|
||||||
|
return $reader->readLength(16)->then(function ($addr) {
|
||||||
|
return inet_ntop($addr);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new UnexpectedValueException('Invalid address type', Server::ERROR_ADDRESS_UNSUPPORTED);
|
||||||
|
}
|
||||||
|
})->then(function ($host) use ($reader, &$remote) {
|
||||||
|
return $reader->readBinary(array('port'=>'n'))->then(function ($data) use ($host, &$remote) {
|
||||||
|
return array($host, $data['port'], $remote);
|
||||||
|
});
|
||||||
|
})->then(function ($target) use ($that, $stream) {
|
||||||
|
return $that->connectTarget($stream, $target);
|
||||||
|
}, function($error) use ($stream) {
|
||||||
|
throw new UnexpectedValueException('SOCKS5 protocol error', $error->getCode(), $error);
|
||||||
|
})->then(function (ConnectionInterface $remote) use ($stream) {
|
||||||
|
$stream->write(pack('C4Nn', 0x05, 0x00, 0x00, 0x01, 0, 0));
|
||||||
|
|
||||||
|
return $remote;
|
||||||
|
}, function(Exception $error) use ($stream){
|
||||||
|
$stream->write(pack('C4Nn', 0x05, $error->getCode() === 0 ? Server::ERROR_GENERAL : $error->getCode(), 0x00, 0x01, 0, 0));
|
||||||
|
|
||||||
|
throw $error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connectTarget(ConnectionInterface $stream, array $target)
|
||||||
|
{
|
||||||
|
$uri = $target[0];
|
||||||
|
if (strpos($uri, ':') !== false) {
|
||||||
|
$uri = '[' . $uri . ']';
|
||||||
|
}
|
||||||
|
$uri .= ':' . $target[1];
|
||||||
|
|
||||||
|
// validate URI so a string hostname can not pass excessive URI parts
|
||||||
|
$parts = parse_url('tcp://' . $uri);
|
||||||
|
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || count($parts) !== 3) {
|
||||||
|
return Promise\reject(new InvalidArgumentException('Invalid target URI given'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($target[2])) {
|
||||||
|
$uri .= '?source=' . rawurlencode($target[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stream->emit('target', $target);
|
||||||
|
$that = $this;
|
||||||
|
$connecting = $this->connector->connect($uri);
|
||||||
|
|
||||||
|
$stream->on('close', function () use ($connecting) {
|
||||||
|
$connecting->cancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
return $connecting->then(function (ConnectionInterface $remote) use ($stream, $that) {
|
||||||
|
$stream->pipe($remote, array('end'=>false));
|
||||||
|
$remote->pipe($stream, array('end'=>false));
|
||||||
|
|
||||||
|
// remote end closes connection => stop reading from local end, try to flush buffer to local and disconnect local
|
||||||
|
$remote->on('end', function() use ($stream, $that) {
|
||||||
|
$stream->emit('shutdown', array('remote', null));
|
||||||
|
$that->endConnection($stream);
|
||||||
|
});
|
||||||
|
|
||||||
|
// local end closes connection => stop reading from remote end, try to flush buffer to remote and disconnect remote
|
||||||
|
$stream->on('end', function() use ($remote, $that) {
|
||||||
|
$that->endConnection($remote);
|
||||||
|
});
|
||||||
|
|
||||||
|
// set bigger buffer size of 100k to improve performance
|
||||||
|
$stream->bufferSize = $remote->bufferSize = 100 * 1024 * 1024;
|
||||||
|
|
||||||
|
return $remote;
|
||||||
|
}, function(Exception $error) {
|
||||||
|
// default to general/unknown error
|
||||||
|
$code = Server::ERROR_GENERAL;
|
||||||
|
|
||||||
|
// map common socket error conditions to limited list of SOCKS error codes
|
||||||
|
if ((defined('SOCKET_EACCES') && $error->getCode() === SOCKET_EACCES) || $error->getCode() === 13) {
|
||||||
|
$code = Server::ERROR_NOT_ALLOWED_BY_RULESET;
|
||||||
|
} elseif ((defined('SOCKET_EHOSTUNREACH') && $error->getCode() === SOCKET_EHOSTUNREACH) || $error->getCode() === 113) {
|
||||||
|
$code = Server::ERROR_HOST_UNREACHABLE;
|
||||||
|
} elseif ((defined('SOCKET_ENETUNREACH') && $error->getCode() === SOCKET_ENETUNREACH) || $error->getCode() === 101) {
|
||||||
|
$code = Server::ERROR_NETWORK_UNREACHABLE;
|
||||||
|
} elseif ((defined('SOCKET_ECONNREFUSED') && $error->getCode() === SOCKET_ECONNREFUSED) || $error->getCode() === 111 || $error->getMessage() === 'Connection refused') {
|
||||||
|
// Socket component does not currently assign an error code for this, so we have to resort to checking the exception message
|
||||||
|
$code = Server::ERROR_CONNECTION_REFUSED;
|
||||||
|
} elseif ((defined('SOCKET_ETIMEDOUT') && $error->getCode() === SOCKET_ETIMEDOUT) || $error->getCode() === 110 || $error instanceof TimeoutException) {
|
||||||
|
// Socket component does not currently assign an error code for this, but we can rely on the TimeoutException
|
||||||
|
$code = Server::ERROR_TTL;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnexpectedValueException('Unable to connect to remote target', $code, $error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
149
vendor/clue/socks-react/src/StreamReader.php
vendored
Normal file
149
vendor/clue/socks-react/src/StreamReader.php
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Clue\React\Socks;
|
||||||
|
|
||||||
|
use React\Promise\Deferred;
|
||||||
|
use \InvalidArgumentException;
|
||||||
|
use \UnexpectedValueException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class StreamReader
|
||||||
|
{
|
||||||
|
const RET_DONE = true;
|
||||||
|
const RET_INCOMPLETE = null;
|
||||||
|
|
||||||
|
private $buffer = '';
|
||||||
|
private $queue = array();
|
||||||
|
|
||||||
|
public function write($data)
|
||||||
|
{
|
||||||
|
$this->buffer .= $data;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$current = reset($this->queue);
|
||||||
|
|
||||||
|
if ($current === false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @var $current Closure */
|
||||||
|
|
||||||
|
$ret = $current($this->buffer);
|
||||||
|
|
||||||
|
if ($ret === self::RET_INCOMPLETE) {
|
||||||
|
// current is incomplete, so wait for further data to arrive
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// current is done, remove from list and continue with next
|
||||||
|
array_shift($this->queue);
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readBinary($structure)
|
||||||
|
{
|
||||||
|
$length = 0;
|
||||||
|
$unpack = '';
|
||||||
|
foreach ($structure as $name=>$format) {
|
||||||
|
if ($length !== 0) {
|
||||||
|
$unpack .= '/';
|
||||||
|
}
|
||||||
|
$unpack .= $format . $name;
|
||||||
|
|
||||||
|
if ($format === 'C') {
|
||||||
|
++$length;
|
||||||
|
} else if ($format === 'n') {
|
||||||
|
$length += 2;
|
||||||
|
} else if ($format === 'N') {
|
||||||
|
$length += 4;
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException('Invalid format given');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->readLength($length)->then(function ($response) use ($unpack) {
|
||||||
|
return unpack($unpack, $response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readLength($bytes)
|
||||||
|
{
|
||||||
|
$deferred = new Deferred();
|
||||||
|
|
||||||
|
$this->readBufferCallback(function (&$buffer) use ($bytes, $deferred) {
|
||||||
|
if (strlen($buffer) >= $bytes) {
|
||||||
|
$deferred->resolve((string)substr($buffer, 0, $bytes));
|
||||||
|
$buffer = (string)substr($buffer, $bytes);
|
||||||
|
|
||||||
|
return StreamReader::RET_DONE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $deferred->promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readByte()
|
||||||
|
{
|
||||||
|
return $this->readBinary(array(
|
||||||
|
'byte' => 'C'
|
||||||
|
))->then(function ($data) {
|
||||||
|
return $data['byte'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readByteAssert($expect)
|
||||||
|
{
|
||||||
|
return $this->readByte()->then(function ($byte) use ($expect) {
|
||||||
|
if ($byte !== $expect) {
|
||||||
|
throw new UnexpectedValueException('Unexpected byte encountered');
|
||||||
|
}
|
||||||
|
return $byte;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readStringNull()
|
||||||
|
{
|
||||||
|
$deferred = new Deferred();
|
||||||
|
$string = '';
|
||||||
|
|
||||||
|
$that = $this;
|
||||||
|
$readOne = function () use (&$readOne, $that, $deferred, &$string) {
|
||||||
|
$that->readByte()->then(function ($byte) use ($deferred, &$string, $readOne) {
|
||||||
|
if ($byte === 0x00) {
|
||||||
|
$deferred->resolve($string);
|
||||||
|
} else {
|
||||||
|
$string .= chr($byte);
|
||||||
|
$readOne();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$readOne();
|
||||||
|
|
||||||
|
return $deferred->promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readBufferCallback(/* callable */ $callable)
|
||||||
|
{
|
||||||
|
if (!is_callable($callable)) {
|
||||||
|
throw new InvalidArgumentException('Given function must be callable');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->queue) {
|
||||||
|
$this->queue []= $callable;
|
||||||
|
} else {
|
||||||
|
$this->queue = array($callable);
|
||||||
|
|
||||||
|
if ($this->buffer !== '') {
|
||||||
|
// this is the first element in the queue and the buffer is filled => trigger write procedure
|
||||||
|
$this->write('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBuffer()
|
||||||
|
{
|
||||||
|
return $this->buffer;
|
||||||
|
}
|
||||||
|
}
|
445
vendor/composer/ClassLoader.php
vendored
Normal file
445
vendor/composer/ClassLoader.php
vendored
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see http://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see http://www.php-fig.org/psr/psr-4/
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
// PSR-4
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
private $useIncludePath = false;
|
||||||
|
private $classMap = array();
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
private $missingClasses = array();
|
||||||
|
private $apcuPrefix;
|
||||||
|
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $classMap Class to filename map
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param array|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param array|string $paths The PSR-4 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param array|string $paths The PSR-0 base directories
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param array|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||||
|
*
|
||||||
|
* @param string|null $apcuPrefix
|
||||||
|
*/
|
||||||
|
public function setApcuPrefix($apcuPrefix)
|
||||||
|
{
|
||||||
|
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getApcuPrefix()
|
||||||
|
{
|
||||||
|
return $this->apcuPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return bool|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||||
|
if ($hit) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if (false === $file && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
apcu_add($this->apcuPrefix.$class, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $file) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
$this->missingClasses[$class] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
$subPath = $class;
|
||||||
|
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||||
|
$subPath = substr($subPath, 0, $lastPos);
|
||||||
|
$search = $subPath.'\\';
|
||||||
|
if (isset($this->prefixDirsPsr4[$search])) {
|
||||||
|
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||||
|
$length = $this->prefixLengthsPsr4[$first][$search];
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*/
|
||||||
|
function includeFile($file)
|
||||||
|
{
|
||||||
|
include $file;
|
||||||
|
}
|
21
vendor/composer/LICENSE
vendored
Normal file
21
vendor/composer/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
9
vendor/composer/autoload_classmap.php
vendored
Normal file
9
vendor/composer/autoload_classmap.php
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
14
vendor/composer/autoload_files.php
vendored
Normal file
14
vendor/composer/autoload_files.php
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_files.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'ad155f8f1cf0d418fe49e248db8c661b' => $vendorDir . '/react/promise/src/functions_include.php',
|
||||||
|
'6b06ce8ccf69c43a60a1e48495a034c9' => $vendorDir . '/react/promise-timer/src/functions.php',
|
||||||
|
'd97a92f16d9d46b74cbec4132395f0a2' => $vendorDir . '/clue/block-react/src/functions.php',
|
||||||
|
'ebf8799635f67b5d7248946fe2154f4a' => $vendorDir . '/ringcentral/psr7/src/functions_include.php',
|
||||||
|
'cea474b4340aa9fa53661e887a21a316' => $vendorDir . '/react/promise-stream/src/functions_include.php',
|
||||||
|
);
|
12
vendor/composer/autoload_namespaces.php
vendored
Normal file
12
vendor/composer/autoload_namespaces.php
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Resque' => array($vendorDir . '/chrisboulton/php-resque/lib'),
|
||||||
|
'Evenement' => array($vendorDir . '/evenement/evenement/src'),
|
||||||
|
'Clue\\Redis\\Protocol' => array($vendorDir . '/clue/redis-protocol/src'),
|
||||||
|
);
|
32
vendor/composer/autoload_psr4.php
vendored
Normal file
32
vendor/composer/autoload_psr4.php
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Socket\\Raw\\' => array($vendorDir . '/clue/socket-raw/src'),
|
||||||
|
'RingCentral\\Psr7\\' => array($vendorDir . '/ringcentral/psr7/src'),
|
||||||
|
'React\\Stream\\' => array($vendorDir . '/react/stream/src'),
|
||||||
|
'React\\Socket\\' => array($vendorDir . '/react/socket/src'),
|
||||||
|
'React\\Promise\\Timer\\' => array($vendorDir . '/react/promise-timer/src'),
|
||||||
|
'React\\Promise\\Stream\\' => array($vendorDir . '/react/promise-stream/src'),
|
||||||
|
'React\\Promise\\' => array($vendorDir . '/react/promise/src'),
|
||||||
|
'React\\Http\\' => array($vendorDir . '/react/http/src'),
|
||||||
|
'React\\HttpClient\\' => array($vendorDir . '/react/http-client/src'),
|
||||||
|
'React\\EventLoop\\' => array($vendorDir . '/react/event-loop/src'),
|
||||||
|
'React\\Dns\\' => array($vendorDir . '/react/dns/src'),
|
||||||
|
'React\\Datagram\\' => array($vendorDir . '/react/datagram/src'),
|
||||||
|
'React\\ChildProcess\\' => array($vendorDir . '/react/child-process/src'),
|
||||||
|
'React\\Cache\\' => array($vendorDir . '/react/cache/src'),
|
||||||
|
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
||||||
|
'Predis\\' => array($vendorDir . '/predis/predis/src'),
|
||||||
|
'ConnectionManager\\Extra\\' => array($vendorDir . '/clue/connection-manager-extra/src'),
|
||||||
|
'Clue\\React\\Socks\\' => array($vendorDir . '/clue/socks-react/src'),
|
||||||
|
'Clue\\React\\Soap\\' => array($vendorDir . '/clue/soap-react/src'),
|
||||||
|
'Clue\\React\\Redis\\' => array($vendorDir . '/clue/redis-react/src'),
|
||||||
|
'Clue\\React\\Mq\\' => array($vendorDir . '/clue/mq-react/src'),
|
||||||
|
'Clue\\React\\HttpProxy\\' => array($vendorDir . '/clue/http-proxy-react/src'),
|
||||||
|
'Clue\\React\\Buzz\\' => array($vendorDir . '/clue/buzz-react/src'),
|
||||||
|
);
|
70
vendor/composer/autoload_real.php
vendored
Normal file
70
vendor/composer/autoload_real.php
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
|
class ComposerAutoloaderInit3150568d47d14b39925e07c8c904a490
|
||||||
|
{
|
||||||
|
private static $loader;
|
||||||
|
|
||||||
|
public static function loadClassLoader($class)
|
||||||
|
{
|
||||||
|
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||||
|
require __DIR__ . '/ClassLoader.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
if (null !== self::$loader) {
|
||||||
|
return self::$loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInit3150568d47d14b39925e07c8c904a490', 'loadClassLoader'), true, true);
|
||||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
spl_autoload_unregister(array('ComposerAutoloaderInit3150568d47d14b39925e07c8c904a490', 'loadClassLoader'));
|
||||||
|
|
||||||
|
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||||
|
if ($useStaticLoader) {
|
||||||
|
require_once __DIR__ . '/autoload_static.php';
|
||||||
|
|
||||||
|
call_user_func(\Composer\Autoload\ComposerStaticInit3150568d47d14b39925e07c8c904a490::getInitializer($loader));
|
||||||
|
} else {
|
||||||
|
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||||
|
foreach ($map as $namespace => $path) {
|
||||||
|
$loader->set($namespace, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$map = require __DIR__ . '/autoload_psr4.php';
|
||||||
|
foreach ($map as $namespace => $path) {
|
||||||
|
$loader->setPsr4($namespace, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||||
|
if ($classMap) {
|
||||||
|
$loader->addClassMap($classMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$loader->register(true);
|
||||||
|
|
||||||
|
if ($useStaticLoader) {
|
||||||
|
$includeFiles = Composer\Autoload\ComposerStaticInit3150568d47d14b39925e07c8c904a490::$files;
|
||||||
|
} else {
|
||||||
|
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||||
|
}
|
||||||
|
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||||
|
composerRequire3150568d47d14b39925e07c8c904a490($fileIdentifier, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function composerRequire3150568d47d14b39925e07c8c904a490($fileIdentifier, $file)
|
||||||
|
{
|
||||||
|
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||||
|
}
|
||||||
|
}
|
183
vendor/composer/autoload_static.php
vendored
Normal file
183
vendor/composer/autoload_static.php
vendored
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_static.php @generated by Composer
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
class ComposerStaticInit3150568d47d14b39925e07c8c904a490
|
||||||
|
{
|
||||||
|
public static $files = array (
|
||||||
|
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
|
||||||
|
'6b06ce8ccf69c43a60a1e48495a034c9' => __DIR__ . '/..' . '/react/promise-timer/src/functions.php',
|
||||||
|
'd97a92f16d9d46b74cbec4132395f0a2' => __DIR__ . '/..' . '/clue/block-react/src/functions.php',
|
||||||
|
'ebf8799635f67b5d7248946fe2154f4a' => __DIR__ . '/..' . '/ringcentral/psr7/src/functions_include.php',
|
||||||
|
'cea474b4340aa9fa53661e887a21a316' => __DIR__ . '/..' . '/react/promise-stream/src/functions_include.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixLengthsPsr4 = array (
|
||||||
|
'S' =>
|
||||||
|
array (
|
||||||
|
'Socket\\Raw\\' => 11,
|
||||||
|
),
|
||||||
|
'R' =>
|
||||||
|
array (
|
||||||
|
'RingCentral\\Psr7\\' => 17,
|
||||||
|
'React\\Stream\\' => 13,
|
||||||
|
'React\\Socket\\' => 13,
|
||||||
|
'React\\Promise\\Timer\\' => 20,
|
||||||
|
'React\\Promise\\Stream\\' => 21,
|
||||||
|
'React\\Promise\\' => 14,
|
||||||
|
'React\\Http\\' => 11,
|
||||||
|
'React\\HttpClient\\' => 17,
|
||||||
|
'React\\EventLoop\\' => 16,
|
||||||
|
'React\\Dns\\' => 10,
|
||||||
|
'React\\Datagram\\' => 15,
|
||||||
|
'React\\ChildProcess\\' => 19,
|
||||||
|
'React\\Cache\\' => 12,
|
||||||
|
),
|
||||||
|
'P' =>
|
||||||
|
array (
|
||||||
|
'Psr\\Http\\Message\\' => 17,
|
||||||
|
'Predis\\' => 7,
|
||||||
|
),
|
||||||
|
'C' =>
|
||||||
|
array (
|
||||||
|
'ConnectionManager\\Extra\\' => 24,
|
||||||
|
'Clue\\React\\Socks\\' => 17,
|
||||||
|
'Clue\\React\\Soap\\' => 16,
|
||||||
|
'Clue\\React\\Redis\\' => 17,
|
||||||
|
'Clue\\React\\Mq\\' => 14,
|
||||||
|
'Clue\\React\\HttpProxy\\' => 21,
|
||||||
|
'Clue\\React\\Buzz\\' => 16,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixDirsPsr4 = array (
|
||||||
|
'Socket\\Raw\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/clue/socket-raw/src',
|
||||||
|
),
|
||||||
|
'RingCentral\\Psr7\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/ringcentral/psr7/src',
|
||||||
|
),
|
||||||
|
'React\\Stream\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/stream/src',
|
||||||
|
),
|
||||||
|
'React\\Socket\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/socket/src',
|
||||||
|
),
|
||||||
|
'React\\Promise\\Timer\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/promise-timer/src',
|
||||||
|
),
|
||||||
|
'React\\Promise\\Stream\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/promise-stream/src',
|
||||||
|
),
|
||||||
|
'React\\Promise\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/promise/src',
|
||||||
|
),
|
||||||
|
'React\\Http\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/http/src',
|
||||||
|
),
|
||||||
|
'React\\HttpClient\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/http-client/src',
|
||||||
|
),
|
||||||
|
'React\\EventLoop\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/event-loop/src',
|
||||||
|
),
|
||||||
|
'React\\Dns\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/dns/src',
|
||||||
|
),
|
||||||
|
'React\\Datagram\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/datagram/src',
|
||||||
|
),
|
||||||
|
'React\\ChildProcess\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/child-process/src',
|
||||||
|
),
|
||||||
|
'React\\Cache\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/cache/src',
|
||||||
|
),
|
||||||
|
'Psr\\Http\\Message\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/psr/http-message/src',
|
||||||
|
),
|
||||||
|
'Predis\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/predis/predis/src',
|
||||||
|
),
|
||||||
|
'ConnectionManager\\Extra\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/clue/connection-manager-extra/src',
|
||||||
|
),
|
||||||
|
'Clue\\React\\Socks\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/clue/socks-react/src',
|
||||||
|
),
|
||||||
|
'Clue\\React\\Soap\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/clue/soap-react/src',
|
||||||
|
),
|
||||||
|
'Clue\\React\\Redis\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/clue/redis-react/src',
|
||||||
|
),
|
||||||
|
'Clue\\React\\Mq\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/clue/mq-react/src',
|
||||||
|
),
|
||||||
|
'Clue\\React\\HttpProxy\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/clue/http-proxy-react/src',
|
||||||
|
),
|
||||||
|
'Clue\\React\\Buzz\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/clue/buzz-react/src',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixesPsr0 = array (
|
||||||
|
'R' =>
|
||||||
|
array (
|
||||||
|
'Resque' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/chrisboulton/php-resque/lib',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'E' =>
|
||||||
|
array (
|
||||||
|
'Evenement' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/evenement/evenement/src',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'C' =>
|
||||||
|
array (
|
||||||
|
'Clue\\Redis\\Protocol' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/clue/redis-protocol/src',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static function getInitializer(ClassLoader $loader)
|
||||||
|
{
|
||||||
|
return \Closure::bind(function () use ($loader) {
|
||||||
|
$loader->prefixLengthsPsr4 = ComposerStaticInit3150568d47d14b39925e07c8c904a490::$prefixLengthsPsr4;
|
||||||
|
$loader->prefixDirsPsr4 = ComposerStaticInit3150568d47d14b39925e07c8c904a490::$prefixDirsPsr4;
|
||||||
|
$loader->prefixesPsr0 = ComposerStaticInit3150568d47d14b39925e07c8c904a490::$prefixesPsr0;
|
||||||
|
|
||||||
|
}, null, ClassLoader::class);
|
||||||
|
}
|
||||||
|
}
|
19
vendor/evenement/evenement/LICENSE
vendored
Normal file
19
vendor/evenement/evenement/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2011 Igor Wiedler
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
17
vendor/evenement/evenement/src/Evenement/EventEmitter.php
vendored
Normal file
17
vendor/evenement/evenement/src/Evenement/EventEmitter.php
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Evenement.
|
||||||
|
*
|
||||||
|
* (c) Igor Wiedler <igor@wiedler.ch>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Evenement;
|
||||||
|
|
||||||
|
class EventEmitter implements EventEmitterInterface
|
||||||
|
{
|
||||||
|
use EventEmitterTrait;
|
||||||
|
}
|
22
vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php
vendored
Normal file
22
vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Evenement.
|
||||||
|
*
|
||||||
|
* (c) Igor Wiedler <igor@wiedler.ch>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Evenement;
|
||||||
|
|
||||||
|
interface EventEmitterInterface
|
||||||
|
{
|
||||||
|
public function on($event, callable $listener);
|
||||||
|
public function once($event, callable $listener);
|
||||||
|
public function removeListener($event, callable $listener);
|
||||||
|
public function removeAllListeners($event = null);
|
||||||
|
public function listeners($event);
|
||||||
|
public function emit($event, array $arguments = []);
|
||||||
|
}
|
73
vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php
vendored
Normal file
73
vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Evenement.
|
||||||
|
*
|
||||||
|
* (c) Igor Wiedler <igor@wiedler.ch>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Evenement;
|
||||||
|
|
||||||
|
trait EventEmitterTrait
|
||||||
|
{
|
||||||
|
protected $listeners = [];
|
||||||
|
|
||||||
|
public function on($event, callable $listener)
|
||||||
|
{
|
||||||
|
if (!isset($this->listeners[$event])) {
|
||||||
|
$this->listeners[$event] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->listeners[$event][] = $listener;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function once($event, callable $listener)
|
||||||
|
{
|
||||||
|
$onceListener = function () use (&$onceListener, $event, $listener) {
|
||||||
|
$this->removeListener($event, $onceListener);
|
||||||
|
|
||||||
|
\call_user_func_array($listener, \func_get_args());
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->on($event, $onceListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeListener($event, callable $listener)
|
||||||
|
{
|
||||||
|
if (isset($this->listeners[$event])) {
|
||||||
|
$index = \array_search($listener, $this->listeners[$event], true);
|
||||||
|
if (false !== $index) {
|
||||||
|
unset($this->listeners[$event][$index]);
|
||||||
|
if (\count($this->listeners[$event]) === 0) {
|
||||||
|
unset($this->listeners[$event]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeAllListeners($event = null)
|
||||||
|
{
|
||||||
|
if ($event !== null) {
|
||||||
|
unset($this->listeners[$event]);
|
||||||
|
} else {
|
||||||
|
$this->listeners = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listeners($event)
|
||||||
|
{
|
||||||
|
return isset($this->listeners[$event]) ? $this->listeners[$event] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function emit($event, array $arguments = [])
|
||||||
|
{
|
||||||
|
foreach ($this->listeners($event) as $listener) {
|
||||||
|
\call_user_func_array($listener, $arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
vendor/predis/predis/LICENSE
vendored
Normal file
22
vendor/predis/predis/LICENSE
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2009-2016 Daniele Alessandri
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
14
vendor/predis/predis/autoload.php
vendored
Normal file
14
vendor/predis/predis/autoload.php
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require __DIR__.'/src/Autoloader.php';
|
||||||
|
|
||||||
|
Predis\Autoloader::register();
|
62
vendor/predis/predis/src/Autoloader.php
vendored
Normal file
62
vendor/predis/predis/src/Autoloader.php
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Predis;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a lightweight PSR-0 compliant autoloader for Predis.
|
||||||
|
*
|
||||||
|
* @author Eric Naeseth <eric@thumbtack.com>
|
||||||
|
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||||
|
*/
|
||||||
|
class Autoloader
|
||||||
|
{
|
||||||
|
private $directory;
|
||||||
|
private $prefix;
|
||||||
|
private $prefixLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $baseDirectory Base directory where the source files are located.
|
||||||
|
*/
|
||||||
|
public function __construct($baseDirectory = __DIR__)
|
||||||
|
{
|
||||||
|
$this->directory = $baseDirectory;
|
||||||
|
$this->prefix = __NAMESPACE__.'\\';
|
||||||
|
$this->prefixLength = strlen($this->prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the autoloader class with the PHP SPL autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Prepend the autoloader on the stack instead of appending it.
|
||||||
|
*/
|
||||||
|
public static function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array(new self(), 'autoload'), true, $prepend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a class from a file using its fully qualified name.
|
||||||
|
*
|
||||||
|
* @param string $className Fully qualified name of a class.
|
||||||
|
*/
|
||||||
|
public function autoload($className)
|
||||||
|
{
|
||||||
|
if (0 === strpos($className, $this->prefix)) {
|
||||||
|
$parts = explode('\\', substr($className, $this->prefixLength));
|
||||||
|
$filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
|
||||||
|
|
||||||
|
if (is_file($filepath)) {
|
||||||
|
require $filepath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
547
vendor/predis/predis/src/Client.php
vendored
Normal file
547
vendor/predis/predis/src/Client.php
vendored
Normal file
@ -0,0 +1,547 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Predis;
|
||||||
|
|
||||||
|
use Predis\Command\CommandInterface;
|
||||||
|
use Predis\Command\RawCommand;
|
||||||
|
use Predis\Command\ScriptCommand;
|
||||||
|
use Predis\Configuration\Options;
|
||||||
|
use Predis\Configuration\OptionsInterface;
|
||||||
|
use Predis\Connection\AggregateConnectionInterface;
|
||||||
|
use Predis\Connection\ConnectionInterface;
|
||||||
|
use Predis\Connection\ParametersInterface;
|
||||||
|
use Predis\Monitor\Consumer as MonitorConsumer;
|
||||||
|
use Predis\Pipeline\Pipeline;
|
||||||
|
use Predis\PubSub\Consumer as PubSubConsumer;
|
||||||
|
use Predis\Response\ErrorInterface as ErrorResponseInterface;
|
||||||
|
use Predis\Response\ResponseInterface;
|
||||||
|
use Predis\Response\ServerException;
|
||||||
|
use Predis\Transaction\MultiExec as MultiExecTransaction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client class used for connecting and executing commands on Redis.
|
||||||
|
*
|
||||||
|
* This is the main high-level abstraction of Predis upon which various other
|
||||||
|
* abstractions are built. Internally it aggregates various other classes each
|
||||||
|
* one with its own responsibility and scope.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||||
|
*/
|
||||||
|
class Client implements ClientInterface, \IteratorAggregate
|
||||||
|
{
|
||||||
|
const VERSION = '1.1.1';
|
||||||
|
|
||||||
|
protected $connection;
|
||||||
|
protected $options;
|
||||||
|
private $profile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $parameters Connection parameters for one or more servers.
|
||||||
|
* @param mixed $options Options to configure some behaviours of the client.
|
||||||
|
*/
|
||||||
|
public function __construct($parameters = null, $options = null)
|
||||||
|
{
|
||||||
|
$this->options = $this->createOptions($options ?: array());
|
||||||
|
$this->connection = $this->createConnection($parameters ?: array());
|
||||||
|
$this->profile = $this->options->profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of Predis\Configuration\Options from different
|
||||||
|
* types of arguments or simply returns the passed argument if it is an
|
||||||
|
* instance of Predis\Configuration\OptionsInterface.
|
||||||
|
*
|
||||||
|
* @param mixed $options Client options.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return OptionsInterface
|
||||||
|
*/
|
||||||
|
protected function createOptions($options)
|
||||||
|
{
|
||||||
|
if (is_array($options)) {
|
||||||
|
return new Options($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($options instanceof OptionsInterface) {
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException('Invalid type for client options.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates single or aggregate connections from different types of arguments
|
||||||
|
* (string, array) or returns the passed argument if it is an instance of a
|
||||||
|
* class implementing Predis\Connection\ConnectionInterface.
|
||||||
|
*
|
||||||
|
* Accepted types for connection parameters are:
|
||||||
|
*
|
||||||
|
* - Instance of Predis\Connection\ConnectionInterface.
|
||||||
|
* - Instance of Predis\Connection\ParametersInterface.
|
||||||
|
* - Array
|
||||||
|
* - String
|
||||||
|
* - Callable
|
||||||
|
*
|
||||||
|
* @param mixed $parameters Connection parameters or connection instance.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected function createConnection($parameters)
|
||||||
|
{
|
||||||
|
if ($parameters instanceof ConnectionInterface) {
|
||||||
|
return $parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($parameters instanceof ParametersInterface || is_string($parameters)) {
|
||||||
|
return $this->options->connections->create($parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($parameters)) {
|
||||||
|
if (!isset($parameters[0])) {
|
||||||
|
return $this->options->connections->create($parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = $this->options;
|
||||||
|
|
||||||
|
if ($options->defined('aggregate')) {
|
||||||
|
$initializer = $this->getConnectionInitializerWrapper($options->aggregate);
|
||||||
|
$connection = $initializer($parameters, $options);
|
||||||
|
} elseif ($options->defined('replication')) {
|
||||||
|
$replication = $options->replication;
|
||||||
|
|
||||||
|
if ($replication instanceof AggregateConnectionInterface) {
|
||||||
|
$connection = $replication;
|
||||||
|
$options->connections->aggregate($connection, $parameters);
|
||||||
|
} else {
|
||||||
|
$initializer = $this->getConnectionInitializerWrapper($replication);
|
||||||
|
$connection = $initializer($parameters, $options);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$connection = $options->cluster;
|
||||||
|
$options->connections->aggregate($connection, $parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_callable($parameters)) {
|
||||||
|
$initializer = $this->getConnectionInitializerWrapper($parameters);
|
||||||
|
$connection = $initializer($this->options);
|
||||||
|
|
||||||
|
return $connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException('Invalid type for connection parameters.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a callable to make sure that its returned value represents a valid
|
||||||
|
* connection type.
|
||||||
|
*
|
||||||
|
* @param mixed $callable
|
||||||
|
*
|
||||||
|
* @return \Closure
|
||||||
|
*/
|
||||||
|
protected function getConnectionInitializerWrapper($callable)
|
||||||
|
{
|
||||||
|
return function () use ($callable) {
|
||||||
|
$connection = call_user_func_array($callable, func_get_args());
|
||||||
|
|
||||||
|
if (!$connection instanceof ConnectionInterface) {
|
||||||
|
throw new \UnexpectedValueException(
|
||||||
|
'The callable connection initializer returned an invalid type.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $connection;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getProfile()
|
||||||
|
{
|
||||||
|
return $this->profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getOptions()
|
||||||
|
{
|
||||||
|
return $this->options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new client instance for the specified connection ID or alias,
|
||||||
|
* only when working with an aggregate connection (cluster, replication).
|
||||||
|
* The new client instances uses the same options of the original one.
|
||||||
|
*
|
||||||
|
* @param string $connectionID Identifier of a connection.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return Client
|
||||||
|
*/
|
||||||
|
public function getClientFor($connectionID)
|
||||||
|
{
|
||||||
|
if (!$connection = $this->getConnectionById($connectionID)) {
|
||||||
|
throw new \InvalidArgumentException("Invalid connection ID: $connectionID.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new static($connection, $this->options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the underlying connection and connects to the server.
|
||||||
|
*/
|
||||||
|
public function connect()
|
||||||
|
{
|
||||||
|
$this->connection->connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the underlying connection and disconnects from the server.
|
||||||
|
*/
|
||||||
|
public function disconnect()
|
||||||
|
{
|
||||||
|
$this->connection->disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the underlying connection and disconnects from the server.
|
||||||
|
*
|
||||||
|
* This is the same as `Client::disconnect()` as it does not actually send
|
||||||
|
* the `QUIT` command to Redis, but simply closes the connection.
|
||||||
|
*/
|
||||||
|
public function quit()
|
||||||
|
{
|
||||||
|
$this->disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current state of the underlying connection.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isConnected()
|
||||||
|
{
|
||||||
|
return $this->connection->isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getConnection()
|
||||||
|
{
|
||||||
|
return $this->connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the specified connection from the aggregate connection when the
|
||||||
|
* client is in cluster or replication mode.
|
||||||
|
*
|
||||||
|
* @param string $connectionID Index or alias of the single connection.
|
||||||
|
*
|
||||||
|
* @throws NotSupportedException
|
||||||
|
*
|
||||||
|
* @return Connection\NodeConnectionInterface
|
||||||
|
*/
|
||||||
|
public function getConnectionById($connectionID)
|
||||||
|
{
|
||||||
|
if (!$this->connection instanceof AggregateConnectionInterface) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
'Retrieving connections by ID is supported only by aggregate connections.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->connection->getConnectionById($connectionID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a command without filtering its arguments, parsing the response,
|
||||||
|
* applying any prefix to keys or throwing exceptions on Redis errors even
|
||||||
|
* regardless of client options.
|
||||||
|
*
|
||||||
|
* It is possible to identify Redis error responses from normal responses
|
||||||
|
* using the second optional argument which is populated by reference.
|
||||||
|
*
|
||||||
|
* @param array $arguments Command arguments as defined by the command signature.
|
||||||
|
* @param bool $error Set to TRUE when Redis returned an error response.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function executeRaw(array $arguments, &$error = null)
|
||||||
|
{
|
||||||
|
$error = false;
|
||||||
|
|
||||||
|
$response = $this->connection->executeCommand(
|
||||||
|
new RawCommand($arguments)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($response instanceof ResponseInterface) {
|
||||||
|
if ($response instanceof ErrorResponseInterface) {
|
||||||
|
$error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function __call($commandID, $arguments)
|
||||||
|
{
|
||||||
|
return $this->executeCommand(
|
||||||
|
$this->createCommand($commandID, $arguments)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function createCommand($commandID, $arguments = array())
|
||||||
|
{
|
||||||
|
return $this->profile->createCommand($commandID, $arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function executeCommand(CommandInterface $command)
|
||||||
|
{
|
||||||
|
$response = $this->connection->executeCommand($command);
|
||||||
|
|
||||||
|
if ($response instanceof ResponseInterface) {
|
||||||
|
if ($response instanceof ErrorResponseInterface) {
|
||||||
|
$response = $this->onErrorResponse($command, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $command->parseResponse($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles -ERR responses returned by Redis.
|
||||||
|
*
|
||||||
|
* @param CommandInterface $command Redis command that generated the error.
|
||||||
|
* @param ErrorResponseInterface $response Instance of the error response.
|
||||||
|
*
|
||||||
|
* @throws ServerException
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
|
||||||
|
{
|
||||||
|
if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
|
||||||
|
$eval = $this->createCommand('EVAL');
|
||||||
|
$eval->setRawArguments($command->getEvalArguments());
|
||||||
|
|
||||||
|
$response = $this->executeCommand($eval);
|
||||||
|
|
||||||
|
if (!$response instanceof ResponseInterface) {
|
||||||
|
$response = $command->parseResponse($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->options->exceptions) {
|
||||||
|
throw new ServerException($response->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the specified initializer method on `$this` by adjusting the
|
||||||
|
* actual invokation depending on the arity (0, 1 or 2 arguments). This is
|
||||||
|
* simply an utility method to create Redis contexts instances since they
|
||||||
|
* follow a common initialization path.
|
||||||
|
*
|
||||||
|
* @param string $initializer Method name.
|
||||||
|
* @param array $argv Arguments for the method.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
private function sharedContextFactory($initializer, $argv = null)
|
||||||
|
{
|
||||||
|
switch (count($argv)) {
|
||||||
|
case 0:
|
||||||
|
return $this->$initializer();
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return is_array($argv[0])
|
||||||
|
? $this->$initializer($argv[0])
|
||||||
|
: $this->$initializer(null, $argv[0]);
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
list($arg0, $arg1) = $argv;
|
||||||
|
|
||||||
|
return $this->$initializer($arg0, $arg1);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return $this->$initializer($this, $argv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new pipeline context and returns it, or returns the results of
|
||||||
|
* a pipeline executed inside the optionally provided callable object.
|
||||||
|
*
|
||||||
|
* @param mixed ... Array of options, a callable for execution, or both.
|
||||||
|
*
|
||||||
|
* @return Pipeline|array
|
||||||
|
*/
|
||||||
|
public function pipeline(/* arguments */)
|
||||||
|
{
|
||||||
|
return $this->sharedContextFactory('createPipeline', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actual pipeline context initializer method.
|
||||||
|
*
|
||||||
|
* @param array $options Options for the context.
|
||||||
|
* @param mixed $callable Optional callable used to execute the context.
|
||||||
|
*
|
||||||
|
* @return Pipeline|array
|
||||||
|
*/
|
||||||
|
protected function createPipeline(array $options = null, $callable = null)
|
||||||
|
{
|
||||||
|
if (isset($options['atomic']) && $options['atomic']) {
|
||||||
|
$class = 'Predis\Pipeline\Atomic';
|
||||||
|
} elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) {
|
||||||
|
$class = 'Predis\Pipeline\FireAndForget';
|
||||||
|
} else {
|
||||||
|
$class = 'Predis\Pipeline\Pipeline';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @var ClientContextInterface
|
||||||
|
*/
|
||||||
|
$pipeline = new $class($this);
|
||||||
|
|
||||||
|
if (isset($callable)) {
|
||||||
|
return $pipeline->execute($callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new transaction context and returns it, or returns the results
|
||||||
|
* of a transaction executed inside the optionally provided callable object.
|
||||||
|
*
|
||||||
|
* @param mixed ... Array of options, a callable for execution, or both.
|
||||||
|
*
|
||||||
|
* @return MultiExecTransaction|array
|
||||||
|
*/
|
||||||
|
public function transaction(/* arguments */)
|
||||||
|
{
|
||||||
|
return $this->sharedContextFactory('createTransaction', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actual transaction context initializer method.
|
||||||
|
*
|
||||||
|
* @param array $options Options for the context.
|
||||||
|
* @param mixed $callable Optional callable used to execute the context.
|
||||||
|
*
|
||||||
|
* @return MultiExecTransaction|array
|
||||||
|
*/
|
||||||
|
protected function createTransaction(array $options = null, $callable = null)
|
||||||
|
{
|
||||||
|
$transaction = new MultiExecTransaction($this, $options);
|
||||||
|
|
||||||
|
if (isset($callable)) {
|
||||||
|
return $transaction->execute($callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new publish/subscribe context and returns it, or starts its loop
|
||||||
|
* inside the optionally provided callable object.
|
||||||
|
*
|
||||||
|
* @param mixed ... Array of options, a callable for execution, or both.
|
||||||
|
*
|
||||||
|
* @return PubSubConsumer|null
|
||||||
|
*/
|
||||||
|
public function pubSubLoop(/* arguments */)
|
||||||
|
{
|
||||||
|
return $this->sharedContextFactory('createPubSub', func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actual publish/subscribe context initializer method.
|
||||||
|
*
|
||||||
|
* @param array $options Options for the context.
|
||||||
|
* @param mixed $callable Optional callable used to execute the context.
|
||||||
|
*
|
||||||
|
* @return PubSubConsumer|null
|
||||||
|
*/
|
||||||
|
protected function createPubSub(array $options = null, $callable = null)
|
||||||
|
{
|
||||||
|
$pubsub = new PubSubConsumer($this, $options);
|
||||||
|
|
||||||
|
if (!isset($callable)) {
|
||||||
|
return $pubsub;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($pubsub as $message) {
|
||||||
|
if (call_user_func($callable, $pubsub, $message) === false) {
|
||||||
|
$pubsub->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new monitor consumer and returns it.
|
||||||
|
*
|
||||||
|
* @return MonitorConsumer
|
||||||
|
*/
|
||||||
|
public function monitor()
|
||||||
|
{
|
||||||
|
return new MonitorConsumer($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getIterator()
|
||||||
|
{
|
||||||
|
$clients = array();
|
||||||
|
$connection = $this->getConnection();
|
||||||
|
|
||||||
|
if (!$connection instanceof \Traversable) {
|
||||||
|
throw new ClientException('The underlying connection is not traversable');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($connection as $node) {
|
||||||
|
$clients[(string) $node] = new static($node, $this->getOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new \ArrayIterator($clients);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user