mirror of
https://github.com/Icinga/icingaweb2-module-reactbundle.git
synced 2025-07-20 12:24:30 +02:00
Version v0.1.4
This commit is contained in:
parent
61c92c7e42
commit
56e8490e1a
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 ComposerAutoloaderInit3214150501998a72ea353f9c1a1e903e::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/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);
|
||||
});
|
||||
}
|
||||
}
|
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/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(
|
||||
);
|
20
vendor/composer/autoload_files.php
vendored
Normal file
20
vendor/composer/autoload_files.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?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',
|
||||
'383eaff206634a77a1be54e64e6459c7' => $vendorDir . '/sabre/uri/lib/functions.php',
|
||||
'ebf8799635f67b5d7248946fe2154f4a' => $vendorDir . '/ringcentral/psr7/src/functions_include.php',
|
||||
'3569eecfeed3bcf0bad3c998a494ecb8' => $vendorDir . '/sabre/xml/lib/Deserializer/functions.php',
|
||||
'93aa591bc4ca510c520999e34229ee79' => $vendorDir . '/sabre/xml/lib/Serializer/functions.php',
|
||||
'2b9d0f43f9552984cfa82fee95491826' => $vendorDir . '/sabre/event/lib/coroutine.php',
|
||||
'd81bab31d3feb45bfe2f283ea3c8fdf7' => $vendorDir . '/sabre/event/lib/Loop/functions.php',
|
||||
'a1cce3d26cc15c00fcd0b3354bd72c88' => $vendorDir . '/sabre/event/lib/Promise/functions.php',
|
||||
'cea474b4340aa9fa53661e887a21a316' => $vendorDir . '/react/promise-stream/src/functions_include.php',
|
||||
'ebdb698ed4152ae445614b69b5e4bb6a' => $vendorDir . '/sabre/http/lib/functions.php',
|
||||
);
|
13
vendor/composer/autoload_namespaces.php
vendored
Normal file
13
vendor/composer/autoload_namespaces.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Resque' => array($vendorDir . '/chrisboulton/php-resque/lib'),
|
||||
'MKraemer' => array($vendorDir . '/mkraemer/react-pcntl/src'),
|
||||
'Evenement' => array($vendorDir . '/evenement/evenement/src'),
|
||||
'Clue\\Redis\\Protocol' => array($vendorDir . '/clue/redis-protocol/src'),
|
||||
);
|
42
vendor/composer/autoload_psr4.php
vendored
Normal file
42
vendor/composer/autoload_psr4.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Socket\\Raw\\' => array($vendorDir . '/clue/socket-raw/src'),
|
||||
'Sabre\\Xml\\' => array($vendorDir . '/sabre/xml/lib'),
|
||||
'Sabre\\VObject\\' => array($vendorDir . '/sabre/vobject/lib'),
|
||||
'Sabre\\Uri\\' => array($vendorDir . '/sabre/uri/lib'),
|
||||
'Sabre\\HTTP\\' => array($vendorDir . '/sabre/http/lib'),
|
||||
'Sabre\\Event\\' => array($vendorDir . '/sabre/event/lib'),
|
||||
'Sabre\\DAV\\' => array($vendorDir . '/sabre/dav/lib/DAV'),
|
||||
'Sabre\\DAVACL\\' => array($vendorDir . '/sabre/dav/lib/DAVACL'),
|
||||
'Sabre\\CardDAV\\' => array($vendorDir . '/sabre/dav/lib/CardDAV'),
|
||||
'Sabre\\CalDAV\\' => array($vendorDir . '/sabre/dav/lib/CalDAV'),
|
||||
'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\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
||||
'Predis\\' => array($vendorDir . '/predis/predis/src'),
|
||||
'GuzzleHttp\\Stream\\' => array($vendorDir . '/guzzlehttp/streams/src'),
|
||||
'GuzzleHttp\\Ring\\' => array($vendorDir . '/guzzlehttp/ringphp/src'),
|
||||
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
|
||||
'ConnectionManager\\Extra\\' => array($vendorDir . '/clue/connection-manager-extra/src'),
|
||||
'Clue\\React\\Socks\\' => array($vendorDir . '/clue/socks-react/src'),
|
||||
'Clue\\React\\Redis\\' => array($vendorDir . '/clue/redis-react/src'),
|
||||
'Clue\\React\\HttpProxy\\' => array($vendorDir . '/clue/http-proxy-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 ComposerAutoloaderInit3214150501998a72ea353f9c1a1e903e
|
||||
{
|
||||
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('ComposerAutoloaderInit3214150501998a72ea353f9c1a1e903e', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit3214150501998a72ea353f9c1a1e903e', '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\ComposerStaticInit3214150501998a72ea353f9c1a1e903e::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\ComposerStaticInit3214150501998a72ea353f9c1a1e903e::$files;
|
||||
} else {
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
}
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire3214150501998a72ea353f9c1a1e903e($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequire3214150501998a72ea353f9c1a1e903e($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
}
|
||||
}
|
249
vendor/composer/autoload_static.php
vendored
Normal file
249
vendor/composer/autoload_static.php
vendored
Normal file
@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit3214150501998a72ea353f9c1a1e903e
|
||||
{
|
||||
public static $files = array (
|
||||
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
|
||||
'6b06ce8ccf69c43a60a1e48495a034c9' => __DIR__ . '/..' . '/react/promise-timer/src/functions.php',
|
||||
'383eaff206634a77a1be54e64e6459c7' => __DIR__ . '/..' . '/sabre/uri/lib/functions.php',
|
||||
'ebf8799635f67b5d7248946fe2154f4a' => __DIR__ . '/..' . '/ringcentral/psr7/src/functions_include.php',
|
||||
'3569eecfeed3bcf0bad3c998a494ecb8' => __DIR__ . '/..' . '/sabre/xml/lib/Deserializer/functions.php',
|
||||
'93aa591bc4ca510c520999e34229ee79' => __DIR__ . '/..' . '/sabre/xml/lib/Serializer/functions.php',
|
||||
'2b9d0f43f9552984cfa82fee95491826' => __DIR__ . '/..' . '/sabre/event/lib/coroutine.php',
|
||||
'd81bab31d3feb45bfe2f283ea3c8fdf7' => __DIR__ . '/..' . '/sabre/event/lib/Loop/functions.php',
|
||||
'a1cce3d26cc15c00fcd0b3354bd72c88' => __DIR__ . '/..' . '/sabre/event/lib/Promise/functions.php',
|
||||
'cea474b4340aa9fa53661e887a21a316' => __DIR__ . '/..' . '/react/promise-stream/src/functions_include.php',
|
||||
'ebdb698ed4152ae445614b69b5e4bb6a' => __DIR__ . '/..' . '/sabre/http/lib/functions.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'S' =>
|
||||
array (
|
||||
'Socket\\Raw\\' => 11,
|
||||
'Sabre\\Xml\\' => 10,
|
||||
'Sabre\\VObject\\' => 14,
|
||||
'Sabre\\Uri\\' => 10,
|
||||
'Sabre\\HTTP\\' => 11,
|
||||
'Sabre\\Event\\' => 12,
|
||||
'Sabre\\DAV\\' => 10,
|
||||
'Sabre\\DAVACL\\' => 13,
|
||||
'Sabre\\CardDAV\\' => 14,
|
||||
'Sabre\\CalDAV\\' => 13,
|
||||
),
|
||||
'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\\Log\\' => 8,
|
||||
'Psr\\Http\\Message\\' => 17,
|
||||
'Predis\\' => 7,
|
||||
),
|
||||
'G' =>
|
||||
array (
|
||||
'GuzzleHttp\\Stream\\' => 18,
|
||||
'GuzzleHttp\\Ring\\' => 16,
|
||||
'GuzzleHttp\\' => 11,
|
||||
),
|
||||
'C' =>
|
||||
array (
|
||||
'ConnectionManager\\Extra\\' => 24,
|
||||
'Clue\\React\\Socks\\' => 17,
|
||||
'Clue\\React\\Redis\\' => 17,
|
||||
'Clue\\React\\HttpProxy\\' => 21,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'Socket\\Raw\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/socket-raw/src',
|
||||
),
|
||||
'Sabre\\Xml\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/sabre/xml/lib',
|
||||
),
|
||||
'Sabre\\VObject\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/sabre/vobject/lib',
|
||||
),
|
||||
'Sabre\\Uri\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/sabre/uri/lib',
|
||||
),
|
||||
'Sabre\\HTTP\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/sabre/http/lib',
|
||||
),
|
||||
'Sabre\\Event\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/sabre/event/lib',
|
||||
),
|
||||
'Sabre\\DAV\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/sabre/dav/lib/DAV',
|
||||
),
|
||||
'Sabre\\DAVACL\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL',
|
||||
),
|
||||
'Sabre\\CardDAV\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV',
|
||||
),
|
||||
'Sabre\\CalDAV\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV',
|
||||
),
|
||||
'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\\Log\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
|
||||
),
|
||||
'Psr\\Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/http-message/src',
|
||||
),
|
||||
'Predis\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/predis/predis/src',
|
||||
),
|
||||
'GuzzleHttp\\Stream\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/streams/src',
|
||||
),
|
||||
'GuzzleHttp\\Ring\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/ringphp/src',
|
||||
),
|
||||
'GuzzleHttp\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
|
||||
),
|
||||
'ConnectionManager\\Extra\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/connection-manager-extra/src',
|
||||
),
|
||||
'Clue\\React\\Socks\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/socks-react/src',
|
||||
),
|
||||
'Clue\\React\\Redis\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/redis-react/src',
|
||||
),
|
||||
'Clue\\React\\HttpProxy\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/http-proxy-react/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixesPsr0 = array (
|
||||
'R' =>
|
||||
array (
|
||||
'Resque' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/chrisboulton/php-resque/lib',
|
||||
),
|
||||
),
|
||||
'M' =>
|
||||
array (
|
||||
'MKraemer' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/mkraemer/react-pcntl/src',
|
||||
),
|
||||
),
|
||||
'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 = ComposerStaticInit3214150501998a72ea353f9c1a1e903e::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit3214150501998a72ea353f9c1a1e903e::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInit3214150501998a72ea353f9c1a1e903e::$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);
|
||||
}
|
||||
}
|
||||
}
|
19
vendor/guzzlehttp/guzzle/LICENSE
vendored
Normal file
19
vendor/guzzlehttp/guzzle/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2011-2015 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.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.
|
148
vendor/guzzlehttp/guzzle/src/BatchResults.php
vendored
Normal file
148
vendor/guzzlehttp/guzzle/src/BatchResults.php
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
namespace GuzzleHttp;
|
||||
|
||||
/**
|
||||
* Represents the result of a batch operation. This result container is
|
||||
* iterable, countable, and you can can get a result by value using the
|
||||
* getResult function.
|
||||
*
|
||||
* Successful results are anything other than exceptions. Failure results are
|
||||
* exceptions.
|
||||
*
|
||||
* @package GuzzleHttp
|
||||
*/
|
||||
class BatchResults implements \Countable, \IteratorAggregate, \ArrayAccess
|
||||
{
|
||||
private $hash;
|
||||
|
||||
/**
|
||||
* @param \SplObjectStorage $hash Hash of key objects to result values.
|
||||
*/
|
||||
public function __construct(\SplObjectStorage $hash)
|
||||
{
|
||||
$this->hash = $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the keys that are available on the batch result.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getKeys()
|
||||
{
|
||||
return iterator_to_array($this->hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a result from the container for the given object. When getting
|
||||
* results for a batch of requests, provide the request object.
|
||||
*
|
||||
* @param object $forObject Object to retrieve the result for.
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getResult($forObject)
|
||||
{
|
||||
return isset($this->hash[$forObject]) ? $this->hash[$forObject] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of successful results.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSuccessful()
|
||||
{
|
||||
$results = [];
|
||||
foreach ($this->hash as $key) {
|
||||
if (!($this->hash[$key] instanceof \Exception)) {
|
||||
$results[] = $this->hash[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of failed results.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFailures()
|
||||
{
|
||||
$results = [];
|
||||
foreach ($this->hash as $key) {
|
||||
if ($this->hash[$key] instanceof \Exception) {
|
||||
$results[] = $this->hash[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows iteration over all batch result values.
|
||||
*
|
||||
* @return \ArrayIterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
$results = [];
|
||||
foreach ($this->hash as $key) {
|
||||
$results[] = $this->hash[$key];
|
||||
}
|
||||
|
||||
return new \ArrayIterator($results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of elements in the batch result.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the batch contains a specific numerical array index.
|
||||
*
|
||||
* @param int $key Index to access
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($key)
|
||||
{
|
||||
return $key < count($this->hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows access of the batch using a numerical array index.
|
||||
*
|
||||
* @param int $key Index to access.
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function offsetGet($key)
|
||||
{
|
||||
$i = -1;
|
||||
foreach ($this->hash as $obj) {
|
||||
if ($key === ++$i) {
|
||||
return $this->hash[$obj];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function offsetUnset($key)
|
||||
{
|
||||
throw new \RuntimeException('Not implemented');
|
||||
}
|
||||
|
||||
public function offsetSet($key, $value)
|
||||
{
|
||||
throw new \RuntimeException('Not implemented');
|
||||
}
|
||||
}
|
362
vendor/guzzlehttp/guzzle/src/Client.php
vendored
Normal file
362
vendor/guzzlehttp/guzzle/src/Client.php
vendored
Normal file
@ -0,0 +1,362 @@
|
||||
<?php
|
||||
namespace GuzzleHttp;
|
||||
|
||||
use GuzzleHttp\Event\HasEmitterTrait;
|
||||
use GuzzleHttp\Message\MessageFactory;
|
||||
use GuzzleHttp\Message\MessageFactoryInterface;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\FutureResponse;
|
||||
use GuzzleHttp\Ring\Core;
|
||||
use GuzzleHttp\Ring\Future\FutureInterface;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use React\Promise\FulfilledPromise;
|
||||
use React\Promise\RejectedPromise;
|
||||
|
||||
/**
|
||||
* HTTP client
|
||||
*/
|
||||
class Client implements ClientInterface
|
||||
{
|
||||
use HasEmitterTrait;
|
||||
|
||||
/** @var MessageFactoryInterface Request factory used by the client */
|
||||
private $messageFactory;
|
||||
|
||||
/** @var Url Base URL of the client */
|
||||
private $baseUrl;
|
||||
|
||||
/** @var array Default request options */
|
||||
private $defaults;
|
||||
|
||||
/** @var callable Request state machine */
|
||||
private $fsm;
|
||||
|
||||
/**
|
||||
* Clients accept an array of constructor parameters.
|
||||
*
|
||||
* Here's an example of creating a client using an URI template for the
|
||||
* client's base_url and an array of default request options to apply
|
||||
* to each request:
|
||||
*
|
||||
* $client = new Client([
|
||||
* 'base_url' => [
|
||||
* 'http://www.foo.com/{version}/',
|
||||
* ['version' => '123']
|
||||
* ],
|
||||
* 'defaults' => [
|
||||
* 'timeout' => 10,
|
||||
* 'allow_redirects' => false,
|
||||
* 'proxy' => '192.168.16.1:10'
|
||||
* ]
|
||||
* ]);
|
||||
*
|
||||
* @param array $config Client configuration settings
|
||||
* - base_url: Base URL of the client that is merged into relative URLs.
|
||||
* Can be a string or an array that contains a URI template followed
|
||||
* by an associative array of expansion variables to inject into the
|
||||
* URI template.
|
||||
* - handler: callable RingPHP handler used to transfer requests
|
||||
* - message_factory: Factory used to create request and response object
|
||||
* - defaults: Default request options to apply to each request
|
||||
* - emitter: Event emitter used for request events
|
||||
* - fsm: (internal use only) The request finite state machine. A
|
||||
* function that accepts a transaction and optional final state. The
|
||||
* function is responsible for transitioning a request through its
|
||||
* lifecycle events.
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->configureBaseUrl($config);
|
||||
$this->configureDefaults($config);
|
||||
|
||||
if (isset($config['emitter'])) {
|
||||
$this->emitter = $config['emitter'];
|
||||
}
|
||||
|
||||
$this->messageFactory = isset($config['message_factory'])
|
||||
? $config['message_factory']
|
||||
: new MessageFactory();
|
||||
|
||||
if (isset($config['fsm'])) {
|
||||
$this->fsm = $config['fsm'];
|
||||
} else {
|
||||
if (isset($config['handler'])) {
|
||||
$handler = $config['handler'];
|
||||
} elseif (isset($config['adapter'])) {
|
||||
$handler = $config['adapter'];
|
||||
} else {
|
||||
$handler = Utils::getDefaultHandler();
|
||||
}
|
||||
$this->fsm = new RequestFsm($handler, $this->messageFactory);
|
||||
}
|
||||
}
|
||||
|
||||
public function getDefaultOption($keyOrPath = null)
|
||||
{
|
||||
return $keyOrPath === null
|
||||
? $this->defaults
|
||||
: Utils::getPath($this->defaults, $keyOrPath);
|
||||
}
|
||||
|
||||
public function setDefaultOption($keyOrPath, $value)
|
||||
{
|
||||
Utils::setPath($this->defaults, $keyOrPath, $value);
|
||||
}
|
||||
|
||||
public function getBaseUrl()
|
||||
{
|
||||
return (string) $this->baseUrl;
|
||||
}
|
||||
|
||||
public function createRequest($method, $url = null, array $options = [])
|
||||
{
|
||||
$options = $this->mergeDefaults($options);
|
||||
// Use a clone of the client's emitter
|
||||
$options['config']['emitter'] = clone $this->getEmitter();
|
||||
$url = $url || (is_string($url) && strlen($url))
|
||||
? $this->buildUrl($url)
|
||||
: (string) $this->baseUrl;
|
||||
|
||||
return $this->messageFactory->createRequest($method, $url, $options);
|
||||
}
|
||||
|
||||
public function get($url = null, $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('GET', $url, $options));
|
||||
}
|
||||
|
||||
public function head($url = null, array $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('HEAD', $url, $options));
|
||||
}
|
||||
|
||||
public function delete($url = null, array $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('DELETE', $url, $options));
|
||||
}
|
||||
|
||||
public function put($url = null, array $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('PUT', $url, $options));
|
||||
}
|
||||
|
||||
public function patch($url = null, array $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('PATCH', $url, $options));
|
||||
}
|
||||
|
||||
public function post($url = null, array $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('POST', $url, $options));
|
||||
}
|
||||
|
||||
public function options($url = null, array $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('OPTIONS', $url, $options));
|
||||
}
|
||||
|
||||
public function send(RequestInterface $request)
|
||||
{
|
||||
$isFuture = $request->getConfig()->get('future');
|
||||
$trans = new Transaction($this, $request, $isFuture);
|
||||
$fn = $this->fsm;
|
||||
|
||||
try {
|
||||
$fn($trans);
|
||||
if ($isFuture) {
|
||||
// Turn the normal response into a future if needed.
|
||||
return $trans->response instanceof FutureInterface
|
||||
? $trans->response
|
||||
: new FutureResponse(new FulfilledPromise($trans->response));
|
||||
}
|
||||
// Resolve deep futures if this is not a future
|
||||
// transaction. This accounts for things like retries
|
||||
// that do not have an immediate side-effect.
|
||||
while ($trans->response instanceof FutureInterface) {
|
||||
$trans->response = $trans->response->wait();
|
||||
}
|
||||
return $trans->response;
|
||||
} catch (\Exception $e) {
|
||||
if ($isFuture) {
|
||||
// Wrap the exception in a promise
|
||||
return new FutureResponse(new RejectedPromise($e));
|
||||
}
|
||||
throw RequestException::wrapException($trans->request, $e);
|
||||
} catch (\TypeError $error) {
|
||||
$exception = new \Exception($error->getMessage(), $error->getCode(), $error);
|
||||
if ($isFuture) {
|
||||
// Wrap the exception in a promise
|
||||
return new FutureResponse(new RejectedPromise($exception));
|
||||
}
|
||||
throw RequestException::wrapException($trans->request, $exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of default options to apply to the client
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaultOptions()
|
||||
{
|
||||
$settings = [
|
||||
'allow_redirects' => true,
|
||||
'exceptions' => true,
|
||||
'decode_content' => true,
|
||||
'verify' => true
|
||||
];
|
||||
|
||||
// Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
|
||||
// We can only trust the HTTP_PROXY environment variable in a CLI
|
||||
// process due to the fact that PHP has no reliable mechanism to
|
||||
// get environment variables that start with "HTTP_".
|
||||
if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {
|
||||
$settings['proxy']['http'] = getenv('HTTP_PROXY');
|
||||
}
|
||||
|
||||
if ($proxy = getenv('HTTPS_PROXY')) {
|
||||
$settings['proxy']['https'] = $proxy;
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a URI template and inherit from the base URL if it's relative
|
||||
*
|
||||
* @param string|array $url URL or an array of the URI template to expand
|
||||
* followed by a hash of template varnames.
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function buildUrl($url)
|
||||
{
|
||||
// URI template (absolute or relative)
|
||||
if (!is_array($url)) {
|
||||
return strpos($url, '://')
|
||||
? (string) $url
|
||||
: (string) $this->baseUrl->combine($url);
|
||||
}
|
||||
|
||||
if (!isset($url[1])) {
|
||||
throw new \InvalidArgumentException('You must provide a hash of '
|
||||
. 'varname options in the second element of a URL array.');
|
||||
}
|
||||
|
||||
// Absolute URL
|
||||
if (strpos($url[0], '://')) {
|
||||
return Utils::uriTemplate($url[0], $url[1]);
|
||||
}
|
||||
|
||||
// Combine the relative URL with the base URL
|
||||
return (string) $this->baseUrl->combine(
|
||||
Utils::uriTemplate($url[0], $url[1])
|
||||
);
|
||||
}
|
||||
|
||||
private function configureBaseUrl(&$config)
|
||||
{
|
||||
if (!isset($config['base_url'])) {
|
||||
$this->baseUrl = new Url('', '');
|
||||
} elseif (!is_array($config['base_url'])) {
|
||||
$this->baseUrl = Url::fromString($config['base_url']);
|
||||
} elseif (count($config['base_url']) < 2) {
|
||||
throw new \InvalidArgumentException('You must provide a hash of '
|
||||
. 'varname options in the second element of a base_url array.');
|
||||
} else {
|
||||
$this->baseUrl = Url::fromString(
|
||||
Utils::uriTemplate(
|
||||
$config['base_url'][0],
|
||||
$config['base_url'][1]
|
||||
)
|
||||
);
|
||||
$config['base_url'] = (string) $this->baseUrl;
|
||||
}
|
||||
}
|
||||
|
||||
private function configureDefaults($config)
|
||||
{
|
||||
if (!isset($config['defaults'])) {
|
||||
$this->defaults = $this->getDefaultOptions();
|
||||
} else {
|
||||
$this->defaults = array_replace(
|
||||
$this->getDefaultOptions(),
|
||||
$config['defaults']
|
||||
);
|
||||
}
|
||||
|
||||
// Add the default user-agent header
|
||||
if (!isset($this->defaults['headers'])) {
|
||||
$this->defaults['headers'] = [
|
||||
'User-Agent' => Utils::getDefaultUserAgent()
|
||||
];
|
||||
} elseif (!Core::hasHeader($this->defaults, 'User-Agent')) {
|
||||
// Add the User-Agent header if one was not already set
|
||||
$this->defaults['headers']['User-Agent'] = Utils::getDefaultUserAgent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges default options into the array passed by reference.
|
||||
*
|
||||
* @param array $options Options to modify by reference
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function mergeDefaults($options)
|
||||
{
|
||||
$defaults = $this->defaults;
|
||||
|
||||
// Case-insensitively merge in default headers if both defaults and
|
||||
// options have headers specified.
|
||||
if (!empty($defaults['headers']) && !empty($options['headers'])) {
|
||||
// Create a set of lowercased keys that are present.
|
||||
$lkeys = [];
|
||||
foreach (array_keys($options['headers']) as $k) {
|
||||
$lkeys[strtolower($k)] = true;
|
||||
}
|
||||
// Merge in lowercase default keys when not present in above set.
|
||||
foreach ($defaults['headers'] as $key => $value) {
|
||||
if (!isset($lkeys[strtolower($key)])) {
|
||||
$options['headers'][$key] = $value;
|
||||
}
|
||||
}
|
||||
// No longer need to merge in headers.
|
||||
unset($defaults['headers']);
|
||||
}
|
||||
|
||||
$result = array_replace_recursive($defaults, $options);
|
||||
foreach ($options as $k => $v) {
|
||||
if ($v === null) {
|
||||
unset($result[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@see GuzzleHttp\Pool} instead.
|
||||
* @see GuzzleHttp\Pool
|
||||
*/
|
||||
public function sendAll($requests, array $options = [])
|
||||
{
|
||||
Pool::send($this, $requests, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use GuzzleHttp\Utils::getDefaultHandler
|
||||
*/
|
||||
public static function getDefaultHandler()
|
||||
{
|
||||
return Utils::getDefaultHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use GuzzleHttp\Utils::getDefaultUserAgent
|
||||
*/
|
||||
public static function getDefaultUserAgent()
|
||||
{
|
||||
return Utils::getDefaultUserAgent();
|
||||
}
|
||||
}
|
150
vendor/guzzlehttp/guzzle/src/ClientInterface.php
vendored
Normal file
150
vendor/guzzlehttp/guzzle/src/ClientInterface.php
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
namespace GuzzleHttp;
|
||||
|
||||
use GuzzleHttp\Event\HasEmitterInterface;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Client interface for sending HTTP requests
|
||||
*/
|
||||
interface ClientInterface extends HasEmitterInterface
|
||||
{
|
||||
const VERSION = '5.3.1';
|
||||
|
||||
/**
|
||||
* Create and return a new {@see RequestInterface} object.
|
||||
*
|
||||
* Use an absolute path to override the base path of the client, or a
|
||||
* relative path to append to the base path of the client. The URL can
|
||||
* contain the query string as well. Use an array to provide a URL
|
||||
* template and additional variables to use in the URL template expansion.
|
||||
*
|
||||
* @param string $method HTTP method
|
||||
* @param string|array|Url $url URL or URI template
|
||||
* @param array $options Array of request options to apply.
|
||||
*
|
||||
* @return RequestInterface
|
||||
*/
|
||||
public function createRequest($method, $url = null, array $options = []);
|
||||
|
||||
/**
|
||||
* Send a GET request
|
||||
*
|
||||
* @param string|array|Url $url URL or URI template
|
||||
* @param array $options Array of request options to apply.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws RequestException When an error is encountered
|
||||
*/
|
||||
public function get($url = null, $options = []);
|
||||
|
||||
/**
|
||||
* Send a HEAD request
|
||||
*
|
||||
* @param string|array|Url $url URL or URI template
|
||||
* @param array $options Array of request options to apply.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws RequestException When an error is encountered
|
||||
*/
|
||||
public function head($url = null, array $options = []);
|
||||
|
||||
/**
|
||||
* Send a DELETE request
|
||||
*
|
||||
* @param string|array|Url $url URL or URI template
|
||||
* @param array $options Array of request options to apply.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws RequestException When an error is encountered
|
||||
*/
|
||||
public function delete($url = null, array $options = []);
|
||||
|
||||
/**
|
||||
* Send a PUT request
|
||||
*
|
||||
* @param string|array|Url $url URL or URI template
|
||||
* @param array $options Array of request options to apply.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws RequestException When an error is encountered
|
||||
*/
|
||||
public function put($url = null, array $options = []);
|
||||
|
||||
/**
|
||||
* Send a PATCH request
|
||||
*
|
||||
* @param string|array|Url $url URL or URI template
|
||||
* @param array $options Array of request options to apply.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws RequestException When an error is encountered
|
||||
*/
|
||||
public function patch($url = null, array $options = []);
|
||||
|
||||
/**
|
||||
* Send a POST request
|
||||
*
|
||||
* @param string|array|Url $url URL or URI template
|
||||
* @param array $options Array of request options to apply.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws RequestException When an error is encountered
|
||||
*/
|
||||
public function post($url = null, array $options = []);
|
||||
|
||||
/**
|
||||
* Send an OPTIONS request
|
||||
*
|
||||
* @param string|array|Url $url URL or URI template
|
||||
* @param array $options Array of request options to apply.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws RequestException When an error is encountered
|
||||
*/
|
||||
public function options($url = null, array $options = []);
|
||||
|
||||
/**
|
||||
* Sends a single request
|
||||
*
|
||||
* @param RequestInterface $request Request to send
|
||||
*
|
||||
* @return \GuzzleHttp\Message\ResponseInterface
|
||||
* @throws \LogicException When the handler does not populate a response
|
||||
* @throws RequestException When an error is encountered
|
||||
*/
|
||||
public function send(RequestInterface $request);
|
||||
|
||||
/**
|
||||
* Get default request options of the client.
|
||||
*
|
||||
* @param string|null $keyOrPath The Path to a particular default request
|
||||
* option to retrieve or pass null to retrieve all default request
|
||||
* options. The syntax uses "/" to denote a path through nested PHP
|
||||
* arrays. For example, "headers/content-type".
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDefaultOption($keyOrPath = null);
|
||||
|
||||
/**
|
||||
* Set a default request option on the client so that any request created
|
||||
* by the client will use the provided default value unless overridden
|
||||
* explicitly when creating a request.
|
||||
*
|
||||
* @param string|null $keyOrPath The Path to a particular configuration
|
||||
* value to set. The syntax uses a path notation that allows you to
|
||||
* specify nested configuration values (e.g., 'headers/content-type').
|
||||
* @param mixed $value Default request option value to set
|
||||
*/
|
||||
public function setDefaultOption($keyOrPath, $value);
|
||||
|
||||
/**
|
||||
* Get the base URL of the client.
|
||||
*
|
||||
* @return string Returns the base URL if present
|
||||
*/
|
||||
public function getBaseUrl();
|
||||
}
|
236
vendor/guzzlehttp/guzzle/src/Collection.php
vendored
Normal file
236
vendor/guzzlehttp/guzzle/src/Collection.php
vendored
Normal file
@ -0,0 +1,236 @@
|
||||
<?php
|
||||
namespace GuzzleHttp;
|
||||
|
||||
/**
|
||||
* Key value pair collection object
|
||||
*/
|
||||
class Collection implements
|
||||
\ArrayAccess,
|
||||
\IteratorAggregate,
|
||||
\Countable,
|
||||
ToArrayInterface
|
||||
{
|
||||
use HasDataTrait;
|
||||
|
||||
/**
|
||||
* @param array $data Associative array of data to set
|
||||
*/
|
||||
public function __construct(array $data = [])
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new collection from an array, validate the keys, and add default
|
||||
* values where missing
|
||||
*
|
||||
* @param array $config Configuration values to apply.
|
||||
* @param array $defaults Default parameters
|
||||
* @param array $required Required parameter names
|
||||
*
|
||||
* @return self
|
||||
* @throws \InvalidArgumentException if a parameter is missing
|
||||
*/
|
||||
public static function fromConfig(
|
||||
array $config = [],
|
||||
array $defaults = [],
|
||||
array $required = []
|
||||
) {
|
||||
$data = $config + $defaults;
|
||||
|
||||
if ($missing = array_diff($required, array_keys($data))) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Config is missing the following keys: ' .
|
||||
implode(', ', $missing));
|
||||
}
|
||||
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all key value pairs
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->data = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific key value.
|
||||
*
|
||||
* @param string $key Key to retrieve.
|
||||
*
|
||||
* @return mixed|null Value of the key or NULL
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
return isset($this->data[$key]) ? $this->data[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a key value pair
|
||||
*
|
||||
* @param string $key Key to set
|
||||
* @param mixed $value Value to set
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value to a key. If a key of the same name has already been added,
|
||||
* the key value will be converted into an array and the new value will be
|
||||
* pushed to the end of the array.
|
||||
*
|
||||
* @param string $key Key to add
|
||||
* @param mixed $value Value to add to the key
|
||||
*/
|
||||
public function add($key, $value)
|
||||
{
|
||||
if (!array_key_exists($key, $this->data)) {
|
||||
$this->data[$key] = $value;
|
||||
} elseif (is_array($this->data[$key])) {
|
||||
$this->data[$key][] = $value;
|
||||
} else {
|
||||
$this->data[$key] = array($this->data[$key], $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a specific key value pair
|
||||
*
|
||||
* @param string $key A key to remove
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
unset($this->data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all keys in the collection
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getKeys()
|
||||
{
|
||||
return array_keys($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the specified key is present.
|
||||
*
|
||||
* @param string $key The key for which to check the existence.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasKey($key)
|
||||
{
|
||||
return array_key_exists($key, $this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any keys contains a certain value
|
||||
*
|
||||
* @param string $value Value to search for
|
||||
*
|
||||
* @return mixed Returns the key if the value was found FALSE if the value
|
||||
* was not found.
|
||||
*/
|
||||
public function hasValue($value)
|
||||
{
|
||||
return array_search($value, $this->data, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the data of the object with the value of an array
|
||||
*
|
||||
* @param array $data Associative array of data
|
||||
*/
|
||||
public function replace(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and merge in a Collection or array of key value pair data.
|
||||
*
|
||||
* @param Collection|array $data Associative array of key value pair data
|
||||
*/
|
||||
public function merge($data)
|
||||
{
|
||||
foreach ($data as $key => $value) {
|
||||
$this->add($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite key value pairs in this collection with all of the data from
|
||||
* an array or collection.
|
||||
*
|
||||
* @param array|\Traversable $data Values to override over this config
|
||||
*/
|
||||
public function overwriteWith($data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
$this->data = $data + $this->data;
|
||||
} elseif ($data instanceof Collection) {
|
||||
$this->data = $data->toArray() + $this->data;
|
||||
} else {
|
||||
foreach ($data as $key => $value) {
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Collection containing all the elements of the collection after
|
||||
* applying the callback function to each one.
|
||||
*
|
||||
* The callable should accept three arguments:
|
||||
* - (string) $key
|
||||
* - (string) $value
|
||||
* - (array) $context
|
||||
*
|
||||
* The callable must return a the altered or unaltered value.
|
||||
*
|
||||
* @param callable $closure Map function to apply
|
||||
* @param array $context Context to pass to the callable
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function map(callable $closure, array $context = [])
|
||||
{
|
||||
$collection = new static();
|
||||
foreach ($this as $key => $value) {
|
||||
$collection[$key] = $closure($key, $value, $context);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over each key value pair in the collection passing them to the
|
||||
* callable. If the callable returns true, the current value from input is
|
||||
* returned into the result Collection.
|
||||
*
|
||||
* The callable must accept two arguments:
|
||||
* - (string) $key
|
||||
* - (string) $value
|
||||
*
|
||||
* @param callable $closure Evaluation function
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function filter(callable $closure)
|
||||
{
|
||||
$collection = new static();
|
||||
foreach ($this->data as $key => $value) {
|
||||
if ($closure($key, $value)) {
|
||||
$collection[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
}
|
248
vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
vendored
Normal file
248
vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Cookie;
|
||||
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
use GuzzleHttp\ToArrayInterface;
|
||||
|
||||
/**
|
||||
* Cookie jar that stores cookies an an array
|
||||
*/
|
||||
class CookieJar implements CookieJarInterface, ToArrayInterface
|
||||
{
|
||||
/** @var SetCookie[] Loaded cookie data */
|
||||
private $cookies = [];
|
||||
|
||||
/** @var bool */
|
||||
private $strictMode;
|
||||
|
||||
/**
|
||||
* @param bool $strictMode Set to true to throw exceptions when invalid
|
||||
* cookies are added to the cookie jar.
|
||||
* @param array $cookieArray Array of SetCookie objects or a hash of arrays
|
||||
* that can be used with the SetCookie constructor
|
||||
*/
|
||||
public function __construct($strictMode = false, $cookieArray = [])
|
||||
{
|
||||
$this->strictMode = $strictMode;
|
||||
|
||||
foreach ($cookieArray as $cookie) {
|
||||
if (!($cookie instanceof SetCookie)) {
|
||||
$cookie = new SetCookie($cookie);
|
||||
}
|
||||
$this->setCookie($cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Cookie jar from an associative array and domain.
|
||||
*
|
||||
* @param array $cookies Cookies to create the jar from
|
||||
* @param string $domain Domain to set the cookies to
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function fromArray(array $cookies, $domain)
|
||||
{
|
||||
$cookieJar = new self();
|
||||
foreach ($cookies as $name => $value) {
|
||||
$cookieJar->setCookie(new SetCookie([
|
||||
'Domain' => $domain,
|
||||
'Name' => $name,
|
||||
'Value' => $value,
|
||||
'Discard' => true
|
||||
]));
|
||||
}
|
||||
|
||||
return $cookieJar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote the cookie value if it is not already quoted and it contains
|
||||
* problematic characters.
|
||||
*
|
||||
* @param string $value Value that may or may not need to be quoted
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCookieValue($value)
|
||||
{
|
||||
if (substr($value, 0, 1) !== '"' &&
|
||||
substr($value, -1, 1) !== '"' &&
|
||||
strpbrk($value, ';,')
|
||||
) {
|
||||
$value = '"' . $value . '"';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
return array_map(function (SetCookie $cookie) {
|
||||
return $cookie->toArray();
|
||||
}, $this->getIterator()->getArrayCopy());
|
||||
}
|
||||
|
||||
public function clear($domain = null, $path = null, $name = null)
|
||||
{
|
||||
if (!$domain) {
|
||||
$this->cookies = [];
|
||||
return;
|
||||
} elseif (!$path) {
|
||||
$this->cookies = array_filter(
|
||||
$this->cookies,
|
||||
function (SetCookie $cookie) use ($path, $domain) {
|
||||
return !$cookie->matchesDomain($domain);
|
||||
}
|
||||
);
|
||||
} elseif (!$name) {
|
||||
$this->cookies = array_filter(
|
||||
$this->cookies,
|
||||
function (SetCookie $cookie) use ($path, $domain) {
|
||||
return !($cookie->matchesPath($path) &&
|
||||
$cookie->matchesDomain($domain));
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$this->cookies = array_filter(
|
||||
$this->cookies,
|
||||
function (SetCookie $cookie) use ($path, $domain, $name) {
|
||||
return !($cookie->getName() == $name &&
|
||||
$cookie->matchesPath($path) &&
|
||||
$cookie->matchesDomain($domain));
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function clearSessionCookies()
|
||||
{
|
||||
$this->cookies = array_filter(
|
||||
$this->cookies,
|
||||
function (SetCookie $cookie) {
|
||||
return !$cookie->getDiscard() && $cookie->getExpires();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public function setCookie(SetCookie $cookie)
|
||||
{
|
||||
// Only allow cookies with set and valid domain, name, value
|
||||
$result = $cookie->validate();
|
||||
if ($result !== true) {
|
||||
if ($this->strictMode) {
|
||||
throw new \RuntimeException('Invalid cookie: ' . $result);
|
||||
} else {
|
||||
$this->removeCookieIfEmpty($cookie);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve conflicts with previously set cookies
|
||||
foreach ($this->cookies as $i => $c) {
|
||||
|
||||
// Two cookies are identical, when their path, and domain are
|
||||
// identical.
|
||||
if ($c->getPath() != $cookie->getPath() ||
|
||||
$c->getDomain() != $cookie->getDomain() ||
|
||||
$c->getName() != $cookie->getName()
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The previously set cookie is a discard cookie and this one is
|
||||
// not so allow the new cookie to be set
|
||||
if (!$cookie->getDiscard() && $c->getDiscard()) {
|
||||
unset($this->cookies[$i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the new cookie's expiration is further into the future, then
|
||||
// replace the old cookie
|
||||
if ($cookie->getExpires() > $c->getExpires()) {
|
||||
unset($this->cookies[$i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the value has changed, we better change it
|
||||
if ($cookie->getValue() !== $c->getValue()) {
|
||||
unset($this->cookies[$i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// The cookie exists, so no need to continue
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->cookies[] = $cookie;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->cookies);
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator(array_values($this->cookies));
|
||||
}
|
||||
|
||||
public function extractCookies(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
if ($cookieHeader = $response->getHeaderAsArray('Set-Cookie')) {
|
||||
foreach ($cookieHeader as $cookie) {
|
||||
$sc = SetCookie::fromString($cookie);
|
||||
if (!$sc->getDomain()) {
|
||||
$sc->setDomain($request->getHost());
|
||||
}
|
||||
$this->setCookie($sc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addCookieHeader(RequestInterface $request)
|
||||
{
|
||||
$values = [];
|
||||
$scheme = $request->getScheme();
|
||||
$host = $request->getHost();
|
||||
$path = $request->getPath();
|
||||
|
||||
foreach ($this->cookies as $cookie) {
|
||||
if ($cookie->matchesPath($path) &&
|
||||
$cookie->matchesDomain($host) &&
|
||||
!$cookie->isExpired() &&
|
||||
(!$cookie->getSecure() || $scheme == 'https')
|
||||
) {
|
||||
$values[] = $cookie->getName() . '='
|
||||
. self::getCookieValue($cookie->getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if ($values) {
|
||||
$request->setHeader('Cookie', implode('; ', $values));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a cookie already exists and the server asks to set it again with a
|
||||
* null value, the cookie must be deleted.
|
||||
*
|
||||
* @param SetCookie $cookie
|
||||
*/
|
||||
private function removeCookieIfEmpty(SetCookie $cookie)
|
||||
{
|
||||
$cookieValue = $cookie->getValue();
|
||||
if ($cookieValue === null || $cookieValue === '') {
|
||||
$this->clear(
|
||||
$cookie->getDomain(),
|
||||
$cookie->getPath(),
|
||||
$cookie->getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
75
vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
vendored
Normal file
75
vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Cookie;
|
||||
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Stores HTTP cookies.
|
||||
*
|
||||
* It extracts cookies from HTTP requests, and returns them in HTTP responses.
|
||||
* CookieJarInterface instances automatically expire contained cookies when
|
||||
* necessary. Subclasses are also responsible for storing and retrieving
|
||||
* cookies from a file, database, etc.
|
||||
*
|
||||
* @link http://docs.python.org/2/library/cookielib.html Inspiration
|
||||
*/
|
||||
interface CookieJarInterface extends \Countable, \IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Add a Cookie header to a request.
|
||||
*
|
||||
* If no matching cookies are found in the cookie jar, then no Cookie
|
||||
* header is added to the request.
|
||||
*
|
||||
* @param RequestInterface $request Request object to update
|
||||
*/
|
||||
public function addCookieHeader(RequestInterface $request);
|
||||
|
||||
/**
|
||||
* Extract cookies from an HTTP response and store them in the CookieJar.
|
||||
*
|
||||
* @param RequestInterface $request Request that was sent
|
||||
* @param ResponseInterface $response Response that was received
|
||||
*/
|
||||
public function extractCookies(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets a cookie in the cookie jar.
|
||||
*
|
||||
* @param SetCookie $cookie Cookie to set.
|
||||
*
|
||||
* @return bool Returns true on success or false on failure
|
||||
*/
|
||||
public function setCookie(SetCookie $cookie);
|
||||
|
||||
/**
|
||||
* Remove cookies currently held in the cookie jar.
|
||||
*
|
||||
* Invoking this method without arguments will empty the whole cookie jar.
|
||||
* If given a $domain argument only cookies belonging to that domain will
|
||||
* be removed. If given a $domain and $path argument, cookies belonging to
|
||||
* the specified path within that domain are removed. If given all three
|
||||
* arguments, then the cookie with the specified name, path and domain is
|
||||
* removed.
|
||||
*
|
||||
* @param string $domain Clears cookies matching a domain
|
||||
* @param string $path Clears cookies matching a domain and path
|
||||
* @param string $name Clears cookies matching a domain, path, and name
|
||||
*
|
||||
* @return CookieJarInterface
|
||||
*/
|
||||
public function clear($domain = null, $path = null, $name = null);
|
||||
|
||||
/**
|
||||
* Discard all sessions cookies.
|
||||
*
|
||||
* Removes cookies that don't have an expire field or a have a discard
|
||||
* field set to true. To be called when the user agent shuts down according
|
||||
* to RFC 2965.
|
||||
*/
|
||||
public function clearSessionCookies();
|
||||
}
|
86
vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
vendored
Normal file
86
vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Cookie;
|
||||
|
||||
use GuzzleHttp\Utils;
|
||||
|
||||
/**
|
||||
* Persists non-session cookies using a JSON formatted file
|
||||
*/
|
||||
class FileCookieJar extends CookieJar
|
||||
{
|
||||
/** @var string filename */
|
||||
private $filename;
|
||||
|
||||
/**
|
||||
* Create a new FileCookieJar object
|
||||
*
|
||||
* @param string $cookieFile File to store the cookie data
|
||||
*
|
||||
* @throws \RuntimeException if the file cannot be found or created
|
||||
*/
|
||||
public function __construct($cookieFile)
|
||||
{
|
||||
$this->filename = $cookieFile;
|
||||
|
||||
if (file_exists($cookieFile)) {
|
||||
$this->load($cookieFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the file when shutting down
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->save($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the cookies to a file.
|
||||
*
|
||||
* @param string $filename File to save
|
||||
* @throws \RuntimeException if the file cannot be found or created
|
||||
*/
|
||||
public function save($filename)
|
||||
{
|
||||
$json = [];
|
||||
foreach ($this as $cookie) {
|
||||
if ($cookie->getExpires() && !$cookie->getDiscard()) {
|
||||
$json[] = $cookie->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
if (false === file_put_contents($filename, json_encode($json))) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new \RuntimeException("Unable to save file {$filename}");
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load cookies from a JSON formatted file.
|
||||
*
|
||||
* Old cookies are kept unless overwritten by newly loaded ones.
|
||||
*
|
||||
* @param string $filename Cookie file to load.
|
||||
* @throws \RuntimeException if the file cannot be loaded.
|
||||
*/
|
||||
public function load($filename)
|
||||
{
|
||||
$json = file_get_contents($filename);
|
||||
if (false === $json) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new \RuntimeException("Unable to load file {$filename}");
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$data = Utils::jsonDecode($json, true);
|
||||
if (is_array($data)) {
|
||||
foreach (Utils::jsonDecode($json, true) as $cookie) {
|
||||
$this->setCookie(new SetCookie($cookie));
|
||||
}
|
||||
} elseif (strlen($data)) {
|
||||
throw new \RuntimeException("Invalid cookie file: {$filename}");
|
||||
}
|
||||
}
|
||||
}
|
66
vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
vendored
Normal file
66
vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Cookie;
|
||||
|
||||
use GuzzleHttp\Utils;
|
||||
|
||||
/**
|
||||
* Persists cookies in the client session
|
||||
*/
|
||||
class SessionCookieJar extends CookieJar
|
||||
{
|
||||
/** @var string session key */
|
||||
private $sessionKey;
|
||||
|
||||
/**
|
||||
* Create a new SessionCookieJar object
|
||||
*
|
||||
* @param string $sessionKey Session key name to store the cookie data in session
|
||||
*/
|
||||
public function __construct($sessionKey)
|
||||
{
|
||||
$this->sessionKey = $sessionKey;
|
||||
$this->load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves cookies to session when shutting down
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save cookies to the client session
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$json = [];
|
||||
foreach ($this as $cookie) {
|
||||
if ($cookie->getExpires() && !$cookie->getDiscard()) {
|
||||
$json[] = $cookie->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
$_SESSION[$this->sessionKey] = json_encode($json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the contents of the client session into the data array
|
||||
*/
|
||||
protected function load()
|
||||
{
|
||||
$cookieJar = isset($_SESSION[$this->sessionKey])
|
||||
? $_SESSION[$this->sessionKey]
|
||||
: null;
|
||||
|
||||
$data = Utils::jsonDecode($cookieJar, true);
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $cookie) {
|
||||
$this->setCookie(new SetCookie($cookie));
|
||||
}
|
||||
} elseif (strlen($data)) {
|
||||
throw new \RuntimeException("Invalid cookie data");
|
||||
}
|
||||
}
|
||||
}
|
373
vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
vendored
Normal file
373
vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
vendored
Normal file
@ -0,0 +1,373 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Cookie;
|
||||
|
||||
use GuzzleHttp\ToArrayInterface;
|
||||
|
||||
/**
|
||||
* Set-Cookie object
|
||||
*/
|
||||
class SetCookie implements ToArrayInterface
|
||||
{
|
||||
/** @var array */
|
||||
private static $defaults = [
|
||||
'Name' => null,
|
||||
'Value' => null,
|
||||
'Domain' => null,
|
||||
'Path' => '/',
|
||||
'Max-Age' => null,
|
||||
'Expires' => null,
|
||||
'Secure' => false,
|
||||
'Discard' => false,
|
||||
'HttpOnly' => false
|
||||
];
|
||||
|
||||
/** @var array Cookie data */
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* Create a new SetCookie object from a string
|
||||
*
|
||||
* @param string $cookie Set-Cookie header string
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function fromString($cookie)
|
||||
{
|
||||
// Create the default return array
|
||||
$data = self::$defaults;
|
||||
// Explode the cookie string using a series of semicolons
|
||||
$pieces = array_filter(array_map('trim', explode(';', $cookie)));
|
||||
// The name of the cookie (first kvp) must include an equal sign.
|
||||
if (empty($pieces) || !strpos($pieces[0], '=')) {
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
// Add the cookie pieces into the parsed data array
|
||||
foreach ($pieces as $part) {
|
||||
|
||||
$cookieParts = explode('=', $part, 2);
|
||||
$key = trim($cookieParts[0]);
|
||||
$value = isset($cookieParts[1])
|
||||
? trim($cookieParts[1], " \n\r\t\0\x0B\"")
|
||||
: true;
|
||||
|
||||
// Only check for non-cookies when cookies have been found
|
||||
if (empty($data['Name'])) {
|
||||
$data['Name'] = $key;
|
||||
$data['Value'] = $value;
|
||||
} else {
|
||||
foreach (array_keys(self::$defaults) as $search) {
|
||||
if (!strcasecmp($search, $key)) {
|
||||
$data[$search] = $value;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$data[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data Array of cookie data provided by a Cookie parser
|
||||
*/
|
||||
public function __construct(array $data = [])
|
||||
{
|
||||
$this->data = array_replace(self::$defaults, $data);
|
||||
// Extract the Expires value and turn it into a UNIX timestamp if needed
|
||||
if (!$this->getExpires() && $this->getMaxAge()) {
|
||||
// Calculate the Expires date
|
||||
$this->setExpires(time() + $this->getMaxAge());
|
||||
} elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
|
||||
$this->setExpires($this->getExpires());
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$str = $this->data['Name'] . '=' . $this->data['Value'] . '; ';
|
||||
foreach ($this->data as $k => $v) {
|
||||
if ($k != 'Name' && $k != 'Value' && $v !== null && $v !== false) {
|
||||
if ($k == 'Expires') {
|
||||
$str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; ';
|
||||
} else {
|
||||
$str .= ($v === true ? $k : "{$k}={$v}") . '; ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rtrim($str, '; ');
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cookie name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->data['Name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cookie name
|
||||
*
|
||||
* @param string $name Cookie name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->data['Name'] = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cookie value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->data['Value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cookie value
|
||||
*
|
||||
* @param string $value Cookie value
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->data['Value'] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the domain
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDomain()
|
||||
{
|
||||
return $this->data['Domain'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the domain of the cookie
|
||||
*
|
||||
* @param string $domain
|
||||
*/
|
||||
public function setDomain($domain)
|
||||
{
|
||||
$this->data['Domain'] = $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->data['Path'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path of the cookie
|
||||
*
|
||||
* @param string $path Path of the cookie
|
||||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
$this->data['Path'] = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum lifetime of the cookie in seconds
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getMaxAge()
|
||||
{
|
||||
return $this->data['Max-Age'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max-age of the cookie
|
||||
*
|
||||
* @param int $maxAge Max age of the cookie in seconds
|
||||
*/
|
||||
public function setMaxAge($maxAge)
|
||||
{
|
||||
$this->data['Max-Age'] = $maxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* The UNIX timestamp when the cookie Expires
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getExpires()
|
||||
{
|
||||
return $this->data['Expires'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the unix timestamp for which the cookie will expire
|
||||
*
|
||||
* @param int $timestamp Unix timestamp
|
||||
*/
|
||||
public function setExpires($timestamp)
|
||||
{
|
||||
$this->data['Expires'] = is_numeric($timestamp)
|
||||
? (int) $timestamp
|
||||
: strtotime($timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether or not this is a secure cookie
|
||||
*
|
||||
* @return null|bool
|
||||
*/
|
||||
public function getSecure()
|
||||
{
|
||||
return $this->data['Secure'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not the cookie is secure
|
||||
*
|
||||
* @param bool $secure Set to true or false if secure
|
||||
*/
|
||||
public function setSecure($secure)
|
||||
{
|
||||
$this->data['Secure'] = $secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether or not this is a session cookie
|
||||
*
|
||||
* @return null|bool
|
||||
*/
|
||||
public function getDiscard()
|
||||
{
|
||||
return $this->data['Discard'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not this is a session cookie
|
||||
*
|
||||
* @param bool $discard Set to true or false if this is a session cookie
|
||||
*/
|
||||
public function setDiscard($discard)
|
||||
{
|
||||
$this->data['Discard'] = $discard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether or not this is an HTTP only cookie
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getHttpOnly()
|
||||
{
|
||||
return $this->data['HttpOnly'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not this is an HTTP only cookie
|
||||
*
|
||||
* @param bool $httpOnly Set to true or false if this is HTTP only
|
||||
*/
|
||||
public function setHttpOnly($httpOnly)
|
||||
{
|
||||
$this->data['HttpOnly'] = $httpOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cookie matches a path value
|
||||
*
|
||||
* @param string $path Path to check against
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function matchesPath($path)
|
||||
{
|
||||
return !$this->getPath() || 0 === stripos($path, $this->getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cookie matches a domain value
|
||||
*
|
||||
* @param string $domain Domain to check against
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function matchesDomain($domain)
|
||||
{
|
||||
// Remove the leading '.' as per spec in RFC 6265.
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.2.3
|
||||
$cookieDomain = ltrim($this->getDomain(), '.');
|
||||
|
||||
// Domain not set or exact match.
|
||||
if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Matching the subdomain according to RFC 6265.
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.1.3
|
||||
if (filter_var($domain, FILTER_VALIDATE_IP)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/i', $domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cookie is expired
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired()
|
||||
{
|
||||
return $this->getExpires() && time() > $this->getExpires();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cookie is valid according to RFC 6265
|
||||
*
|
||||
* @return bool|string Returns true if valid or an error message if invalid
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
// Names must not be empty, but can be 0
|
||||
$name = $this->getName();
|
||||
if (empty($name) && !is_numeric($name)) {
|
||||
return 'The cookie name must not be empty';
|
||||
}
|
||||
|
||||
// Check if any of the invalid characters are present in the cookie name
|
||||
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
|
||||
return "Cookie name must not cannot invalid characters: =,; \\t\\r\\n\\013\\014";
|
||||
}
|
||||
|
||||
// Value must not be empty, but can be 0
|
||||
$value = $this->getValue();
|
||||
if (empty($value) && !is_numeric($value)) {
|
||||
return 'The cookie value must not be empty';
|
||||
}
|
||||
|
||||
// Domains must not be empty, but can be 0
|
||||
// A "0" is not a valid internet domain, but may be used as server name
|
||||
// in a private network.
|
||||
$domain = $this->getDomain();
|
||||
if (empty($domain) && !is_numeric($domain)) {
|
||||
return 'The cookie domain must not be empty';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
20
vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php
vendored
Normal file
20
vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Event;
|
||||
|
||||
/**
|
||||
* Basic event class that can be extended.
|
||||
*/
|
||||
abstract class AbstractEvent implements EventInterface
|
||||
{
|
||||
private $propagationStopped = false;
|
||||
|
||||
public function isPropagationStopped()
|
||||
{
|
||||
return $this->propagationStopped;
|
||||
}
|
||||
|
||||
public function stopPropagation()
|
||||
{
|
||||
$this->propagationStopped = true;
|
||||
}
|
||||
}
|
61
vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php
vendored
Normal file
61
vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Event;
|
||||
|
||||
use GuzzleHttp\Transaction;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Base class for request events, providing a request and client getter.
|
||||
*/
|
||||
abstract class AbstractRequestEvent extends AbstractEvent
|
||||
{
|
||||
/** @var Transaction */
|
||||
protected $transaction;
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
*/
|
||||
public function __construct(Transaction $transaction)
|
||||
{
|
||||
$this->transaction = $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTTP client associated with the event.
|
||||
*
|
||||
* @return ClientInterface
|
||||
*/
|
||||
public function getClient()
|
||||
{
|
||||
return $this->transaction->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request object
|
||||
*
|
||||
* @return RequestInterface
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->transaction->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of transaction retries.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRetryCount()
|
||||
{
|
||||
return $this->transaction->retries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Transaction
|
||||
*/
|
||||
public function getTransaction()
|
||||
{
|
||||
return $this->transaction;
|
||||
}
|
||||
}
|
40
vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php
vendored
Normal file
40
vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Event;
|
||||
|
||||
/**
|
||||
* Abstract request event that can be retried.
|
||||
*/
|
||||
class AbstractRetryableEvent extends AbstractTransferEvent
|
||||
{
|
||||
/**
|
||||
* Mark the request as needing a retry and stop event propagation.
|
||||
*
|
||||
* This action allows you to retry a request without emitting the "end"
|
||||
* event multiple times for a given request. When retried, the request
|
||||
* emits a before event and is then sent again using the client that sent
|
||||
* the original request.
|
||||
*
|
||||
* When retrying, it is important to limit the number of retries you allow
|
||||
* to prevent infinite loops.
|
||||
*
|
||||
* This action can only be taken during the "complete" and "error" events.
|
||||
*
|
||||
* @param int $afterDelay If specified, the amount of time in milliseconds
|
||||
* to delay before retrying. Note that this must
|
||||
* be supported by the underlying RingPHP handler
|
||||
* to work properly. Set to 0 or provide no value
|
||||
* to retry immediately.
|
||||
*/
|
||||
public function retry($afterDelay = 0)
|
||||
{
|
||||
// Setting the transition state to 'retry' will cause the next state
|
||||
// transition of the transaction to retry the request.
|
||||
$this->transaction->state = 'retry';
|
||||
|
||||
if ($afterDelay) {
|
||||
$this->transaction->request->getConfig()->set('delay', $afterDelay);
|
||||
}
|
||||
|
||||
$this->stopPropagation();
|
||||
}
|
||||
}
|
63
vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php
vendored
Normal file
63
vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Event;
|
||||
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
use GuzzleHttp\Ring\Future\FutureInterface;
|
||||
|
||||
/**
|
||||
* Event that contains transfer statistics, and can be intercepted.
|
||||
*/
|
||||
abstract class AbstractTransferEvent extends AbstractRequestEvent
|
||||
{
|
||||
/**
|
||||
* Get all transfer information as an associative array if no $name
|
||||
* argument is supplied, or gets a specific transfer statistic if
|
||||
* a $name attribute is supplied (e.g., 'total_time').
|
||||
*
|
||||
* @param string $name Name of the transfer stat to retrieve
|
||||
*
|
||||
* @return mixed|null|array
|
||||
*/
|
||||
public function getTransferInfo($name = null)
|
||||
{
|
||||
if (!$name) {
|
||||
return $this->transaction->transferInfo;
|
||||
}
|
||||
|
||||
return isset($this->transaction->transferInfo[$name])
|
||||
? $this->transaction->transferInfo[$name]
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true/false if a response is available.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasResponse()
|
||||
{
|
||||
return !($this->transaction->response instanceof FutureInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response.
|
||||
*
|
||||
* @return ResponseInterface|null
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->hasResponse() ? $this->transaction->response : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept the request and associate a response
|
||||
*
|
||||
* @param ResponseInterface $response Response to set
|
||||
*/
|
||||
public function intercept(ResponseInterface $response)
|
||||
{
|
||||
$this->transaction->response = $response;
|
||||
$this->transaction->exception = null;
|
||||
$this->stopPropagation();
|
||||
}
|
||||
}
|
26
vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php
vendored
Normal file
26
vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Event;
|
||||
|
||||
use GuzzleHttp\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Event object emitted before a request is sent.
|
||||
*
|
||||
* This event MAY be emitted multiple times (i.e., if a request is retried).
|
||||
* You MAY change the Response associated with the request using the
|
||||
* intercept() method of the event.
|
||||
*/
|
||||
class BeforeEvent extends AbstractRequestEvent
|
||||
{
|
||||
/**
|
||||
* Intercept the request and associate a response
|
||||
*
|
||||
* @param ResponseInterface $response Response to set
|
||||
*/
|
||||
public function intercept(ResponseInterface $response)
|
||||
{
|
||||
$this->transaction->response = $response;
|
||||
$this->transaction->exception = null;
|
||||
$this->stopPropagation();
|
||||
}
|
||||
}
|
14
vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php
vendored
Normal file
14
vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Event;
|
||||
|
||||
/**
|
||||
* Event object emitted after a request has been completed.
|
||||
*
|
||||
* This event MAY be emitted multiple times for a single request. You MAY
|
||||
* change the Response associated with the request using the intercept()
|
||||
* method of the event.
|
||||
*
|
||||
* This event allows the request to be retried if necessary using the retry()
|
||||
* method of the event.
|
||||
*/
|
||||
class CompleteEvent extends AbstractRetryableEvent {}
|
145
vendor/guzzlehttp/guzzle/src/Event/Emitter.php
vendored
Normal file
145
vendor/guzzlehttp/guzzle/src/Event/Emitter.php
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Event;
|
||||
|
||||
/**
|
||||
* Guzzle event emitter.
|
||||
*
|
||||
* Some of this class is based on the Symfony EventDispatcher component, which
|
||||
* ships with the following license:
|
||||
*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://github.com/symfony/symfony/tree/master/src/Symfony/Component/EventDispatcher
|
||||
*/
|
||||
class Emitter implements EmitterInterface
|
||||
{
|
||||
/** @var array */
|
||||
private $listeners = [];
|
||||
|
||||
/** @var array */
|
||||
private $sorted = [];
|
||||
|
||||
public function on($eventName, callable $listener, $priority = 0)
|
||||
{
|
||||
if ($priority === 'first') {
|
||||
$priority = isset($this->listeners[$eventName])
|
||||
? max(array_keys($this->listeners[$eventName])) + 1
|
||||
: 1;
|
||||
} elseif ($priority === 'last') {
|
||||
$priority = isset($this->listeners[$eventName])
|
||||
? min(array_keys($this->listeners[$eventName])) - 1
|
||||
: -1;
|
||||
}
|
||||
|
||||
$this->listeners[$eventName][$priority][] = $listener;
|
||||
unset($this->sorted[$eventName]);
|
||||
}
|
||||
|
||||
public function once($eventName, callable $listener, $priority = 0)
|
||||
{
|
||||
$onceListener = function (
|
||||
EventInterface $event
|
||||
) use (&$onceListener, $eventName, $listener, $priority) {
|
||||
$this->removeListener($eventName, $onceListener);
|
||||
$listener($event, $eventName);
|
||||
};
|
||||
|
||||
$this->on($eventName, $onceListener, $priority);
|
||||
}
|
||||
|
||||
public function removeListener($eventName, callable $listener)
|
||||
{
|
||||
if (empty($this->listeners[$eventName])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->listeners[$eventName] as $priority => $listeners) {
|
||||
if (false !== ($key = array_search($listener, $listeners, true))) {
|
||||
unset(
|
||||
$this->listeners[$eventName][$priority][$key],
|
||||
$this->sorted[$eventName]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function listeners($eventName = null)
|
||||
{
|
||||
// Return all events in a sorted priority order
|
||||
if ($eventName === null) {
|
||||
foreach (array_keys($this->listeners) as $eventName) {
|
||||
if (empty($this->sorted[$eventName])) {
|
||||
$this->listeners($eventName);
|
||||
}
|
||||
}
|
||||
return $this->sorted;
|
||||
}
|
||||
|
||||
// Return the listeners for a specific event, sorted in priority order
|
||||
if (empty($this->sorted[$eventName])) {
|
||||
$this->sorted[$eventName] = [];
|
||||
if (isset($this->listeners[$eventName])) {
|
||||
krsort($this->listeners[$eventName], SORT_NUMERIC);
|
||||
foreach ($this->listeners[$eventName] as $listeners) {
|
||||
foreach ($listeners as $listener) {
|
||||
$this->sorted[$eventName][] = $listener;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->sorted[$eventName];
|
||||
}
|
||||
|
||||
public function hasListeners($eventName)
|
||||
{
|
||||
return !empty($this->listeners[$eventName]);
|
||||
}
|
||||
|
||||
public function emit($eventName, EventInterface $event)
|
||||
{
|
||||
if (isset($this->listeners[$eventName])) {
|
||||
foreach ($this->listeners($eventName) as $listener) {
|
||||
$listener($event, $eventName);
|
||||
if ($event->isPropagationStopped()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
public function attach(SubscriberInterface $subscriber)
|
||||
{
|
||||
foreach ($subscriber->getEvents() as $eventName => $listeners) {
|
||||
if (is_array($listeners[0])) {
|
||||
foreach ($listeners as $listener) {
|
||||
$this->on(
|
||||
$eventName,
|
||||
[$subscriber, $listener[0]],
|
||||
isset($listener[1]) ? $listener[1] : 0
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$this->on(
|
||||
$eventName,
|
||||
[$subscriber, $listeners[0]],
|
||||
isset($listeners[1]) ? $listeners[1] : 0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function detach(SubscriberInterface $subscriber)
|
||||
{
|
||||
foreach ($subscriber->getEvents() as $eventName => $listener) {
|
||||
$this->removeListener($eventName, [$subscriber, $listener[0]]);
|
||||
}
|
||||
}
|
||||
}
|
96
vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php
vendored
Normal file
96
vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Event;
|
||||
|
||||
/**
|
||||
* Guzzle event emitter.
|
||||
*/
|
||||
interface EmitterInterface
|
||||
{
|
||||
/**
|
||||
* Binds a listener to a specific event.
|
||||
*
|
||||
* @param string $eventName Name of the event to bind to.
|
||||
* @param callable $listener Listener to invoke when triggered.
|
||||
* @param int|string $priority The higher this value, the earlier an event
|
||||
* listener will be triggered in the chain (defaults to 0). You can
|
||||
* pass "first" or "last" to dynamically specify the event priority
|
||||
* based on the current event priorities associated with the given
|
||||
* event name in the emitter. Use "first" to set the priority to the
|
||||
* current highest priority plus one. Use "last" to set the priority to
|
||||
* the current lowest event priority minus one.
|
||||
*/
|
||||
public function on($eventName, callable $listener, $priority = 0);
|
||||
|
||||
/**
|
||||
* Binds a listener to a specific event. After the listener is triggered
|
||||
* once, it is removed as a listener.
|
||||
*
|
||||
* @param string $eventName Name of the event to bind to.
|
||||
* @param callable $listener Listener to invoke when triggered.
|
||||
* @param int $priority The higher this value, the earlier an event
|
||||
* listener will be triggered in the chain (defaults to 0)
|
||||
*/
|
||||
public function once($eventName, callable $listener, $priority = 0);
|
||||
|
||||
/**
|
||||
* Removes an event listener from the specified event.
|
||||
*
|
||||
* @param string $eventName The event to remove a listener from
|
||||
* @param callable $listener The listener to remove
|
||||
*/
|
||||
public function removeListener($eventName, callable $listener);
|
||||
|
||||
/**
|
||||
* Gets the listeners of a specific event or all listeners if no event is
|
||||
* specified.
|
||||
*
|
||||
* @param string $eventName The name of the event. Pass null (the default)
|
||||
* to retrieve all listeners.
|
||||
*
|
||||
* @return array The event listeners for the specified event, or all event
|
||||
* listeners by event name. The format of the array when retrieving a
|
||||
* specific event list is an array of callables. The format of the array
|
||||
* when retrieving all listeners is an associative array of arrays of
|
||||
* callables.
|
||||
*/
|
||||
public function listeners($eventName = null);
|
||||
|
||||
/**
|
||||
* Checks if the emitter has listeners by the given name.
|
||||
*
|
||||
* @param string $eventName The name of the event to check.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasListeners($eventName);
|
||||
|
||||
/**
|
||||
* Emits an event to all registered listeners.
|
||||
*
|
||||
* Each event that is bound to the emitted eventName receives a
|
||||
* EventInterface, the name of the event, and the event emitter.
|
||||
*
|
||||
* @param string $eventName The name of the event to dispatch.
|
||||
* @param EventInterface $event The event to pass to the event handlers/listeners.
|
||||
*
|
||||
* @return EventInterface Returns the provided event object
|
||||
*/
|
||||
public function emit($eventName, EventInterface $event);
|
||||
|
||||
/**
|
||||
* Attaches an event subscriber.
|
||||
*
|
||||
* The subscriber is asked for all the events it is interested in and added
|
||||
* as an event listener for each event.
|
||||
*
|
||||
* @param SubscriberInterface $subscriber Subscriber to attach.
|
||||
*/
|
||||
public function attach(SubscriberInterface $subscriber);
|
||||
|
||||
/**
|
||||
* Detaches an event subscriber.
|
||||
*
|
||||
* @param SubscriberInterface $subscriber Subscriber to detach.
|
||||
*/
|
||||
public function detach(SubscriberInterface $subscriber);
|
||||
}
|
28
vendor/guzzlehttp/guzzle/src/Event/EndEvent.php
vendored
Normal file
28
vendor/guzzlehttp/guzzle/src/Event/EndEvent.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Event;
|
||||
|
||||
/**
|
||||
* A terminal event that is emitted when a request transaction has ended.
|
||||
*
|
||||
* This event is emitted for both successful responses and responses that
|
||||
* encountered an exception. You need to check if an exception is present
|
||||
* in your listener to know the difference.
|
||||
*
|
||||
* You MAY intercept the response associated with the event if needed, but keep
|
||||
* in mind that the "complete" event will not be triggered as a result.
|
||||
*/
|
||||
class EndEvent extends AbstractTransferEvent
|
||||
{
|
||||
/**
|
||||
* Get the exception that was encountered (if any).
|
||||
*
|
||||
* This method should be used to check if the request was sent successfully
|
||||
* or if it encountered errors.
|
||||
*
|
||||
* @return \Exception|null
|
||||
*/
|
||||
public function getException()
|
||||
{
|
||||
return $this->transaction->exception;
|
||||
}
|
||||
}
|
27
vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php
vendored
Normal file
27
vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Event;
|
||||
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
/**
|
||||
* Event emitted when an error occurs while sending a request.
|
||||
*
|
||||
* This event MAY be emitted multiple times. You MAY intercept the exception
|
||||
* and inject a response into the event to rescue the request using the
|
||||
* intercept() method of the event.
|
||||
*
|
||||
* This event allows the request to be retried using the "retry" method of the
|
||||
* event.
|
||||
*/
|
||||
class ErrorEvent extends AbstractRetryableEvent
|
||||
{
|
||||
/**
|
||||
* Get the exception that was encountered
|
||||
*
|
||||
* @return RequestException
|
||||
*/
|
||||
public function getException()
|
||||
{
|
||||
return $this->transaction->exception;
|
||||
}
|
||||
}
|
23
vendor/guzzlehttp/guzzle/src/Event/EventInterface.php
vendored
Normal file
23
vendor/guzzlehttp/guzzle/src/Event/EventInterface.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace GuzzleHttp\Event;
|
||||
|
||||
/**
|
||||
* Base event interface used when dispatching events to listeners using an
|
||||
* event emitter.
|
||||
*/
|
||||
interface EventInterface
|
||||
{
|
||||
/**
|
||||
* Returns whether or not stopPropagation was called on the event.
|
||||
*
|
||||
* @return bool
|
||||
* @see Event::stopPropagation
|
||||
*/
|
||||
public function isPropagationStopped();
|
||||
|
||||
/**
|
||||
* Stops the propagation of the event, preventing subsequent listeners
|
||||
* registered to the same event from being invoked.
|
||||
*/
|
||||
public function stopPropagation();
|
||||
}
|
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