mirror of
https://github.com/Icinga/icingaweb2-module-reactbundle.git
synced 2025-07-31 01:34:15 +02:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1e682710d9 | ||
|
649bdeaa76 | ||
|
62967011dc | ||
|
825f9e95c7 | ||
|
9ab4b2efed | ||
|
342a1d15fc | ||
|
b0c9f6d2b7 | ||
|
61986dd131 | ||
|
c699cdf689 | ||
|
71d4bfe08e | ||
|
a41978b574 | ||
|
40391095e3 | ||
|
f44e3d2bdb | ||
|
285c0b8d30 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,4 +1,3 @@
|
||||
.idea
|
||||
/vendor/
|
||||
/.idea/
|
||||
.*.sw[op]
|
||||
composer.lock
|
||||
vendor
|
||||
|
@ -1,4 +1,6 @@
|
||||
Copyright (c) 2012-2018 Ben Ramsey <ben@benramsey.com>
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2018 Icinga GmbH https://www.icinga.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
|
17
README.md
17
README.md
@ -1,3 +1,10 @@
|
||||
# DEPRECATED
|
||||
|
||||
The currently maintained version of this project can be found [here](https://github.com/Icinga/icinga-php-thirdparty).
|
||||
|
||||
💡 Some modules (e.g. the Director) still depend on this module. This module is still available for usage, so if you're on Icinga Web 2 v2.8.2 or lower please install v0.9.0.
|
||||
💡 However, if you're on Icinga Web 2 v2.9.0 or higher, this module is **not** required anymore. Unless you're running the Director in version 1.8.0 or lower, v1.8.1 also doesn't require this module.
|
||||
|
||||
Icinga Web 2 - ReactPHP-based 3rd party libraries
|
||||
=================================================
|
||||
|
||||
@ -6,16 +13,16 @@ for asynchronous PHP-based Icinga Web 2 modules. Please download the latest
|
||||
release and install it like any other module.
|
||||
|
||||
> **HINT**: Do NOT install the GIT master, it will not work! Checking out a
|
||||
> branch like `stable/0.5.0` or a tag like `v0.5.0` is fine.
|
||||
> branch like `stable/0.9.0` or a tag like `v0.9.0` is fine.
|
||||
|
||||
Sample Tarball installation
|
||||
---------------------------
|
||||
|
||||
```sh
|
||||
MODULE_NAME=reactbundle
|
||||
MODULE_VERSION=v0.5.0
|
||||
MODULE_VERSION=v0.9.0
|
||||
MODULES_PATH="/usr/share/icingaweb2/modules"
|
||||
MODULE_PATH="${MODULES_PATH}/${MODULE_PATH}"
|
||||
MODULE_PATH="${MODULES_PATH}/${MODULE_NAME}"
|
||||
RELEASES="https://github.com/Icinga/icingaweb2-module-${MODULE_NAME}/archive"
|
||||
mkdir "$MODULE_PATH" \
|
||||
&& wget -q $RELEASES/${MODULE_VERSION}.tar.gz -O - \
|
||||
@ -28,7 +35,7 @@ Sample GIT installation
|
||||
|
||||
```sh
|
||||
MODULE_NAME=reactbundle
|
||||
MODULE_VERSION=v0.5.0
|
||||
MODULE_VERSION=v0.9.0
|
||||
REPO="https://github.com/Icinga/icingaweb2-module-${MODULE_NAME}"
|
||||
MODULES_PATH="/usr/share/icingaweb2/modules"
|
||||
git clone ${REPO} "${MODULES_PATH}/${MODULE_NAME}" --branch "${MODULE_VERSION}"
|
||||
@ -48,4 +55,4 @@ Developer Documentation
|
||||
|
||||
e.g.
|
||||
|
||||
./bin/make-release.sh 0.5.0
|
||||
./bin/make-release.sh 0.9.0
|
||||
|
@ -8,6 +8,12 @@ if [[ -z $VERSION ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function fail {
|
||||
local msg="$1"
|
||||
echo "ERROR: $msg"
|
||||
exit 1
|
||||
}
|
||||
|
||||
TAG=$(git tag | grep -c "$VERSION")
|
||||
|
||||
if [[ "$TAG" -ne "0" ]]; then
|
||||
@ -20,22 +26,24 @@ BRANCH="stable/$VERSION"
|
||||
git checkout -b "$BRANCH"
|
||||
git rm -rf vendor
|
||||
rm -rf vendor
|
||||
rm composer.lock
|
||||
composer install
|
||||
rm -f composer.lock
|
||||
composer install || fail "composer install failed"
|
||||
find vendor/ -type f -name "*.php" \
|
||||
| grep -v '/examples/' \
|
||||
| grep -v '/example/' \
|
||||
| grep -v '/tests/' \
|
||||
| grep -v '/test/' \
|
||||
| xargs git add -f
|
||||
find vendor/ -type f -name LICENSE | xargs git add -f
|
||||
sed -i '' "s/^Version:.*/Version: v$VERSION/" module.info
|
||||
| xargs -L1 git add -f
|
||||
find vendor/ -type f -name LICENSE | xargs -L1 git add -f
|
||||
find vendor/ -type f -name '*.json' | xargs -L1 git add -f
|
||||
sed -i.bak "s/^Version:.*/Version: $VERSION/" module.info && rm -f module.info.bak
|
||||
git add module.info
|
||||
git add composer.lock -f
|
||||
git commit -m "Version v$VERSION"
|
||||
|
||||
rm -f composer.lock
|
||||
rm -rf vendor
|
||||
git checkout vendor
|
||||
composer validate --no-check-all --strict || fail "Composer validate failed"
|
||||
|
||||
git tag -a v$VERSION -m "Version v$VERSION"
|
||||
echo "Finished, tagged v$VERSION"
|
||||
|
@ -16,29 +16,33 @@
|
||||
"require": {
|
||||
"php": ">=5.6.3",
|
||||
"ext-curl": "*",
|
||||
"clue/block-react": "^1.3",
|
||||
"clue/buzz-react": "^2.5",
|
||||
"clue/block-react": "^1",
|
||||
"clue/buzz-react": "~2.7.0",
|
||||
"clue/connection-manager-extra": "^1.1",
|
||||
"clue/http-proxy-react": "^1.4",
|
||||
"clue/http-proxy-react": "^1",
|
||||
"clue/mq-react": "^1.1",
|
||||
"clue/redis-react": "^2.3",
|
||||
"clue/soap-react": "^1.0",
|
||||
"clue/socket-raw": "^1.4",
|
||||
"clue/socks-react": "^1.0",
|
||||
"clue/socks-react": "^1",
|
||||
"clue/stdio-react": "^2.3",
|
||||
"evenement/evenement": "^2",
|
||||
"predis/predis": "^1.1",
|
||||
"psr/http-message": "^1",
|
||||
"ramsey/uuid": "^3.8",
|
||||
"react/child-process": "^0.6",
|
||||
"react/datagram": "^1.4",
|
||||
"react/dns": "^0.4",
|
||||
"react/datagram": "^1.5",
|
||||
"react/dns": "^1",
|
||||
"react/event-loop": "^1.1",
|
||||
"react/http": "^0.8",
|
||||
"react/http": "^1",
|
||||
"react/http-client": "^0.5",
|
||||
"react/promise": "^2.7",
|
||||
"react/promise": "^2",
|
||||
"react/promise-stream": "^1",
|
||||
"react/promise-timer": "^1.5",
|
||||
"react/socket": "^1.2",
|
||||
"react/stream": "^1.1"
|
||||
"react/socket": "^1",
|
||||
"react/stream": "^1.1",
|
||||
"guzzlehttp/psr7": "^1.7",
|
||||
"guzzlehttp/guzzle": "^6.5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
}
|
||||
|
19
vendor/autoload.php
vendored
19
vendor/autoload.php
vendored
@ -1,7 +1,20 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
namespace Icinga\Module\Reactbundle {
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
use Icinga\Application\Hook\ApplicationStateHook;
|
||||
|
||||
return ComposerAutoloaderInit0ee91abee730dc4eb186583ca53653ff::getLoader();
|
||||
class ApplicationState extends ApplicationStateHook
|
||||
{
|
||||
public function collectMessages()
|
||||
{
|
||||
$this->addError(
|
||||
'reactbundle.master',
|
||||
time(),
|
||||
'Please install a Release version of the Reactbundle module, not the GIT master'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->provideHook('ApplicationState', '\\Icinga\\Module\\Reactbundle\\ApplicationState');
|
||||
}
|
||||
|
21
vendor/clue/block-react/LICENSE
vendored
21
vendor/clue/block-react/LICENSE
vendored
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Christian Lück
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
221
vendor/clue/block-react/src/functions.php
vendored
221
vendor/clue/block-react/src/functions.php
vendored
@ -1,221 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Block;
|
||||
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\Promise\PromiseInterface;
|
||||
use React\Promise\CancellablePromiseInterface;
|
||||
use UnderflowException;
|
||||
use Exception;
|
||||
use React\Promise;
|
||||
use React\Promise\Timer;
|
||||
use React\Promise\Timer\TimeoutException;
|
||||
|
||||
/**
|
||||
* wait/sleep for $time seconds
|
||||
*
|
||||
* The $time value will be used as a timer for the loop so that it keeps running
|
||||
* until the timeout triggers.
|
||||
* This implies that if you pass a really small (or negative) value, it will still
|
||||
* start a timer and will thus trigger at the earliest possible time in the future.
|
||||
*
|
||||
* @param float $time
|
||||
* @param LoopInterface $loop
|
||||
*/
|
||||
function sleep($time, LoopInterface $loop)
|
||||
{
|
||||
await(Timer\resolve($time, $loop), $loop);
|
||||
}
|
||||
|
||||
/**
|
||||
* block waiting for the given $promise to resolve
|
||||
*
|
||||
* Once the promise is resolved, this will return whatever the promise resolves to.
|
||||
*
|
||||
* Once the promise is rejected, this will throw whatever the promise rejected with.
|
||||
* If the promise did not reject with an `Exception`, then this function will
|
||||
* throw an `UnexpectedValueException` instead.
|
||||
*
|
||||
* If no $timeout is given and the promise stays pending, then this will
|
||||
* potentially wait/block forever until the promise is settled.
|
||||
*
|
||||
* If a $timeout is given and the promise is still pending once the timeout
|
||||
* triggers, this will cancel() the promise and throw a `TimeoutException`.
|
||||
* This implies that if you pass a really small (or negative) value, it will still
|
||||
* start a timer and will thus trigger at the earliest possible time in the future.
|
||||
*
|
||||
* @param PromiseInterface $promise
|
||||
* @param LoopInterface $loop
|
||||
* @param null|float $timeout (optional) maximum timeout in seconds or null=wait forever
|
||||
* @return mixed returns whatever the promise resolves to
|
||||
* @throws Exception when the promise is rejected
|
||||
* @throws TimeoutException if the $timeout is given and triggers
|
||||
*/
|
||||
function await(PromiseInterface $promise, LoopInterface $loop, $timeout = null)
|
||||
{
|
||||
$wait = true;
|
||||
$resolved = null;
|
||||
$exception = null;
|
||||
$rejected = false;
|
||||
|
||||
if ($timeout !== null) {
|
||||
$promise = Timer\timeout($promise, $timeout, $loop);
|
||||
}
|
||||
|
||||
$promise->then(
|
||||
function ($c) use (&$resolved, &$wait, $loop) {
|
||||
$resolved = $c;
|
||||
$wait = false;
|
||||
$loop->stop();
|
||||
},
|
||||
function ($error) use (&$exception, &$rejected, &$wait, $loop) {
|
||||
$exception = $error;
|
||||
$rejected = true;
|
||||
$wait = false;
|
||||
$loop->stop();
|
||||
}
|
||||
);
|
||||
|
||||
// Explicitly overwrite argument with null value. This ensure that this
|
||||
// argument does not show up in the stack trace in PHP 7+ only.
|
||||
$promise = null;
|
||||
|
||||
while ($wait) {
|
||||
$loop->run();
|
||||
}
|
||||
|
||||
if ($rejected) {
|
||||
if (!$exception instanceof \Exception) {
|
||||
$exception = new \UnexpectedValueException(
|
||||
'Promise rejected with unexpected value of type ' . (is_object(($exception) ? get_class($exception) : gettype($exception))),
|
||||
0,
|
||||
$exception instanceof \Throwable ? $exception : null
|
||||
);
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait for ANY of the given promises to resolve
|
||||
*
|
||||
* Once the first promise is resolved, this will try to cancel() all
|
||||
* remaining promises and return whatever the first promise resolves to.
|
||||
*
|
||||
* If ALL promises fail to resolve, this will fail and throw an Exception.
|
||||
*
|
||||
* If no $timeout is given and either promise stays pending, then this will
|
||||
* potentially wait/block forever until the first promise is settled.
|
||||
*
|
||||
* If a $timeout is given and either promise is still pending once the timeout
|
||||
* triggers, this will cancel() all pending promises and throw a `TimeoutException`.
|
||||
* This implies that if you pass a really small (or negative) value, it will still
|
||||
* start a timer and will thus trigger at the earliest possible time in the future.
|
||||
*
|
||||
* @param array $promises
|
||||
* @param LoopInterface $loop
|
||||
* @param null|float $timeout (optional) maximum timeout in seconds or null=wait forever
|
||||
* @return mixed returns whatever the first promise resolves to
|
||||
* @throws Exception if ALL promises are rejected
|
||||
* @throws TimeoutException if the $timeout is given and triggers
|
||||
*/
|
||||
function awaitAny(array $promises, LoopInterface $loop, $timeout = null)
|
||||
{
|
||||
// Explicitly overwrite argument with null value. This ensure that this
|
||||
// argument does not show up in the stack trace in PHP 7+ only.
|
||||
$all = $promises;
|
||||
$promises = null;
|
||||
|
||||
try {
|
||||
// Promise\any() does not cope with an empty input array, so reject this here
|
||||
if (!$all) {
|
||||
throw new UnderflowException('Empty input array');
|
||||
}
|
||||
|
||||
$ret = await(Promise\any($all)->then(null, function () {
|
||||
// rejects with an array of rejection reasons => reject with Exception instead
|
||||
throw new Exception('All promises rejected');
|
||||
}), $loop, $timeout);
|
||||
} catch (TimeoutException $e) {
|
||||
// the timeout fired
|
||||
// => try to cancel all promises (rejected ones will be ignored anyway)
|
||||
_cancelAllPromises($all);
|
||||
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
// if the above throws, then ALL promises are already rejected
|
||||
// => try to cancel all promises (rejected ones will be ignored anyway)
|
||||
_cancelAllPromises($all);
|
||||
|
||||
throw new UnderflowException('No promise could resolve', 0, $e);
|
||||
}
|
||||
|
||||
// if we reach this, then ANY of the given promises resolved
|
||||
// => try to cancel all promises (settled ones will be ignored anyway)
|
||||
_cancelAllPromises($all);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait for ALL of the given promises to resolve
|
||||
*
|
||||
* Once the last promise resolves, this will return an array with whatever
|
||||
* each promise resolves to. Array keys will be left intact, i.e. they can
|
||||
* be used to correlate the return array to the promises passed.
|
||||
*
|
||||
* If ANY promise fails to resolve, this will try to cancel() all
|
||||
* remaining promises and throw an Exception.
|
||||
* If the promise did not reject with an `Exception`, then this function will
|
||||
* throw an `UnexpectedValueException` instead.
|
||||
*
|
||||
* If no $timeout is given and either promise stays pending, then this will
|
||||
* potentially wait/block forever until the last promise is settled.
|
||||
*
|
||||
* If a $timeout is given and either promise is still pending once the timeout
|
||||
* triggers, this will cancel() all pending promises and throw a `TimeoutException`.
|
||||
* This implies that if you pass a really small (or negative) value, it will still
|
||||
* start a timer and will thus trigger at the earliest possible time in the future.
|
||||
*
|
||||
* @param array $promises
|
||||
* @param LoopInterface $loop
|
||||
* @param null|float $timeout (optional) maximum timeout in seconds or null=wait forever
|
||||
* @return array returns an array with whatever each promise resolves to
|
||||
* @throws Exception when ANY promise is rejected
|
||||
* @throws TimeoutException if the $timeout is given and triggers
|
||||
*/
|
||||
function awaitAll(array $promises, LoopInterface $loop, $timeout = null)
|
||||
{
|
||||
// Explicitly overwrite argument with null value. This ensure that this
|
||||
// argument does not show up in the stack trace in PHP 7+ only.
|
||||
$all = $promises;
|
||||
$promises = null;
|
||||
|
||||
try {
|
||||
return await(Promise\all($all), $loop, $timeout);
|
||||
} catch (Exception $e) {
|
||||
// ANY of the given promises rejected or the timeout fired
|
||||
// => try to cancel all promises (rejected ones will be ignored anyway)
|
||||
_cancelAllPromises($all);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* internal helper function used to iterate over an array of Promise instances and cancel() each
|
||||
*
|
||||
* @internal
|
||||
* @param array $promises
|
||||
*/
|
||||
function _cancelAllPromises(array $promises)
|
||||
{
|
||||
foreach ($promises as $promise) {
|
||||
if ($promise instanceof CancellablePromiseInterface) {
|
||||
$promise->cancel();
|
||||
}
|
||||
}
|
||||
}
|
21
vendor/clue/buzz-react/LICENSE
vendored
21
vendor/clue/buzz-react/LICENSE
vendored
@ -1,21 +0,0 @@
|
||||
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.
|
281
vendor/clue/buzz-react/src/Browser.php
vendored
281
vendor/clue/buzz-react/src/Browser.php
vendored
@ -1,281 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Buzz;
|
||||
|
||||
use Clue\React\Buzz\Io\Sender;
|
||||
use Clue\React\Buzz\Io\Transaction;
|
||||
use Clue\React\Buzz\Message\MessageFactory;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\Promise\PromiseInterface;
|
||||
use React\Socket\ConnectorInterface;
|
||||
use React\Stream\ReadableStreamInterface;
|
||||
|
||||
class Browser
|
||||
{
|
||||
private $transaction;
|
||||
private $messageFactory;
|
||||
private $baseUri = null;
|
||||
|
||||
/** @var LoopInterface $loop */
|
||||
private $loop;
|
||||
|
||||
/**
|
||||
* The `Browser` is responsible for sending HTTP requests to your HTTP server
|
||||
* and keeps track of pending incoming HTTP responses.
|
||||
* It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage).
|
||||
*
|
||||
* ```php
|
||||
* $loop = React\EventLoop\Factory::create();
|
||||
*
|
||||
* $browser = new Browser($loop);
|
||||
* ```
|
||||
*
|
||||
* If you need custom connector settings (DNS resolution, TLS parameters, timeouts,
|
||||
* proxy servers etc.), you can explicitly pass a custom instance of the
|
||||
* [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface):
|
||||
*
|
||||
* ```php
|
||||
* $connector = new \React\Socket\Connector($loop, array(
|
||||
* 'dns' => '127.0.0.1',
|
||||
* 'tcp' => array(
|
||||
* 'bindto' => '192.168.10.1:0'
|
||||
* ),
|
||||
* 'tls' => array(
|
||||
* 'verify_peer' => false,
|
||||
* 'verify_peer_name' => false
|
||||
* )
|
||||
* ));
|
||||
*
|
||||
* $browser = new Browser($loop, $connector);
|
||||
* ```
|
||||
*
|
||||
* @param LoopInterface $loop
|
||||
* @param ConnectorInterface|null $connector [optional] Connector to use.
|
||||
* Should be `null` in order to use default Connector.
|
||||
*/
|
||||
public function __construct(LoopInterface $loop, ConnectorInterface $connector = null)
|
||||
{
|
||||
$this->messageFactory = new MessageFactory();
|
||||
$this->transaction = new Transaction(
|
||||
Sender::createFromLoop($loop, $connector, $this->messageFactory),
|
||||
$this->messageFactory,
|
||||
$loop
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|UriInterface $url URI for the request.
|
||||
* @param array $headers
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function get($url, array $headers = array())
|
||||
{
|
||||
return $this->send($this->messageFactory->request('GET', $url, $headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|UriInterface $url URI for the request.
|
||||
* @param array $headers
|
||||
* @param string|ReadableStreamInterface $content
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function post($url, array $headers = array(), $content = '')
|
||||
{
|
||||
return $this->send($this->messageFactory->request('POST', $url, $headers, $content));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|UriInterface $url URI for the request.
|
||||
* @param array $headers
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function head($url, array $headers = array())
|
||||
{
|
||||
return $this->send($this->messageFactory->request('HEAD', $url, $headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|UriInterface $url URI for the request.
|
||||
* @param array $headers
|
||||
* @param string|ReadableStreamInterface $content
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function patch($url, array $headers = array(), $content = '')
|
||||
{
|
||||
return $this->send($this->messageFactory->request('PATCH', $url , $headers, $content));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|UriInterface $url URI for the request.
|
||||
* @param array $headers
|
||||
* @param string|ReadableStreamInterface $content
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function put($url, array $headers = array(), $content = '')
|
||||
{
|
||||
return $this->send($this->messageFactory->request('PUT', $url, $headers, $content));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|UriInterface $url URI for the request.
|
||||
* @param array $headers
|
||||
* @param string|ReadableStreamInterface $content
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function delete($url, array $headers = array(), $content = '')
|
||||
{
|
||||
return $this->send($this->messageFactory->request('DELETE', $url, $headers, $content));
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits an array of field values similar to submitting a form (`application/x-www-form-urlencoded`).
|
||||
*
|
||||
* ```php
|
||||
* $browser->submit($url, array('user' => 'test', 'password' => 'secret'));
|
||||
* ```
|
||||
*
|
||||
* @param string|UriInterface $url URI for the request.
|
||||
* @param array $fields
|
||||
* @param array $headers
|
||||
* @param string $method
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function submit($url, array $fields, $headers = array(), $method = 'POST')
|
||||
{
|
||||
$headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
$content = http_build_query($fields);
|
||||
|
||||
return $this->send($this->messageFactory->request($method, $url, $headers, $content));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an arbitrary instance implementing the [`RequestInterface`](#requestinterface) (PSR-7).
|
||||
*
|
||||
* All the above [predefined methods](#methods) default to sending requests as HTTP/1.0.
|
||||
* If you need a custom HTTP protocol method or version, then you may want to use this
|
||||
* method:
|
||||
*
|
||||
* ```php
|
||||
* $request = new Request('OPTIONS', $url);
|
||||
* $request = $request->withProtocolVersion('1.1');
|
||||
*
|
||||
* $browser->send($request)->then(…);
|
||||
* ```
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function send(RequestInterface $request)
|
||||
{
|
||||
if ($this->baseUri !== null) {
|
||||
// ensure we're actually below the base URI
|
||||
$request = $request->withUri($this->messageFactory->expandBase($request->getUri(), $this->baseUri));
|
||||
}
|
||||
|
||||
return $this->transaction->send($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the base URI used to resolve relative URIs to.
|
||||
*
|
||||
* ```php
|
||||
* $newBrowser = $browser->withBase('http://api.example.com/v3');
|
||||
* ```
|
||||
*
|
||||
* Notice that the [`Browser`](#browser) is an immutable object, i.e. the `withBase()` method
|
||||
* actually returns a *new* [`Browser`](#browser) instance with the given base URI applied.
|
||||
*
|
||||
* Any requests to relative URIs will then be processed by first prepending
|
||||
* the (absolute) base URI.
|
||||
* Please note that this merely prepends the base URI and does *not* resolve
|
||||
* any relative path references (like `../` etc.).
|
||||
* This is mostly useful for (RESTful) API calls where all endpoints (URIs)
|
||||
* are located under a common base URI scheme.
|
||||
*
|
||||
* ```php
|
||||
* // will request http://api.example.com/v3/example
|
||||
* $newBrowser->get('/example')->then(…);
|
||||
* ```
|
||||
*
|
||||
* By definition of this library, a given base URI MUST always absolute and
|
||||
* can not contain any placeholders.
|
||||
*
|
||||
* @param string|UriInterface $baseUri absolute base URI
|
||||
* @return self
|
||||
* @throws InvalidArgumentException if the given $baseUri is not a valid absolute URI
|
||||
* @see self::withoutBase()
|
||||
*/
|
||||
public function withBase($baseUri)
|
||||
{
|
||||
$browser = clone $this;
|
||||
$browser->baseUri = $this->messageFactory->uri($baseUri);
|
||||
|
||||
if ($browser->baseUri->getScheme() === '' || $browser->baseUri->getHost() === '') {
|
||||
throw new \InvalidArgumentException('Base URI must be absolute');
|
||||
}
|
||||
|
||||
return $browser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the base URI.
|
||||
*
|
||||
* ```php
|
||||
* $newBrowser = $browser->withoutBase();
|
||||
* ```
|
||||
*
|
||||
* Notice that the [`Browser`](#browser) is an immutable object, i.e. the `withoutBase()` method
|
||||
* actually returns a *new* [`Browser`](#browser) instance without any base URI applied.
|
||||
*
|
||||
* See also [`withBase()`](#withbase).
|
||||
*
|
||||
* @return self
|
||||
* @see self::withBase()
|
||||
*/
|
||||
public function withoutBase()
|
||||
{
|
||||
$browser = clone $this;
|
||||
$browser->baseUri = null;
|
||||
|
||||
return $browser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the [options](#options) to use:
|
||||
*
|
||||
* The [`Browser`](#browser) class exposes several options for the handling of
|
||||
* HTTP transactions. These options resemble some of PHP's
|
||||
* [HTTP context options](http://php.net/manual/en/context.http.php) and
|
||||
* can be controlled via the following API (and their defaults):
|
||||
*
|
||||
* ```php
|
||||
* $newBrowser = $browser->withOptions(array(
|
||||
* 'timeout' => null,
|
||||
* 'followRedirects' => true,
|
||||
* 'maxRedirects' => 10,
|
||||
* 'obeySuccessCode' => true,
|
||||
* 'streaming' => false,
|
||||
* ));
|
||||
* ```
|
||||
*
|
||||
* See also [timeouts](#timeouts), [redirects](#redirects) and
|
||||
* [streaming](#streaming) for more details.
|
||||
*
|
||||
* Notice that the [`Browser`](#browser) is an immutable object, i.e. this
|
||||
* method actually returns a *new* [`Browser`](#browser) instance with the
|
||||
* options applied.
|
||||
*
|
||||
* @param array $options
|
||||
* @return self
|
||||
*/
|
||||
public function withOptions(array $options)
|
||||
{
|
||||
$browser = clone $this;
|
||||
$browser->transaction = $this->transaction->withOptions($options);
|
||||
|
||||
return $browser;
|
||||
}
|
||||
}
|
152
vendor/clue/buzz-react/src/Io/Sender.php
vendored
152
vendor/clue/buzz-react/src/Io/Sender.php
vendored
@ -1,152 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Buzz\Io;
|
||||
|
||||
use Clue\React\Buzz\Message\MessageFactory;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\HttpClient\Client as HttpClient;
|
||||
use React\HttpClient\Response as ResponseStream;
|
||||
use React\Promise\PromiseInterface;
|
||||
use React\Promise\Deferred;
|
||||
use React\Socket\ConnectorInterface;
|
||||
use React\Stream\ReadableStreamInterface;
|
||||
|
||||
/**
|
||||
* [Internal] Sends requests and receives responses
|
||||
*
|
||||
* The `Sender` is responsible for passing the [`RequestInterface`](#requestinterface) objects to
|
||||
* the underlying [`HttpClient`](https://github.com/reactphp/http-client) library
|
||||
* and keeps track of its transmission and converts its reponses back to [`ResponseInterface`](#responseinterface) objects.
|
||||
*
|
||||
* It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage)
|
||||
* and the default [`Connector`](https://github.com/reactphp/socket-client) and [DNS `Resolver`](https://github.com/reactphp/dns).
|
||||
*
|
||||
* The `Sender` class mostly exists in order to abstract changes on the underlying
|
||||
* components away from this package in order to provide backwards and forwards
|
||||
* compatibility.
|
||||
*
|
||||
* @internal You SHOULD NOT rely on this API, it is subject to change without prior notice!
|
||||
* @see Browser
|
||||
*/
|
||||
class Sender
|
||||
{
|
||||
/**
|
||||
* create a new default sender attached to the given event loop
|
||||
*
|
||||
* This method is used internally to create the "default sender".
|
||||
*
|
||||
* You may also use this method if you need custom DNS or connector
|
||||
* settings. You can use this method manually like this:
|
||||
*
|
||||
* ```php
|
||||
* $connector = new \React\Socket\Connector($loop);
|
||||
* $sender = \Clue\React\Buzz\Io\Sender::createFromLoop($loop, $connector);
|
||||
* ```
|
||||
*
|
||||
* @param LoopInterface $loop
|
||||
* @param ConnectorInterface|null $connector
|
||||
* @return self
|
||||
*/
|
||||
public static function createFromLoop(LoopInterface $loop, ConnectorInterface $connector = null, MessageFactory $messageFactory)
|
||||
{
|
||||
return new self(new HttpClient($loop, $connector), $messageFactory);
|
||||
}
|
||||
|
||||
private $http;
|
||||
private $messageFactory;
|
||||
|
||||
/**
|
||||
* [internal] Instantiate Sender
|
||||
*
|
||||
* @param HttpClient $http
|
||||
* @internal
|
||||
*/
|
||||
public function __construct(HttpClient $http, MessageFactory $messageFactory)
|
||||
{
|
||||
$this->http = $http;
|
||||
$this->messageFactory = $messageFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @internal
|
||||
* @param RequestInterface $request
|
||||
* @return PromiseInterface Promise<ResponseInterface, Exception>
|
||||
*/
|
||||
public function send(RequestInterface $request)
|
||||
{
|
||||
$uri = $request->getUri();
|
||||
|
||||
// URIs are required to be absolute for the HttpClient to work
|
||||
if ($uri->getScheme() === '' || $uri->getHost() === '') {
|
||||
return \React\Promise\reject(new \InvalidArgumentException('Sending request requires absolute URI with scheme and host'));
|
||||
}
|
||||
|
||||
$body = $request->getBody();
|
||||
|
||||
// automatically assign a Content-Length header if the body size is known
|
||||
if ($body->getSize() !== null && $body->getSize() !== 0 && !$request->hasHeader('Content-Length')) {
|
||||
$request = $request->withHeader('Content-Length', (string)$body->getSize());
|
||||
}
|
||||
|
||||
if ($body instanceof ReadableStreamInterface && $body->isReadable() && !$request->hasHeader('Content-Length')) {
|
||||
$request = $request->withHeader('Transfer-Encoding', 'chunked');
|
||||
}
|
||||
|
||||
$headers = array();
|
||||
foreach ($request->getHeaders() as $name => $values) {
|
||||
$headers[$name] = implode(', ', $values);
|
||||
}
|
||||
|
||||
$requestStream = $this->http->request($request->getMethod(), (string)$uri, $headers, $request->getProtocolVersion());
|
||||
|
||||
$deferred = new Deferred(function ($_, $reject) use ($requestStream) {
|
||||
// close request stream if request is cancelled
|
||||
$reject(new \RuntimeException('Request cancelled'));
|
||||
$requestStream->close();
|
||||
});
|
||||
|
||||
$requestStream->on('error', function($error) use ($deferred) {
|
||||
$deferred->reject($error);
|
||||
});
|
||||
|
||||
$messageFactory = $this->messageFactory;
|
||||
$requestStream->on('response', function (ResponseStream $responseStream) use ($deferred, $messageFactory) {
|
||||
// apply response header values from response stream
|
||||
$deferred->resolve($messageFactory->response(
|
||||
$responseStream->getVersion(),
|
||||
$responseStream->getCode(),
|
||||
$responseStream->getReasonPhrase(),
|
||||
$responseStream->getHeaders(),
|
||||
$responseStream
|
||||
));
|
||||
});
|
||||
|
||||
if ($body instanceof ReadableStreamInterface) {
|
||||
if ($body->isReadable()) {
|
||||
if ($request->hasHeader('Content-Length')) {
|
||||
// length is known => just write to request
|
||||
$body->pipe($requestStream);
|
||||
} else {
|
||||
// length unknown => apply chunked transfer-encoding
|
||||
// this should be moved somewhere else obviously
|
||||
$body->on('data', function ($data) use ($requestStream) {
|
||||
$requestStream->write(dechex(strlen($data)) . "\r\n" . $data . "\r\n");
|
||||
});
|
||||
$body->on('end', function() use ($requestStream) {
|
||||
$requestStream->end("0\r\n\r\n");
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// stream is not readable => end request without body
|
||||
$requestStream->end();
|
||||
}
|
||||
} else {
|
||||
// body is fully buffered => write as one chunk
|
||||
$requestStream->end((string)$body);
|
||||
}
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
}
|
245
vendor/clue/buzz-react/src/Io/Transaction.php
vendored
245
vendor/clue/buzz-react/src/Io/Transaction.php
vendored
@ -1,245 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Buzz\Io;
|
||||
|
||||
use Clue\React\Buzz\Message\ResponseException;
|
||||
use Clue\React\Buzz\Message\MessageFactory;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\Promise\Deferred;
|
||||
use React\Promise\PromiseInterface;
|
||||
use React\Promise\Timer\TimeoutException;
|
||||
use React\Stream\ReadableStreamInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class Transaction
|
||||
{
|
||||
private $sender;
|
||||
private $messageFactory;
|
||||
private $loop;
|
||||
|
||||
// context: http.timeout (ini_get('default_socket_timeout'): 60)
|
||||
private $timeout;
|
||||
|
||||
// context: http.follow_location (true)
|
||||
private $followRedirects = true;
|
||||
|
||||
// context: http.max_redirects (10)
|
||||
private $maxRedirects = 10;
|
||||
|
||||
// context: http.ignore_errors (false)
|
||||
private $obeySuccessCode = true;
|
||||
|
||||
private $streaming = false;
|
||||
|
||||
public function __construct(Sender $sender, MessageFactory $messageFactory, LoopInterface $loop)
|
||||
{
|
||||
$this->sender = $sender;
|
||||
$this->messageFactory = $messageFactory;
|
||||
$this->loop = $loop;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return self returns new instance, without modifying existing instance
|
||||
*/
|
||||
public function withOptions(array $options)
|
||||
{
|
||||
$transaction = clone $this;
|
||||
foreach ($options as $name => $value) {
|
||||
if (property_exists($transaction, $name)) {
|
||||
// restore default value if null is given
|
||||
if ($value === null) {
|
||||
$default = new self($this->sender, $this->messageFactory, $this->loop);
|
||||
$value = $default->$name;
|
||||
}
|
||||
|
||||
$transaction->$name = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
public function send(RequestInterface $request)
|
||||
{
|
||||
$deferred = new Deferred(function () use (&$deferred) {
|
||||
if (isset($deferred->pending)) {
|
||||
$deferred->pending->cancel();
|
||||
unset($deferred->pending);
|
||||
}
|
||||
});
|
||||
|
||||
$deferred->numRequests = 0;
|
||||
|
||||
$this->next($request, $deferred)->then(
|
||||
array($deferred, 'resolve'),
|
||||
array($deferred, 'reject')
|
||||
);
|
||||
|
||||
// use timeout from options or default to PHP's default_socket_timeout (60)
|
||||
$timeout = (float)($this->timeout !== null ? $this->timeout : ini_get("default_socket_timeout"));
|
||||
if ($timeout < 0) {
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
return \React\Promise\Timer\timeout($deferred->promise(), $timeout, $this->loop)->then(null, function ($e) {
|
||||
if ($e instanceof TimeoutException) {
|
||||
throw new \RuntimeException(
|
||||
'Request timed out after ' . $e->getTimeout() . ' seconds'
|
||||
);
|
||||
}
|
||||
throw $e;
|
||||
});
|
||||
}
|
||||
|
||||
private function next(RequestInterface $request, Deferred $deferred)
|
||||
{
|
||||
$this->progress('request', array($request));
|
||||
|
||||
$that = $this;
|
||||
++$deferred->numRequests;
|
||||
|
||||
$promise = $this->sender->send($request);
|
||||
|
||||
if (!$this->streaming) {
|
||||
$promise = $promise->then(function ($response) use ($deferred, $that) {
|
||||
return $that->bufferResponse($response, $deferred);
|
||||
});
|
||||
}
|
||||
|
||||
$deferred->pending = $promise;
|
||||
|
||||
return $promise->then(
|
||||
function (ResponseInterface $response) use ($request, $that, $deferred) {
|
||||
return $that->onResponse($response, $request, $deferred);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param ResponseInterface $response
|
||||
* @return PromiseInterface Promise<ResponseInterface, Exception>
|
||||
*/
|
||||
public function bufferResponse(ResponseInterface $response, $deferred)
|
||||
{
|
||||
$stream = $response->getBody();
|
||||
|
||||
// body is not streaming => already buffered
|
||||
if (!$stream instanceof ReadableStreamInterface) {
|
||||
return \React\Promise\resolve($response);
|
||||
}
|
||||
|
||||
// buffer stream and resolve with buffered body
|
||||
$messageFactory = $this->messageFactory;
|
||||
$promise = \React\Promise\Stream\buffer($stream)->then(
|
||||
function ($body) use ($response, $messageFactory) {
|
||||
return $response->withBody($messageFactory->body($body));
|
||||
},
|
||||
function ($e) use ($stream) {
|
||||
// try to close stream if buffering fails (or is cancelled)
|
||||
$stream->close();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
);
|
||||
|
||||
$deferred->pending = $promise;
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param ResponseInterface $response
|
||||
* @param RequestInterface $request
|
||||
* @throws ResponseException
|
||||
* @return ResponseInterface|PromiseInterface
|
||||
*/
|
||||
public function onResponse(ResponseInterface $response, RequestInterface $request, $deferred)
|
||||
{
|
||||
$this->progress('response', array($response, $request));
|
||||
|
||||
if ($this->followRedirects && ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400)) {
|
||||
return $this->onResponseRedirect($response, $request, $deferred);
|
||||
}
|
||||
|
||||
// only status codes 200-399 are considered to be valid, reject otherwise
|
||||
if ($this->obeySuccessCode && ($response->getStatusCode() < 200 || $response->getStatusCode() >= 400)) {
|
||||
throw new ResponseException($response);
|
||||
}
|
||||
|
||||
// resolve our initial promise
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ResponseInterface $response
|
||||
* @param RequestInterface $request
|
||||
* @return PromiseInterface
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function onResponseRedirect(ResponseInterface $response, RequestInterface $request, Deferred $deferred)
|
||||
{
|
||||
// resolve location relative to last request URI
|
||||
$location = $this->messageFactory->uriRelative($request->getUri(), $response->getHeaderLine('Location'));
|
||||
|
||||
$request = $this->makeRedirectRequest($request, $location);
|
||||
$this->progress('redirect', array($request));
|
||||
|
||||
if ($deferred->numRequests >= $this->maxRedirects) {
|
||||
throw new \RuntimeException('Maximum number of redirects (' . $this->maxRedirects . ') exceeded');
|
||||
}
|
||||
|
||||
return $this->next($request, $deferred);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @param UriInterface $location
|
||||
* @return RequestInterface
|
||||
*/
|
||||
private function makeRedirectRequest(RequestInterface $request, UriInterface $location)
|
||||
{
|
||||
$originalHost = $request->getUri()->getHost();
|
||||
$request = $request
|
||||
->withoutHeader('Host')
|
||||
->withoutHeader('Content-Type')
|
||||
->withoutHeader('Content-Length');
|
||||
|
||||
// Remove authorization if changing hostnames (but not if just changing ports or protocols).
|
||||
if ($location->getHost() !== $originalHost) {
|
||||
$request = $request->withoutHeader('Authorization');
|
||||
}
|
||||
|
||||
// naïve approach..
|
||||
$method = ($request->getMethod() === 'HEAD') ? 'HEAD' : 'GET';
|
||||
|
||||
return $this->messageFactory->request($method, $location, $request->getHeaders());
|
||||
}
|
||||
|
||||
private function progress($name, array $args = array())
|
||||
{
|
||||
return;
|
||||
|
||||
echo $name;
|
||||
|
||||
foreach ($args as $arg) {
|
||||
echo ' ';
|
||||
if ($arg instanceof ResponseInterface) {
|
||||
echo 'HTTP/' . $arg->getProtocolVersion() . ' ' . $arg->getStatusCode() . ' ' . $arg->getReasonPhrase();
|
||||
} elseif ($arg instanceof RequestInterface) {
|
||||
echo $arg->getMethod() . ' ' . $arg->getRequestTarget() . ' HTTP/' . $arg->getProtocolVersion();
|
||||
} else {
|
||||
echo $arg;
|
||||
}
|
||||
}
|
||||
|
||||
echo PHP_EOL;
|
||||
}
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Buzz\Message;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use RingCentral\Psr7\Request;
|
||||
use RingCentral\Psr7\Response;
|
||||
use RingCentral\Psr7\Uri;
|
||||
use React\Stream\ReadableStreamInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class MessageFactory
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of RequestInterface for the given request parameters
|
||||
*
|
||||
* @param string $method
|
||||
* @param string|UriInterface $uri
|
||||
* @param array $headers
|
||||
* @param string|ReadableStreamInterface $content
|
||||
* @return Request
|
||||
*/
|
||||
public function request($method, $uri, $headers = array(), $content = '')
|
||||
{
|
||||
return new Request($method, $uri, $headers, $this->body($content), '1.0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of ResponseInterface for the given response parameters
|
||||
*
|
||||
* @param string $version
|
||||
* @param int $status
|
||||
* @param string $reason
|
||||
* @param array $headers
|
||||
* @param ReadableStreamInterface|string $body
|
||||
* @return Response
|
||||
* @uses self::body()
|
||||
*/
|
||||
public function response($version, $status, $reason, $headers = array(), $body = '')
|
||||
{
|
||||
return new Response($status, $headers, $this->body($body), $version, $reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of StreamInterface for the given body contents
|
||||
*
|
||||
* @param ReadableStreamInterface|string $body
|
||||
* @return StreamInterface
|
||||
*/
|
||||
public function body($body)
|
||||
{
|
||||
if ($body instanceof ReadableStreamInterface) {
|
||||
return new ReadableBodyStream($body);
|
||||
}
|
||||
|
||||
return \RingCentral\Psr7\stream_for($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of UriInterface for the given URI string
|
||||
*
|
||||
* @param string $uri
|
||||
* @return UriInterface
|
||||
*/
|
||||
public function uri($uri)
|
||||
{
|
||||
return new Uri($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of UriInterface for the given URI string relative to the given base URI
|
||||
*
|
||||
* @param UriInterface $base
|
||||
* @param string $uri
|
||||
* @return UriInterface
|
||||
*/
|
||||
public function uriRelative(UriInterface $base, $uri)
|
||||
{
|
||||
return Uri::resolve($base, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the given relative or absolute $uri by appending it behind $this base URI
|
||||
*
|
||||
* The given $uri parameter can be either a relative or absolute URI and
|
||||
* as such can not contain any URI template placeholders.
|
||||
*
|
||||
* As such, the outcome of this method represents a valid, absolute URI
|
||||
* which will be returned as an instance implementing `UriInterface`.
|
||||
*
|
||||
* If the given $uri is a relative URI, it will simply be appended behind $base URI.
|
||||
*
|
||||
* If the given $uri is an absolute URI, it will simply be verified to
|
||||
* be *below* the given $base URI.
|
||||
*
|
||||
* @param UriInterface $uri
|
||||
* @param UriInterface $base
|
||||
* @return UriInterface
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function expandBase(UriInterface $uri, UriInterface $base)
|
||||
{
|
||||
if ($uri->getScheme() !== '') {
|
||||
if (strpos((string)$uri, (string)$base) !== 0) {
|
||||
throw new \UnexpectedValueException('Invalid base, "' . $uri . '" does not appear to be below "' . $base . '"');
|
||||
}
|
||||
return $uri;
|
||||
}
|
||||
|
||||
$uri = (string)$uri;
|
||||
$base = (string)$base;
|
||||
|
||||
if ($uri !== '' && substr($base, -1) !== '/' && substr($uri, 0, 1) !== '?') {
|
||||
$base .= '/';
|
||||
}
|
||||
|
||||
if (isset($uri[0]) && $uri[0] === '/') {
|
||||
$uri = substr($uri, 1);
|
||||
}
|
||||
|
||||
return $this->uri($base . $uri);
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Buzz\Message;
|
||||
|
||||
use Evenement\EventEmitter;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use React\Stream\ReadableStreamInterface;
|
||||
use React\Stream\Util;
|
||||
use React\Stream\WritableStreamInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ReadableBodyStream extends EventEmitter implements ReadableStreamInterface, StreamInterface
|
||||
{
|
||||
private $input;
|
||||
private $closed = false;
|
||||
|
||||
public function __construct(ReadableStreamInterface $input)
|
||||
{
|
||||
$this->input = $input;
|
||||
|
||||
$that = $this;
|
||||
$input->on('data', function ($data) use ($that) {
|
||||
$that->emit('data', array($data));
|
||||
});
|
||||
$input->on('error', function ($error) use ($that) {
|
||||
$that->emit('error', array($error));
|
||||
$that->close();
|
||||
});
|
||||
$input->on('end', function () use ($that) {
|
||||
$that->emit('end');
|
||||
$that->close();
|
||||
});
|
||||
$input->on('close', array($that, 'close'));
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if (!$this->closed) {
|
||||
$this->closed = true;
|
||||
$this->input->close();
|
||||
|
||||
$this->emit('close');
|
||||
$this->removeAllListeners();
|
||||
}
|
||||
}
|
||||
|
||||
public function isReadable()
|
||||
{
|
||||
return $this->input->isReadable();
|
||||
}
|
||||
|
||||
public function pause()
|
||||
{
|
||||
$this->input->pause();
|
||||
}
|
||||
|
||||
public function resume()
|
||||
{
|
||||
$this->input->resume();
|
||||
}
|
||||
|
||||
public function pipe(WritableStreamInterface $dest, array $options = array())
|
||||
{
|
||||
Util::pipe($this, $dest, $options);
|
||||
|
||||
return $dest;
|
||||
}
|
||||
|
||||
public function eof()
|
||||
{
|
||||
return !$this->isReadable();
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function detach()
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function tell()
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function isSeekable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function rewind()
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function isWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function write($string)
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function read($length)
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function getContents()
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
return ($key === null) ? array() : null;
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Buzz\Message;
|
||||
|
||||
use RuntimeException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* The `ResponseException` is an `Exception` sub-class that will be used to reject
|
||||
* a request promise if the remote server returns a non-success status code
|
||||
* (anything but 2xx or 3xx).
|
||||
* You can control this behavior via the ["obeySuccessCode" option](#options).
|
||||
*
|
||||
* The `getCode(): int` method can be used to
|
||||
* return the HTTP response status code.
|
||||
*/
|
||||
class ResponseException extends RuntimeException
|
||||
{
|
||||
private $response;
|
||||
|
||||
public function __construct(ResponseInterface $response, $message = null, $code = null, $previous = null)
|
||||
{
|
||||
if ($message === null) {
|
||||
$message = 'HTTP status code ' . $response->getStatusCode() . ' (' . $response->getReasonPhrase() . ')';
|
||||
}
|
||||
if ($code === null) {
|
||||
$code = $response->getStatusCode();
|
||||
}
|
||||
parent::__construct($message, $code, $previous);
|
||||
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access its underlying [`ResponseInterface`](#responseinterface) object.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
}
|
21
vendor/clue/connection-manager-extra/LICENSE
vendored
21
vendor/clue/connection-manager-extra/LICENSE
vendored
@ -1,21 +0,0 @@
|
||||
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.
|
@ -1,30 +0,0 @@
|
||||
<?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);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
<?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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
<?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;
|
||||
});
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
<?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;
|
||||
});
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
<?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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace ConnectionManager\Extra\Multiple;
|
||||
|
||||
class ConnectionManagerRandom extends ConnectionManagerConsecutive
|
||||
{
|
||||
public function connect($uri)
|
||||
{
|
||||
$managers = $this->managers;
|
||||
shuffle($managers);
|
||||
|
||||
return $this->tryConnection($managers, $uri);
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
<?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
21
vendor/clue/http-proxy-react/LICENSE
vendored
@ -1,21 +0,0 @@
|
||||
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.
|
268
vendor/clue/http-proxy-react/src/ProxyConnector.php
vendored
268
vendor/clue/http-proxy-react/src/ProxyConnector.php
vendored
@ -1,268 +0,0 @@
|
||||
<?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 $headers = '';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param array $httpHeaders Custom HTTP headers to be sent to the proxy.
|
||||
* @throws InvalidArgumentException if the proxy URL is invalid
|
||||
*/
|
||||
public function __construct($proxyUrl, ConnectorInterface $connector, array $httpHeaders = array())
|
||||
{
|
||||
// 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->headers = 'Proxy-Authorization: Basic ' . base64_encode(
|
||||
rawurldecode($parts['user'] . ':' . (isset($parts['pass']) ? $parts['pass'] : ''))
|
||||
) . "\r\n";
|
||||
}
|
||||
|
||||
// append any additional custom request headers
|
||||
foreach ($httpHeaders as $name => $values) {
|
||||
foreach ((array)$values as $value) {
|
||||
$this->headers .= $name . ': ' . $value . "\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'));
|
||||
}
|
||||
|
||||
$target = $parts['host'] . ':' . $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'] = trim($parts['host'], '[]');
|
||||
}
|
||||
|
||||
// append query string
|
||||
$proxyUri .= '?' . http_build_query($args, '', '&');
|
||||
|
||||
// append fragment from URI if given
|
||||
if (isset($parts['fragment'])) {
|
||||
$proxyUri .= '#' . $parts['fragment'];
|
||||
}
|
||||
|
||||
$connecting = $this->connector->connect($proxyUri);
|
||||
|
||||
$deferred = new Deferred(function ($_, $reject) use ($connecting, $uri) {
|
||||
$reject(new RuntimeException(
|
||||
'Connection to ' . $uri . ' cancelled while waiting for proxy (ECONNABORTED)',
|
||||
defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103
|
||||
));
|
||||
|
||||
// either close active connection or cancel pending connection attempt
|
||||
$connecting->then(function (ConnectionInterface $stream) {
|
||||
$stream->close();
|
||||
});
|
||||
$connecting->cancel();
|
||||
});
|
||||
|
||||
$headers = $this->headers;
|
||||
$connecting->then(function (ConnectionInterface $stream) use ($target, $headers, $deferred, $uri) {
|
||||
// keep buffering data until headers are complete
|
||||
$buffer = '';
|
||||
$stream->on('data', $fn = function ($chunk) use (&$buffer, $deferred, $stream, &$fn, $uri) {
|
||||
$buffer .= $chunk;
|
||||
|
||||
$pos = strpos($buffer, "\r\n\r\n");
|
||||
if ($pos !== false) {
|
||||
// end of headers received => stop buffering
|
||||
$stream->removeListener('data', $fn);
|
||||
$fn = null;
|
||||
|
||||
// try to parse headers as response message
|
||||
try {
|
||||
$response = Psr7\parse_response(substr($buffer, 0, $pos));
|
||||
} catch (Exception $e) {
|
||||
$deferred->reject(new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy returned invalid response (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(
|
||||
'Connection to ' . $uri . ' failed because proxy denied access with HTTP error code ' . $response->getStatusCode() . ' (' . $response->getReasonPhrase() . ') (EACCES)',
|
||||
defined('SOCKET_EACCES') ? SOCKET_EACCES : 13
|
||||
));
|
||||
$stream->close();
|
||||
return;
|
||||
} elseif ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
|
||||
// map non-2xx status code to ECONNREFUSED
|
||||
$deferred->reject(new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy refused connection with HTTP error code ' . $response->getStatusCode() . ' (' . $response->getReasonPhrase() . ') (ECONNREFUSED)',
|
||||
defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
|
||||
));
|
||||
$stream->close();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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(
|
||||
'Connection to ' . $uri . ' failed because proxy response headers exceed maximum of 8 KiB (EMSGSIZE)',
|
||||
defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90
|
||||
));
|
||||
$stream->close();
|
||||
}
|
||||
});
|
||||
|
||||
$stream->on('error', function (Exception $e) use ($deferred, $uri) {
|
||||
$deferred->reject(new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because connection to proxy caused a stream error (EIO)',
|
||||
defined('SOCKET_EIO') ? SOCKET_EIO : 5,
|
||||
$e
|
||||
));
|
||||
});
|
||||
|
||||
$stream->on('close', function () use ($deferred, $uri) {
|
||||
$deferred->reject(new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because connection to proxy was lost while waiting for response (ECONNRESET)',
|
||||
defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 104
|
||||
));
|
||||
});
|
||||
|
||||
$stream->write("CONNECT " . $target . " HTTP/1.1\r\nHost: " . $target . "\r\n" . $headers . "\r\n");
|
||||
}, function (Exception $e) use ($deferred, $uri) {
|
||||
$deferred->reject($e = new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because connection to proxy failed (ECONNREFUSED)',
|
||||
defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111,
|
||||
$e
|
||||
));
|
||||
|
||||
// avoid garbage references by replacing all closures in call stack.
|
||||
// what a lovely piece of code!
|
||||
$r = new \ReflectionProperty('Exception', 'trace');
|
||||
$r->setAccessible(true);
|
||||
$trace = $r->getValue($e);
|
||||
foreach ($trace as &$one) {
|
||||
foreach ($one['args'] as &$arg) {
|
||||
if ($arg instanceof \Closure) {
|
||||
$arg = 'Object(' . get_class($arg) . ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
$r->setValue($e, $trace);
|
||||
});
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
}
|
21
vendor/clue/mq-react/LICENSE
vendored
21
vendor/clue/mq-react/LICENSE
vendored
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Christian Lück
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
331
vendor/clue/mq-react/src/Queue.php
vendored
331
vendor/clue/mq-react/src/Queue.php
vendored
@ -1,331 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Mq;
|
||||
|
||||
use React\Promise;
|
||||
use React\Promise\CancellablePromiseInterface;
|
||||
use React\Promise\Deferred;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* The `Queue` is responsible for managing your operations and ensuring not too
|
||||
* many operations are executed at once. It's a very simple and lightweight
|
||||
* in-memory implementation of the
|
||||
* [leaky bucket](https://en.wikipedia.org/wiki/Leaky_bucket#As_a_queue) algorithm.
|
||||
*
|
||||
* This means that you control how many operations can be executed concurrently.
|
||||
* If you add a job to the queue and it still below the limit, it will be executed
|
||||
* immediately. If you keep adding new jobs to the queue and its concurrency limit
|
||||
* is reached, it will not start a new operation and instead queue this for future
|
||||
* execution. Once one of the pending operations complete, it will pick the next
|
||||
* job from the qeueue and execute this operation.
|
||||
*/
|
||||
class Queue implements \Countable
|
||||
{
|
||||
private $concurrency;
|
||||
private $limit;
|
||||
private $handler;
|
||||
|
||||
private $pending = 0;
|
||||
private $queue = array();
|
||||
|
||||
/**
|
||||
* Concurrently process all given jobs through the given `$handler`.
|
||||
*
|
||||
* This is a convenience method which uses the `Queue` internally to
|
||||
* schedule all jobs while limiting concurrency to ensure no more than
|
||||
* `$concurrency` jobs ever run at once. It will return a promise which
|
||||
* resolves with the results of all jobs on success.
|
||||
*
|
||||
* ```php
|
||||
* $loop = React\EventLoop\Factory::create();
|
||||
* $browser = new Clue\React\Buzz\Browser($loop);
|
||||
*
|
||||
* $promise = Queue:all(3, $urls, function ($url) use ($browser) {
|
||||
* return $browser->get($url);
|
||||
* });
|
||||
*
|
||||
* $promise->then(function (array $responses) {
|
||||
* echo 'All ' . count($responses) . ' successful!' . PHP_EOL;
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* If either of the jobs fail, it will reject the resulting promise and will
|
||||
* try to cancel all outstanding jobs. Similarly, calling `cancel()` on the
|
||||
* resulting promise will try to cancel all outstanding jobs. See
|
||||
* [promises](#promises) and [cancellation](#cancellation) for details.
|
||||
*
|
||||
* The `$concurrency` parameter sets a new soft limit for the maximum number
|
||||
* of jobs to handle concurrently. Finding a good concurrency limit depends
|
||||
* on your particular use case. It's common to limit concurrency to a rather
|
||||
* small value, as doing more than a dozen of things at once may easily
|
||||
* overwhelm the receiving side. Using a `1` value will ensure that all jobs
|
||||
* are processed one after another, effectively creating a "waterfall" of
|
||||
* jobs. Using a value less than 1 will reject with an
|
||||
* `InvalidArgumentException` without processing any jobs.
|
||||
*
|
||||
* ```php
|
||||
* // handle up to 10 jobs concurrently
|
||||
* $promise = Queue:all(10, $jobs, $handler);
|
||||
* ```
|
||||
*
|
||||
* ```php
|
||||
* // handle each job after another without concurrency (waterfall)
|
||||
* $promise = Queue:all(1, $jobs, $handler);
|
||||
* ```
|
||||
*
|
||||
* The `$jobs` parameter must be an array with all jobs to process. Each
|
||||
* value in this array will be passed to the `$handler` to start one job.
|
||||
* The array keys will be preserved in the resulting array, while the array
|
||||
* values will be replaced with the job results as returned by the
|
||||
* `$handler`. If this array is empty, this method will resolve with an
|
||||
* empty array without processing any jobs.
|
||||
*
|
||||
* The `$handler` parameter must be a valid callable that accepts your job
|
||||
* parameters, invokes the appropriate operation and returns a Promise as a
|
||||
* placeholder for its future result. If the given argument is not a valid
|
||||
* callable, this method will reject with an `InvalidArgumentExceptionn`
|
||||
* without processing any jobs.
|
||||
*
|
||||
* ```php
|
||||
* // using a Closure as handler is usually recommended
|
||||
* $promise = Queue::all(10, $jobs, function ($url) use ($browser) {
|
||||
* return $browser->get($url);
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ```php
|
||||
* // accepts any callable, so PHP's array notation is also supported
|
||||
* $promise = Queue:all(10, $jobs, array($browser, 'get'));
|
||||
* ```
|
||||
*
|
||||
* > Keep in mind that returning an array of response messages means that
|
||||
* the whole response body has to be kept in memory.
|
||||
*
|
||||
* @param int $concurrency concurrency soft limit
|
||||
* @param array $jobs
|
||||
* @param callable $handler
|
||||
* @return PromiseInterface Returns a Promise<mixed[]> which resolves with an array of all resolution values
|
||||
* or rejects when any of the operations reject.
|
||||
*/
|
||||
public static function all($concurrency, array $jobs, $handler)
|
||||
{
|
||||
try {
|
||||
// limit number of concurrent operations
|
||||
$q = new self($concurrency, null, $handler);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
// reject if $concurrency or $handler is invalid
|
||||
return Promise\reject($e);
|
||||
}
|
||||
|
||||
// try invoking all operations and automatically queue excessive ones
|
||||
$promises = array_map($q, $jobs);
|
||||
|
||||
return new Promise\Promise(function ($resolve, $reject) use ($promises) {
|
||||
Promise\all($promises)->then($resolve, function ($e) use ($promises, $reject) {
|
||||
// cancel all pending promises if a single promise fails
|
||||
foreach (array_reverse($promises) as $promise) {
|
||||
if ($promise instanceof CancellablePromiseInterface) {
|
||||
$promise->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
// reject with original rejection message
|
||||
$reject($e);
|
||||
});
|
||||
}, function () use ($promises) {
|
||||
// cancel all pending promises on cancellation
|
||||
foreach (array_reverse($promises) as $promise) {
|
||||
if ($promise instanceof CancellablePromiseInterface) {
|
||||
$promise->cancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new queue object.
|
||||
*
|
||||
* You can create any number of queues, for example when you want to apply
|
||||
* different limits to different kind of operations.
|
||||
*
|
||||
* The `$concurrency` parameter sets a new soft limit for the maximum number
|
||||
* of jobs to handle concurrently. Finding a good concurrency limit depends
|
||||
* on your particular use case. It's common to limit concurrency to a rather
|
||||
* small value, as doing more than a dozen of things at once may easily
|
||||
* overwhelm the receiving side.
|
||||
*
|
||||
* The `$limit` parameter sets a new hard limit on how many jobs may be
|
||||
* outstanding (kept in memory) at once. Depending on your particular use
|
||||
* case, it's usually safe to keep a few hundreds or thousands of jobs in
|
||||
* memory. If you do not want to apply an upper limit, you can pass a `null`
|
||||
* value which is semantically more meaningful than passing a big number.
|
||||
*
|
||||
* ```php
|
||||
* // handle up to 10 jobs concurrently, but keep no more than 1000 in memory
|
||||
* $q = new Queue(10, 1000, $handler);
|
||||
* ```
|
||||
*
|
||||
* ```php
|
||||
* // handle up to 10 jobs concurrently, do not limit queue size
|
||||
* $q = new Queue(10, null, $handler);
|
||||
* ```
|
||||
*
|
||||
* ```php
|
||||
* // handle up to 10 jobs concurrently, reject all further jobs
|
||||
* $q = new Queue(10, 10, $handler);
|
||||
* ```
|
||||
*
|
||||
* The `$handler` parameter must be a valid callable that accepts your job
|
||||
* parameters, invokes the appropriate operation and returns a Promise as a
|
||||
* placeholder for its future result.
|
||||
*
|
||||
* ```php
|
||||
* // using a Closure as handler is usually recommended
|
||||
* $q = new Queue(10, null, function ($url) use ($browser) {
|
||||
* return $browser->get($url);
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ```php
|
||||
* // PHP's array callable as handler is also supported
|
||||
* $q = new Queue(10, null, array($browser, 'get'));
|
||||
* ```
|
||||
*
|
||||
* @param int $concurrency concurrency soft limit
|
||||
* @param int|null $limit queue hard limit or NULL=unlimited
|
||||
* @param callable $handler
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct($concurrency, $limit, $handler)
|
||||
{
|
||||
if ($concurrency < 1 || ($limit !== null && ($limit < 1 || $concurrency > $limit))) {
|
||||
throw new \InvalidArgumentException('Invalid limit given');
|
||||
}
|
||||
if (!is_callable($handler)) {
|
||||
throw new \InvalidArgumentException('Invalid handler given');
|
||||
}
|
||||
|
||||
$this->concurrency = $concurrency;
|
||||
$this->limit = $limit;
|
||||
$this->handler = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Queue instance is invokable, so that invoking `$q(...$args)` will
|
||||
* actually be forwarded as `$handler(...$args)` as given in the
|
||||
* `$handler` argument when concurrency is still below limits.
|
||||
*
|
||||
* Each operation may take some time to complete, but due to its async nature you
|
||||
* can actually start any number of (queued) operations. Once the concurrency limit
|
||||
* is reached, this invocation will simply be queued and this will return a pending
|
||||
* promise which will start the actual operation once another operation is
|
||||
* completed. This means that this is handled entirely transparently and you do not
|
||||
* need to worry about this concurrency limit yourself.
|
||||
*
|
||||
* @return \React\Promise\PromiseInterface
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
// happy path: simply invoke handler if we're below concurrency limit
|
||||
if ($this->pending < $this->concurrency) {
|
||||
++$this->pending;
|
||||
|
||||
// invoke handler and await its resolution before invoking next queued job
|
||||
return $this->await(
|
||||
call_user_func_array($this->handler, func_get_args())
|
||||
);
|
||||
}
|
||||
|
||||
// we're currently above concurreny limit, make sure we do not exceed maximum queue limit
|
||||
if ($this->limit !== null && $this->count() >= $this->limit) {
|
||||
return Promise\reject(new \OverflowException('Maximum queue limit of ' . $this->limit . ' exceeded'));
|
||||
}
|
||||
|
||||
// if we reach this point, then this job will need to be queued
|
||||
// get next queue position
|
||||
$queue =& $this->queue;
|
||||
$queue[] = null;
|
||||
end($queue);
|
||||
$id = key($queue);
|
||||
|
||||
$deferred = new Deferred(function ($_, $reject) use (&$queue, $id, &$deferred) {
|
||||
// forward cancellation to pending operation if it is currently executing
|
||||
if (isset($deferred->pending) && $deferred->pending instanceof CancellablePromiseInterface) {
|
||||
$deferred->pending->cancel();
|
||||
}
|
||||
unset($deferred->pending);
|
||||
|
||||
if (isset($deferred->args)) {
|
||||
// queued promise cancelled before its handler is invoked
|
||||
// remove from queue and reject explicitly
|
||||
unset($queue[$id], $deferred->args);
|
||||
$reject(new \RuntimeException('Cancelled queued job before processing started'));
|
||||
}
|
||||
});
|
||||
|
||||
// queue job to process if number of pending jobs is below concurrency limit again
|
||||
$deferred->args = func_get_args();
|
||||
$queue[$id] = $deferred;
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return $this->pending + count($this->queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function await(PromiseInterface $promise)
|
||||
{
|
||||
$that = $this;
|
||||
|
||||
return $promise->then(function ($result) use ($that) {
|
||||
$that->processQueue();
|
||||
|
||||
return $result;
|
||||
}, function ($error) use ($that) {
|
||||
$that->processQueue();
|
||||
|
||||
return Promise\reject($error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function processQueue()
|
||||
{
|
||||
// skip if we're still above concurrency limit or there's no queued job waiting
|
||||
if (--$this->pending >= $this->concurrency || !$this->queue) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* @var $deferred Deferred */
|
||||
$deferred = reset($this->queue);
|
||||
unset($this->queue[key($this->queue)]);
|
||||
|
||||
// once number of pending jobs is below concurrency limit again:
|
||||
// await this situation, invoke handler and await its resolution before invoking next queued job
|
||||
++$this->pending;
|
||||
|
||||
$promise = call_user_func_array($this->handler, $deferred->args);
|
||||
$deferred->pending = $promise;
|
||||
unset($deferred->args);
|
||||
|
||||
// invoke handler and await its resolution before invoking next queued job
|
||||
$this->await($promise)->then(
|
||||
function ($result) use ($deferred) {
|
||||
unset($deferred->pending);
|
||||
$deferred->resolve($result);
|
||||
},
|
||||
function ($e) use ($deferred) {
|
||||
unset($deferred->pending);
|
||||
$deferred->reject($e);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?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());
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?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);
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\Redis\Protocol\Parser;
|
||||
|
||||
use UnexpectedValueException;
|
||||
|
||||
class ParserException extends UnexpectedValueException
|
||||
{
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
<?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);
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -1,151 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
<?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
21
vendor/clue/redis-react/LICENSE
vendored
@ -1,21 +0,0 @@
|
||||
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.
|
54
vendor/clue/redis-react/src/Client.php
vendored
54
vendor/clue/redis-react/src/Client.php
vendored
@ -1,54 +0,0 @@
|
||||
<?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
|
||||
*
|
||||
* @return void
|
||||
* @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.
|
||||
*
|
||||
* @return void
|
||||
* @see self::end() for closing the connection once the client is idle
|
||||
*/
|
||||
public function close();
|
||||
}
|
203
vendor/clue/redis-react/src/Factory.php
vendored
203
vendor/clue/redis-react/src/Factory.php
vendored
@ -1,203 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Redis;
|
||||
|
||||
use Clue\Redis\Protocol\Factory as ProtocolFactory;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\Promise\Deferred;
|
||||
use React\Promise\Timer\TimeoutException;
|
||||
use React\Socket\ConnectionInterface;
|
||||
use React\Socket\Connector;
|
||||
use React\Socket\ConnectorInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Factory
|
||||
{
|
||||
private $loop;
|
||||
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->loop = $loop;
|
||||
$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<Client> resolves with Client or rejects with \Exception
|
||||
*/
|
||||
public function createClient($target)
|
||||
{
|
||||
try {
|
||||
$parts = $this->parseUrl($target);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
return \React\Promise\reject($e);
|
||||
}
|
||||
|
||||
$connecting = $this->connector->connect($parts['authority']);
|
||||
$deferred = new Deferred(function ($_, $reject) use ($connecting) {
|
||||
// connection cancelled, start with rejecting attempt, then clean up
|
||||
$reject(new \RuntimeException('Connection to Redis server cancelled'));
|
||||
|
||||
// either close successful connection or cancel pending connection attempt
|
||||
$connecting->then(function (ConnectionInterface $connection) {
|
||||
$connection->close();
|
||||
});
|
||||
$connecting->cancel();
|
||||
});
|
||||
|
||||
$protocol = $this->protocol;
|
||||
$promise = $connecting->then(function (ConnectionInterface $stream) use ($protocol) {
|
||||
return new StreamingClient($stream, $protocol->createResponseParser(), $protocol->createSerializer());
|
||||
}, function (\Exception $e) {
|
||||
throw new \RuntimeException(
|
||||
'Connection to Redis server failed because underlying transport connection failed',
|
||||
0,
|
||||
$e
|
||||
);
|
||||
});
|
||||
|
||||
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 new \RuntimeException(
|
||||
'Connection to Redis server failed because AUTH command failed',
|
||||
0,
|
||||
$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 new \RuntimeException(
|
||||
'Connection to Redis server failed because SELECT command failed',
|
||||
0,
|
||||
$error
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
$promise->then(array($deferred, 'resolve'), array($deferred, 'reject'));
|
||||
|
||||
// use timeout from explicit ?timeout=x parameter or default to PHP's default_socket_timeout (60)
|
||||
$timeout = (float) isset($parts['timeout']) ? $parts['timeout'] : ini_get("default_socket_timeout");
|
||||
if ($timeout < 0) {
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
return \React\Promise\Timer\timeout($deferred->promise(), $timeout, $this->loop)->then(null, function ($e) {
|
||||
if ($e instanceof TimeoutException) {
|
||||
throw new \RuntimeException(
|
||||
'Connection to Redis server timed out after ' . $e->getTimeout() . ' seconds'
|
||||
);
|
||||
}
|
||||
throw $e;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Redis client connected to address of given redis instance
|
||||
*
|
||||
* @param string $target
|
||||
* @return Client
|
||||
*/
|
||||
public function createLazyClient($target)
|
||||
{
|
||||
return new LazyClient($target, $this, $this->loop);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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'];
|
||||
}
|
||||
|
||||
if (isset($args['timeout'])) {
|
||||
$ret['timeout'] = $args['timeout'];
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
216
vendor/clue/redis-react/src/LazyClient.php
vendored
216
vendor/clue/redis-react/src/LazyClient.php
vendored
@ -1,216 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Redis;
|
||||
|
||||
use Evenement\EventEmitter;
|
||||
use React\Stream\Util;
|
||||
use React\EventLoop\LoopInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class LazyClient extends EventEmitter implements Client
|
||||
{
|
||||
private $target;
|
||||
/** @var Factory */
|
||||
private $factory;
|
||||
private $closed = false;
|
||||
private $promise;
|
||||
|
||||
private $loop;
|
||||
private $idlePeriod = 60.0;
|
||||
private $idleTimer;
|
||||
private $pending = 0;
|
||||
|
||||
private $subscribed = array();
|
||||
private $psubscribed = array();
|
||||
|
||||
/**
|
||||
* @param $target
|
||||
*/
|
||||
public function __construct($target, Factory $factory, LoopInterface $loop)
|
||||
{
|
||||
$args = array();
|
||||
\parse_str(\parse_url($target, \PHP_URL_QUERY), $args);
|
||||
if (isset($args['idle'])) {
|
||||
$this->idlePeriod = (float)$args['idle'];
|
||||
}
|
||||
|
||||
$this->target = $target;
|
||||
$this->factory = $factory;
|
||||
$this->loop = $loop;
|
||||
}
|
||||
|
||||
private function client()
|
||||
{
|
||||
if ($this->promise !== null) {
|
||||
return $this->promise;
|
||||
}
|
||||
|
||||
$self = $this;
|
||||
$pending =& $this->promise;
|
||||
$idleTimer=& $this->idleTimer;
|
||||
$subscribed =& $this->subscribed;
|
||||
$psubscribed =& $this->psubscribed;
|
||||
$loop = $this->loop;
|
||||
return $pending = $this->factory->createClient($this->target)->then(function (Client $client) use ($self, &$pending, &$idleTimer, &$subscribed, &$psubscribed, $loop) {
|
||||
// connection completed => remember only until closed
|
||||
$client->on('close', function () use (&$pending, $self, &$subscribed, &$psubscribed, &$idleTimer, $loop) {
|
||||
$pending = null;
|
||||
|
||||
// foward unsubscribe/punsubscribe events when underlying connection closes
|
||||
$n = count($subscribed);
|
||||
foreach ($subscribed as $channel => $_) {
|
||||
$self->emit('unsubscribe', array($channel, --$n));
|
||||
}
|
||||
$n = count($psubscribed);
|
||||
foreach ($psubscribed as $pattern => $_) {
|
||||
$self->emit('punsubscribe', array($pattern, --$n));
|
||||
}
|
||||
$subscribed = array();
|
||||
$psubscribed = array();
|
||||
|
||||
if ($idleTimer !== null) {
|
||||
$loop->cancelTimer($idleTimer);
|
||||
$idleTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
// keep track of all channels and patterns this connection is subscribed to
|
||||
$client->on('subscribe', function ($channel) use (&$subscribed) {
|
||||
$subscribed[$channel] = true;
|
||||
});
|
||||
$client->on('psubscribe', function ($pattern) use (&$psubscribed) {
|
||||
$psubscribed[$pattern] = true;
|
||||
});
|
||||
$client->on('unsubscribe', function ($channel) use (&$subscribed) {
|
||||
unset($subscribed[$channel]);
|
||||
});
|
||||
$client->on('punsubscribe', function ($pattern) use (&$psubscribed) {
|
||||
unset($psubscribed[$pattern]);
|
||||
});
|
||||
|
||||
Util::forwardEvents(
|
||||
$client,
|
||||
$self,
|
||||
array(
|
||||
'message',
|
||||
'subscribe',
|
||||
'unsubscribe',
|
||||
'pmessage',
|
||||
'psubscribe',
|
||||
'punsubscribe',
|
||||
)
|
||||
);
|
||||
|
||||
return $client;
|
||||
}, function (\Exception $e) use (&$pending) {
|
||||
// connection failed => discard connection attempt
|
||||
$pending = null;
|
||||
|
||||
throw $e;
|
||||
});
|
||||
}
|
||||
|
||||
public function __call($name, $args)
|
||||
{
|
||||
if ($this->closed) {
|
||||
return \React\Promise\reject(new \RuntimeException('Connection closed'));
|
||||
}
|
||||
|
||||
$that = $this;
|
||||
return $this->client()->then(function (Client $client) use ($name, $args, $that) {
|
||||
$that->awake();
|
||||
return \call_user_func_array(array($client, $name), $args)->then(
|
||||
function ($result) use ($that) {
|
||||
$that->idle();
|
||||
return $result;
|
||||
},
|
||||
function ($error) use ($that) {
|
||||
$that->idle();
|
||||
throw $error;
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public function end()
|
||||
{
|
||||
if ($this->promise === null) {
|
||||
$this->close();
|
||||
}
|
||||
|
||||
if ($this->closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$that = $this;
|
||||
return $this->client()->then(function (Client $client) use ($that) {
|
||||
$client->on('close', function () use ($that) {
|
||||
$that->close();
|
||||
});
|
||||
$client->end();
|
||||
});
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if ($this->closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->closed = true;
|
||||
|
||||
// either close active connection or cancel pending connection attempt
|
||||
if ($this->promise !== null) {
|
||||
$this->promise->then(function (Client $client) {
|
||||
$client->close();
|
||||
});
|
||||
if ($this->promise !== null) {
|
||||
$this->promise->cancel();
|
||||
$this->promise = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->idleTimer !== null) {
|
||||
$this->loop->cancelTimer($this->idleTimer);
|
||||
$this->idleTimer = null;
|
||||
}
|
||||
|
||||
$this->emit('close');
|
||||
$this->removeAllListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function awake()
|
||||
{
|
||||
++$this->pending;
|
||||
|
||||
if ($this->idleTimer !== null) {
|
||||
$this->loop->cancelTimer($this->idleTimer);
|
||||
$this->idleTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function idle()
|
||||
{
|
||||
--$this->pending;
|
||||
|
||||
if ($this->pending < 1 && $this->idlePeriod >= 0 && !$this->subscribed && !$this->psubscribed) {
|
||||
$idleTimer =& $this->idleTimer;
|
||||
$promise =& $this->promise;
|
||||
$idleTimer = $this->loop->addTimer($this->idlePeriod, function () use (&$idleTimer, &$promise) {
|
||||
$promise->then(function (Client $client) {
|
||||
$client->close();
|
||||
});
|
||||
$promise = null;
|
||||
$idleTimer = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
180
vendor/clue/redis-react/src/StreamingClient.php
vendored
180
vendor/clue/redis-react/src/StreamingClient.php
vendored
@ -1,180 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Redis;
|
||||
|
||||
use Evenement\EventEmitter;
|
||||
use Clue\Redis\Protocol\Parser\ParserInterface;
|
||||
use Clue\Redis\Protocol\Parser\ParserException;
|
||||
use Clue\Redis\Protocol\Serializer\SerializerInterface;
|
||||
use Clue\Redis\Protocol\Factory as ProtocolFactory;
|
||||
use UnderflowException;
|
||||
use RuntimeException;
|
||||
use InvalidArgumentException;
|
||||
use React\Promise\Deferred;
|
||||
use Clue\Redis\Protocol\Model\ErrorReply;
|
||||
use Clue\Redis\Protocol\Model\ModelInterface;
|
||||
use Clue\Redis\Protocol\Model\MultiBulkReply;
|
||||
use React\Stream\DuplexStreamInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class StreamingClient extends EventEmitter implements Client
|
||||
{
|
||||
private $stream;
|
||||
private $parser;
|
||||
private $serializer;
|
||||
private $requests = array();
|
||||
private $ending = false;
|
||||
private $closed = false;
|
||||
|
||||
private $subscribed = 0;
|
||||
private $psubscribed = 0;
|
||||
|
||||
public function __construct(DuplexStreamInterface $stream, ParserInterface $parser = null, SerializerInterface $serializer = null)
|
||||
{
|
||||
if ($parser === null || $serializer === null) {
|
||||
$factory = new ProtocolFactory();
|
||||
if ($parser === null) {
|
||||
$parser = $factory->createResponseParser();
|
||||
}
|
||||
if ($serializer === null) {
|
||||
$serializer = $factory->createSerializer();
|
||||
}
|
||||
}
|
||||
|
||||
$that = $this;
|
||||
$stream->on('data', function($chunk) use ($parser, $that) {
|
||||
try {
|
||||
$models = $parser->pushIncoming($chunk);
|
||||
}
|
||||
catch (ParserException $error) {
|
||||
$that->emit('error', array($error));
|
||||
$that->close();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($models as $data) {
|
||||
try {
|
||||
$that->handleMessage($data);
|
||||
}
|
||||
catch (UnderflowException $error) {
|
||||
$that->emit('error', array($error));
|
||||
$that->close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$stream->on('close', array($this, 'close'));
|
||||
|
||||
$this->stream = $stream;
|
||||
$this->parser = $parser;
|
||||
$this->serializer = $serializer;
|
||||
}
|
||||
|
||||
public function __call($name, $args)
|
||||
{
|
||||
$request = new Deferred();
|
||||
$promise = $request->promise();
|
||||
|
||||
$name = strtolower($name);
|
||||
|
||||
// special (p)(un)subscribe commands only accept a single parameter and have custom response logic applied
|
||||
static $pubsubs = array('subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe');
|
||||
|
||||
if ($this->ending) {
|
||||
$request->reject(new RuntimeException('Connection closed'));
|
||||
} elseif (count($args) !== 1 && in_array($name, $pubsubs)) {
|
||||
$request->reject(new InvalidArgumentException('PubSub commands limited to single argument'));
|
||||
} elseif ($name === 'monitor') {
|
||||
$request->reject(new \BadMethodCallException('MONITOR command explicitly not supported'));
|
||||
} else {
|
||||
$this->stream->write($this->serializer->getRequestMessage($name, $args));
|
||||
$this->requests []= $request;
|
||||
}
|
||||
|
||||
if (in_array($name, $pubsubs)) {
|
||||
$that = $this;
|
||||
$subscribed =& $this->subscribed;
|
||||
$psubscribed =& $this->psubscribed;
|
||||
|
||||
$promise->then(function ($array) use ($that, &$subscribed, &$psubscribed) {
|
||||
$first = array_shift($array);
|
||||
|
||||
// (p)(un)subscribe messages are to be forwarded
|
||||
$that->emit($first, $array);
|
||||
|
||||
// remember number of (p)subscribe topics
|
||||
if ($first === 'subscribe' || $first === 'unsubscribe') {
|
||||
$subscribed = $array[1];
|
||||
} else {
|
||||
$psubscribed = $array[1];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
public function handleMessage(ModelInterface $message)
|
||||
{
|
||||
if (($this->subscribed !== 0 || $this->psubscribed !== 0) && $message instanceof MultiBulkReply) {
|
||||
$array = $message->getValueNative();
|
||||
$first = array_shift($array);
|
||||
|
||||
// pub/sub messages are to be forwarded and should not be processed as request responses
|
||||
if (in_array($first, array('message', 'pmessage'))) {
|
||||
$this->emit($first, $array);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->requests) {
|
||||
throw new UnderflowException('Unexpected reply received, no matching request found');
|
||||
}
|
||||
|
||||
$request = array_shift($this->requests);
|
||||
/* @var $request Deferred */
|
||||
|
||||
if ($message instanceof ErrorReply) {
|
||||
$request->reject($message);
|
||||
} else {
|
||||
$request->resolve($message->getValueNative());
|
||||
}
|
||||
|
||||
if ($this->ending && !$this->requests) {
|
||||
$this->close();
|
||||
}
|
||||
}
|
||||
|
||||
public function end()
|
||||
{
|
||||
$this->ending = true;
|
||||
|
||||
if (!$this->requests) {
|
||||
$this->close();
|
||||
}
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if ($this->closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->ending = true;
|
||||
$this->closed = true;
|
||||
|
||||
$this->stream->close();
|
||||
|
||||
$this->emit('close');
|
||||
|
||||
// reject all remaining requests in the queue
|
||||
while($this->requests) {
|
||||
$request = array_shift($this->requests);
|
||||
/* @var $request Request */
|
||||
$request->reject(new RuntimeException('Connection closing'));
|
||||
}
|
||||
}
|
||||
}
|
21
vendor/clue/soap-react/LICENSE
vendored
21
vendor/clue/soap-react/LICENSE
vendored
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Christian Lück
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
323
vendor/clue/soap-react/src/Client.php
vendored
323
vendor/clue/soap-react/src/Client.php
vendored
@ -1,323 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Soap;
|
||||
|
||||
use Clue\React\Buzz\Browser;
|
||||
use Clue\React\Soap\Protocol\ClientDecoder;
|
||||
use Clue\React\Soap\Protocol\ClientEncoder;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use React\Promise\Deferred;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* The `Client` class is responsible for communication with the remote SOAP
|
||||
* WebService server.
|
||||
*
|
||||
* It requires a [`Browser`](https://github.com/clue/reactphp-buzz#browser) object
|
||||
* bound to the main [`EventLoop`](https://github.com/reactphp/event-loop#usage)
|
||||
* in order to handle async requests, the WSDL file contents and an optional
|
||||
* array of SOAP options:
|
||||
*
|
||||
* ```php
|
||||
* $loop = React\EventLoop\Factory::create();
|
||||
* $browser = new Clue\React\Buzz\Browser($loop);
|
||||
*
|
||||
* $wsdl = '<?xml …';
|
||||
* $options = array();
|
||||
*
|
||||
* $client = new Client($browser, $wsdl, $options);
|
||||
* ```
|
||||
*
|
||||
* If you need custom connector settings (DNS resolution, TLS parameters, timeouts,
|
||||
* proxy servers etc.), you can explicitly pass a custom instance of the
|
||||
* [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface)
|
||||
* to the [`Browser`](https://github.com/clue/reactphp-buzz#browser) instance:
|
||||
*
|
||||
* ```php
|
||||
* $connector = new \React\Socket\Connector($loop, array(
|
||||
* 'dns' => '127.0.0.1',
|
||||
* 'tcp' => array(
|
||||
* 'bindto' => '192.168.10.1:0'
|
||||
* ),
|
||||
* 'tls' => array(
|
||||
* 'verify_peer' => false,
|
||||
* 'verify_peer_name' => false
|
||||
* )
|
||||
* ));
|
||||
*
|
||||
* $browser = new Browser($loop, $connector);
|
||||
* $client = new Client($browser, $wsdl);
|
||||
* ```
|
||||
*
|
||||
* The `Client` works similar to PHP's `SoapClient` (which it uses under the
|
||||
* hood), but leaves you the responsibility to load the WSDL file. This allows
|
||||
* you to use local WSDL files, WSDL files from a cache or the most common form,
|
||||
* downloading the WSDL file contents from an URL through the `Browser`:
|
||||
*
|
||||
* ```php
|
||||
* $browser = new Browser($loop);
|
||||
*
|
||||
* $browser->get($url)->then(
|
||||
* function (ResponseInterface $response) use ($browser) {
|
||||
* // WSDL file is ready, create client
|
||||
* $client = new Client($browser, (string)$response->getBody());
|
||||
*
|
||||
* // do something…
|
||||
* },
|
||||
* function (Exception $e) {
|
||||
* // an error occured while trying to download the WSDL
|
||||
* }
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* The `Client` constructor loads the given WSDL file contents into memory and
|
||||
* parses its definition. If the given WSDL file is invalid and can not be
|
||||
* parsed, this will throw a `SoapFault`:
|
||||
*
|
||||
* ```php
|
||||
* try {
|
||||
* $client = new Client($browser, $wsdl);
|
||||
* } catch (SoapFault $e) {
|
||||
* echo 'Error: ' . $e->getMessage() . PHP_EOL;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* > Note that if you have `ext-xdebug` loaded, this may halt with a fatal
|
||||
* error instead of throwing a `SoapFault`. It is not recommended to use this
|
||||
* extension in production, so this should only ever affect test environments.
|
||||
*
|
||||
* The `Client` constructor accepts an array of options. All given options will
|
||||
* be passed through to the underlying `SoapClient`. However, not all options
|
||||
* make sense in this async implementation and as such may not have the desired
|
||||
* effect. See also [`SoapClient`](http://php.net/manual/en/soapclient.soapclient.php)
|
||||
* documentation for more details.
|
||||
*
|
||||
* If working in WSDL mode, the `$options` parameter is optional. If working in
|
||||
* non-WSDL mode, the WSDL parameter must be set to `null` and the options
|
||||
* parameter must contain the `location` and `uri` options, where `location` is
|
||||
* the URL of the SOAP server to send the request to, and `uri` is the target
|
||||
* namespace of the SOAP service:
|
||||
*
|
||||
* ```php
|
||||
* $client = new Client($browser, null, array(
|
||||
* 'location' => 'http://example.com',
|
||||
* 'uri' => 'http://ping.example.com',
|
||||
* ));
|
||||
* ```
|
||||
*
|
||||
* Similarly, if working in WSDL mode, the `location` option can be used to
|
||||
* explicitly overwrite the URL of the SOAP server to send the request to:
|
||||
*
|
||||
* ```php
|
||||
* $client = new Client($browser, $wsdl, array(
|
||||
* 'location' => 'http://example.com'
|
||||
* ));
|
||||
* ```
|
||||
*
|
||||
* You can use the `soap_version` option to change from the default SOAP 1.1 to
|
||||
* use SOAP 1.2 instead:
|
||||
*
|
||||
* ```php
|
||||
* $client = new Client($browser, $wsdl, array(
|
||||
* 'soap_version' => SOAP_1_2
|
||||
* ));
|
||||
* ```
|
||||
*
|
||||
* You can use the `classmap` option to map certain WSDL types to PHP classes
|
||||
* like this:
|
||||
*
|
||||
* ```php
|
||||
* $client = new Client($browser, $wsdl, array(
|
||||
* 'classmap' => array(
|
||||
* 'getBankResponseType' => BankResponse::class
|
||||
* )
|
||||
* ));
|
||||
* ```
|
||||
*
|
||||
* The `proxy_host` option (and family) is not supported by this library. As an
|
||||
* alternative, you can configure the given `$browser` instance to use an
|
||||
* [HTTP proxy server](https://github.com/clue/reactphp-buzz#http-proxy).
|
||||
* If you find any other option is missing or not supported here, PRs are much
|
||||
* appreciated!
|
||||
*
|
||||
* All public methods of the `Client` are considered *advanced usage*.
|
||||
* If you want to call RPC functions, see below for the [`Proxy`](#proxy) class.
|
||||
*/
|
||||
class Client
|
||||
{
|
||||
private $browser;
|
||||
private $encoder;
|
||||
private $decoder;
|
||||
|
||||
/**
|
||||
* Instantiate a new SOAP client for the given WSDL contents.
|
||||
*
|
||||
* @param Browser $browser
|
||||
* @param string|null $wsdlContents
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(Browser $browser, $wsdlContents, array $options = array())
|
||||
{
|
||||
$wsdl = $wsdlContents !== null ? 'data://text/plain;base64,' . base64_encode($wsdlContents) : null;
|
||||
|
||||
// Accept HTTP responses with error status codes as valid responses.
|
||||
// This is done in order to process these error responses through the normal SOAP decoder.
|
||||
// Additionally, we explicitly limit number of redirects to zero because following redirects makes little sense
|
||||
// because it transforms the POST request to a GET one and hence loses the SOAP request body.
|
||||
$browser = $browser->withOptions(array(
|
||||
'obeySuccessCode' => false,
|
||||
'followRedirects' => true,
|
||||
'maxRedirects' => 0
|
||||
));
|
||||
|
||||
$this->browser = $browser;
|
||||
$this->encoder = new ClientEncoder($wsdl, $options);
|
||||
$this->decoder = new ClientDecoder($wsdl, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue the given function to be sent via SOAP and wait for a response from the remote web service.
|
||||
*
|
||||
* ```php
|
||||
* // advanced usage, see Proxy for recommended alternative
|
||||
* $promise = $client->soapCall('ping', array('hello', 42));
|
||||
* ```
|
||||
*
|
||||
* Note: This is considered *advanced usage*, you may want to look into using the [`Proxy`](#proxy) instead.
|
||||
*
|
||||
* ```php
|
||||
* $proxy = new Proxy($client);
|
||||
* $promise = $proxy->ping('hello', 42);
|
||||
* ```
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed[] $args
|
||||
* @return PromiseInterface Returns a Promise<mixed, Exception>
|
||||
*/
|
||||
public function soapCall($name, $args)
|
||||
{
|
||||
try {
|
||||
$request = $this->encoder->encode($name, $args);
|
||||
} catch (\Exception $e) {
|
||||
$deferred = new Deferred();
|
||||
$deferred->reject($e);
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
$decoder = $this->decoder;
|
||||
|
||||
return $this->browser->send($request)->then(
|
||||
function (ResponseInterface $response) use ($decoder, $name) {
|
||||
// HTTP response received => decode results for this function call
|
||||
return $decoder->decode($name, (string)$response->getBody());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of functions defined in the WSDL.
|
||||
*
|
||||
* It returns the equivalent of PHP's
|
||||
* [`SoapClient::__getFunctions()`](http://php.net/manual/en/soapclient.getfunctions.php).
|
||||
* In non-WSDL mode, this method returns `null`.
|
||||
*
|
||||
* @return string[]|null
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return $this->encoder->__getFunctions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of types defined in the WSDL.
|
||||
*
|
||||
* It returns the equivalent of PHP's
|
||||
* [`SoapClient::__getTypes()`](http://php.net/manual/en/soapclient.gettypes.php).
|
||||
* In non-WSDL mode, this method returns `null`.
|
||||
*
|
||||
* @return string[]|null
|
||||
*/
|
||||
public function getTypes()
|
||||
{
|
||||
return $this->encoder->__getTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location (URI) of the given webservice `$function`.
|
||||
*
|
||||
* Note that this is not to be confused with the WSDL file location.
|
||||
* A WSDL file can contain any number of function definitions.
|
||||
* It's very common that all of these functions use the same location definition.
|
||||
* However, technically each function can potentially use a different location.
|
||||
*
|
||||
* The `$function` parameter should be a string with the the SOAP function name.
|
||||
* See also [`getFunctions()`](#getfunctions) for a list of all available functions.
|
||||
*
|
||||
* ```php
|
||||
* assert('http://example.com/soap/service' === $client->getLocation('echo'));
|
||||
* ```
|
||||
*
|
||||
* For easier access, this function also accepts a numeric function index.
|
||||
* It then uses [`getFunctions()`](#getfunctions) internally to get the function
|
||||
* name for the given index.
|
||||
* This is particularly useful for the very common case where all functions use the
|
||||
* same location and accessing the first location is sufficient.
|
||||
*
|
||||
* ```php
|
||||
* assert('http://example.com/soap/service' === $client->getLocation(0));
|
||||
* ```
|
||||
*
|
||||
* When the `location` option has been set in the `Client` constructor
|
||||
* (such as when in non-WSDL mode) or via the `withLocation()` method, this
|
||||
* method returns the value of the given location.
|
||||
*
|
||||
* Passing a `$function` not defined in the WSDL file will throw a `SoapFault`.
|
||||
*
|
||||
* @param string|int $function
|
||||
* @return string
|
||||
* @throws \SoapFault if given function does not exist
|
||||
* @see self::getFunctions()
|
||||
*/
|
||||
public function getLocation($function)
|
||||
{
|
||||
if (is_int($function)) {
|
||||
$functions = $this->getFunctions();
|
||||
if (isset($functions[$function]) && preg_match('/^\w+ (\w+)\(/', $functions[$function], $match)) {
|
||||
$function = $match[1];
|
||||
}
|
||||
}
|
||||
|
||||
// encode request for given $function
|
||||
return (string)$this->encoder->encode($function, array())->getUri();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new `Client` with the updated location (URI) for all functions.
|
||||
*
|
||||
* Note that this is not to be confused with the WSDL file location.
|
||||
* A WSDL file can contain any number of function definitions.
|
||||
* It's very common that all of these functions use the same location definition.
|
||||
* However, technically each function can potentially use a different location.
|
||||
*
|
||||
* ```php
|
||||
* $client = $client->withLocation('http://example.com/soap');
|
||||
*
|
||||
* assert('http://example.com/soap' === $client->getLocation('echo'));
|
||||
* ```
|
||||
*
|
||||
* As an alternative to this method, you can also set the `location` option
|
||||
* in the `Client` constructor (such as when in non-WSDL mode).
|
||||
*
|
||||
* @param string $location
|
||||
* @return self
|
||||
* @see self::getLocation()
|
||||
*/
|
||||
public function withLocation($location)
|
||||
{
|
||||
$client = clone $this;
|
||||
$client->encoder = clone $this->encoder;
|
||||
$client->encoder->__setLocation($location);
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Soap\Protocol;
|
||||
|
||||
use \SoapClient;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ClientDecoder extends SoapClient
|
||||
{
|
||||
private $response = null;
|
||||
|
||||
/**
|
||||
* Decodes the SOAP response / return value from the given SOAP envelope (HTTP response body)
|
||||
*
|
||||
* @param string $function
|
||||
* @param string $response
|
||||
* @return mixed
|
||||
* @throws \SoapFault if response indicates a fault (error condition) or is invalid
|
||||
*/
|
||||
public function decode($function, $response)
|
||||
{
|
||||
// Temporarily save response internally for further processing
|
||||
$this->response = $response;
|
||||
|
||||
// Let's pretend we just invoked the given SOAP function.
|
||||
// This won't actually invoke anything (see `__doRequest()`), but this
|
||||
// requires a valid function name to match its definition in the WSDL.
|
||||
// Internally, simply use the injected response to parse its results.
|
||||
$ret = $this->__soapCall($function, array());
|
||||
$this->response = null;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites the internal request logic to parse the response
|
||||
*
|
||||
* By overwriting this method, we can skip the actual request sending logic
|
||||
* and still use the internal parsing logic by injecting the response as
|
||||
* the return code in this method. This will implicitly be invoked by the
|
||||
* call to `pseudoCall()` in the above `decode()` method.
|
||||
*
|
||||
* @see SoapClient::__doRequest()
|
||||
*/
|
||||
public function __doRequest($request, $location, $action, $version, $one_way = 0)
|
||||
{
|
||||
// the actual result doesn't actually matter, just return the given result
|
||||
// this will be processed internally and will return the parsed result
|
||||
return $this->response;
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Soap\Protocol;
|
||||
|
||||
use \SoapClient;
|
||||
use RingCentral\Psr7\Request;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ClientEncoder extends SoapClient
|
||||
{
|
||||
private $request = null;
|
||||
|
||||
/**
|
||||
* Encodes the given RPC function name and arguments as a SOAP request
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $args
|
||||
* @return Request
|
||||
* @throws \SoapFault if request is invalid according to WSDL
|
||||
*/
|
||||
public function encode($name, $args)
|
||||
{
|
||||
$this->__soapCall($name, $args);
|
||||
|
||||
$request = $this->request;
|
||||
$this->request = null;
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites the internal request logic to build the request message
|
||||
*
|
||||
* By overwriting this method, we can skip the actual request sending logic
|
||||
* and still use the internal request serializing logic by accessing the
|
||||
* given `$request` parameter and building our custom request object from
|
||||
* it. We skip/ignore its parsing logic by returing an empty response here.
|
||||
* This will implicitly be invoked by the call to `__soapCall()` in the
|
||||
* above `encode()` method.
|
||||
*
|
||||
* @see SoapClient::__doRequest()
|
||||
*/
|
||||
public function __doRequest($request, $location, $action, $version, $one_way = 0)
|
||||
{
|
||||
$headers = array();
|
||||
if ($version === SOAP_1_1) {
|
||||
$headers = array(
|
||||
'SOAPAction' => $action,
|
||||
'Content-Type' => 'text/xml; charset=utf-8'
|
||||
);
|
||||
} elseif ($version === SOAP_1_2) {
|
||||
$headers = array(
|
||||
'Content-Type' => 'application/soap+xml; charset=utf-8; action=' . $action
|
||||
);
|
||||
}
|
||||
|
||||
$this->request = new Request(
|
||||
'POST',
|
||||
(string)$location,
|
||||
$headers,
|
||||
(string)$request
|
||||
);
|
||||
|
||||
// do not actually block here, just pretend we're done...
|
||||
return '';
|
||||
}
|
||||
}
|
50
vendor/clue/soap-react/src/Proxy.php
vendored
50
vendor/clue/soap-react/src/Proxy.php
vendored
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Soap;
|
||||
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* The `Proxy` class wraps an existing [`Client`](#client) instance in order to ease calling
|
||||
* SOAP functions.
|
||||
*
|
||||
* ```php
|
||||
* $proxy = new Proxy($client);
|
||||
* ```
|
||||
*
|
||||
* Each and every method call to the `Proxy` class will be sent via SOAP.
|
||||
*
|
||||
* ```php
|
||||
* $proxy->myMethod($myArg1, $myArg2)->then(function ($response) {
|
||||
* // result received
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Please refer to your WSDL or its accompanying documentation for details
|
||||
* on which functions and arguments are supported.
|
||||
*
|
||||
* > Note that this class is called "Proxy" because it will forward (proxy) all
|
||||
* method calls to the actual SOAP service via the underlying
|
||||
* [`Client::soapCall()`](#soapcall) method. This is not to be confused with
|
||||
* using a proxy server. See [`Client`](#client) documentation for more
|
||||
* details on how to use an HTTP proxy server.
|
||||
*/
|
||||
final class Proxy
|
||||
{
|
||||
private $client;
|
||||
|
||||
public function __construct(Client $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed[] $args
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function __call($name, $args)
|
||||
{
|
||||
return $this->client->soapCall($name, $args);
|
||||
}
|
||||
}
|
21
vendor/clue/socket-raw/LICENSE
vendored
21
vendor/clue/socket-raw/LICENSE
vendored
@ -1,21 +0,0 @@
|
||||
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
80
vendor/clue/socket-raw/src/Exception.php
vendored
@ -1,80 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
279
vendor/clue/socket-raw/src/Factory.php
vendored
279
vendor/clue/socket-raw/src/Factory.php
vendored
@ -1,279 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Socket\Raw;
|
||||
|
||||
use \InvalidArgumentException;
|
||||
|
||||
class Factory
|
||||
{
|
||||
/**
|
||||
* create client socket connected to given target address
|
||||
*
|
||||
* @param string $address target address to connect to
|
||||
* @param null|float $timeout connection timeout (in seconds), default null = no limit
|
||||
* @return \Socket\Raw\Socket
|
||||
* @throws InvalidArgumentException if given address is invalid
|
||||
* @throws Exception on error
|
||||
* @uses self::createFromString()
|
||||
* @uses Socket::connect()
|
||||
* @uses Socket::connectTimeout()
|
||||
*/
|
||||
public function createClient($address, $timeout = null)
|
||||
{
|
||||
$socket = $this->createFromString($address, $scheme);
|
||||
|
||||
try {
|
||||
if ($timeout === null) {
|
||||
$socket->connect($address);
|
||||
} else {
|
||||
// connectTimeout enables non-blocking mode, so turn blocking on again
|
||||
$socket->connectTimeout($address, $timeout);
|
||||
$socket->setBlocking(true);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
530
vendor/clue/socket-raw/src/Socket.php
vendored
530
vendor/clue/socket-raw/src/Socket.php
vendored
@ -1,530 +0,0 @@
|
||||
<?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 (or EWOULDBLOCK on Windows) => otherwise re-throw
|
||||
if ($e->getCode() !== SOCKET_EINPROGRESS && $e->getCode() !== SOCKET_EWOULDBLOCK) {
|
||||
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
21
vendor/clue/socks-react/LICENSE
vendored
@ -1,21 +0,0 @@
|
||||
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.
|
441
vendor/clue/socks-react/src/Client.php
vendored
441
vendor/clue/socks-react/src/Client.php
vendored
@ -1,441 +0,0 @@
|
||||
<?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;
|
||||
|
||||
final class Client implements ConnectorInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var ConnectorInterface
|
||||
*/
|
||||
private $connector;
|
||||
|
||||
private $socksUri;
|
||||
|
||||
private $protocolVersion = 5;
|
||||
|
||||
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)?)(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' && $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 === '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 ReactPHP'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 (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'];
|
||||
}
|
||||
|
||||
// start TCP/IP connection to SOCKS server
|
||||
$connecting = $this->connector->connect($socksUri);
|
||||
|
||||
$deferred = new Deferred(function ($_, $reject) use ($uri, $connecting) {
|
||||
$reject(new RuntimeException(
|
||||
'Connection to ' . $uri . ' cancelled while waiting for proxy (ECONNABORTED)',
|
||||
defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103
|
||||
));
|
||||
|
||||
// either close active connection or cancel pending connection attempt
|
||||
$connecting->then(function (ConnectionInterface $stream) {
|
||||
$stream->close();
|
||||
});
|
||||
$connecting->cancel();
|
||||
});
|
||||
|
||||
// handle SOCKS protocol once connection is ready
|
||||
// resolve plain connection once SOCKS protocol is completed
|
||||
$that = $this;
|
||||
$connecting->then(
|
||||
function (ConnectionInterface $stream) use ($that, $host, $port, $deferred, $uri) {
|
||||
$that->handleConnectedSocks($stream, $host, $port, $deferred, $uri);
|
||||
},
|
||||
function (Exception $e) use ($uri, $deferred) {
|
||||
$deferred->reject($e = new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because connection to proxy failed (ECONNREFUSED)',
|
||||
defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111,
|
||||
$e
|
||||
));
|
||||
|
||||
// avoid garbage references by replacing all closures in call stack.
|
||||
// what a lovely piece of code!
|
||||
$r = new \ReflectionProperty('Exception', 'trace');
|
||||
$r->setAccessible(true);
|
||||
$trace = $r->getValue($e);
|
||||
foreach ($trace as &$one) {
|
||||
foreach ($one['args'] as &$arg) {
|
||||
if ($arg instanceof \Closure) {
|
||||
$arg = 'Object(' . get_class($arg) . ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
$r->setValue($e, $trace);
|
||||
}
|
||||
);
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper used to handle the communication with the SOCKS server
|
||||
*
|
||||
* @param ConnectionInterface $stream
|
||||
* @param string $host
|
||||
* @param int $port
|
||||
* @param Deferred $deferred
|
||||
* @param string $uri
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function handleConnectedSocks(ConnectionInterface $stream, $host, $port, Deferred $deferred, $uri)
|
||||
{
|
||||
$reader = new StreamReader();
|
||||
$stream->on('data', array($reader, 'write'));
|
||||
|
||||
$stream->on('error', $onError = function (Exception $e) use ($deferred, $uri) {
|
||||
$deferred->reject(new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because connection to proxy caused a stream error (EIO)',
|
||||
defined('SOCKET_EIO') ? SOCKET_EIO : 5, $e)
|
||||
);
|
||||
});
|
||||
|
||||
$stream->on('close', $onClose = function () use ($deferred, $uri) {
|
||||
$deferred->reject(new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because connection to proxy was lost while waiting for response from proxy (ECONNRESET)',
|
||||
defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 104)
|
||||
);
|
||||
});
|
||||
|
||||
if ($this->protocolVersion === 5) {
|
||||
$promise = $this->handleSocks5($stream, $host, $port, $reader, $uri);
|
||||
} else {
|
||||
$promise = $this->handleSocks4($stream, $host, $port, $reader, $uri);
|
||||
}
|
||||
|
||||
$promise->then(function () use ($deferred, $stream, $reader, $onError, $onClose) {
|
||||
$stream->removeListener('data', array($reader, 'write'));
|
||||
$stream->removeListener('error', $onError);
|
||||
$stream->removeListener('close', $onClose);
|
||||
|
||||
$deferred->resolve($stream);
|
||||
}, function (Exception $error) use ($deferred, $stream, $uri) {
|
||||
// pass custom RuntimeException through as-is, otherwise wrap in protocol error
|
||||
if (!$error instanceof RuntimeException) {
|
||||
$error = new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy returned invalid response (EBADMSG)',
|
||||
defined('SOCKET_EBADMSG') ? SOCKET_EBADMSG: 71,
|
||||
$error
|
||||
);
|
||||
}
|
||||
|
||||
$deferred->reject($error);
|
||||
$stream->close();
|
||||
});
|
||||
}
|
||||
|
||||
private function handleSocks4(ConnectionInterface $stream, $host, $port, StreamReader $reader, $uri)
|
||||
{
|
||||
// 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) use ($uri) {
|
||||
if ($data['null'] !== 0x00) {
|
||||
throw new Exception('Invalid SOCKS response');
|
||||
}
|
||||
if ($data['status'] !== 0x5a) {
|
||||
throw new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy refused connection with error code ' . sprintf('0x%02X', $data['status']) . ' (ECONNREFUSED)',
|
||||
defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function handleSocks5(ConnectionInterface $stream, $host, $port, StreamReader $reader, $uri)
|
||||
{
|
||||
// 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, $uri) {
|
||||
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) use ($uri) {
|
||||
if ($data['version'] !== 0x01 || $data['status'] !== 0x00) {
|
||||
throw new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy denied access with given authentication details (EACCES)',
|
||||
defined('SOCKET_EACCES') ? SOCKET_EACCES : 13
|
||||
);
|
||||
}
|
||||
});
|
||||
} else if ($data['method'] !== 0x00) {
|
||||
// any other method than "no authentication"
|
||||
throw new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy denied access due to unsupported authentication method (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, $uri) {
|
||||
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(
|
||||
'Connection to ' . $uri . ' failed because proxy refused connection with general server failure (ECONNREFUSED)',
|
||||
defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
|
||||
);
|
||||
} elseif ($data['status'] === Server::ERROR_NOT_ALLOWED_BY_RULESET) {
|
||||
throw new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy denied access due to ruleset (EACCES)',
|
||||
defined('SOCKET_EACCES') ? SOCKET_EACCES : 13
|
||||
);
|
||||
} elseif ($data['status'] === Server::ERROR_NETWORK_UNREACHABLE) {
|
||||
throw new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy reported network unreachable (ENETUNREACH)',
|
||||
defined('SOCKET_ENETUNREACH') ? SOCKET_ENETUNREACH : 101
|
||||
);
|
||||
} elseif ($data['status'] === Server::ERROR_HOST_UNREACHABLE) {
|
||||
throw new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy reported host unreachable (EHOSTUNREACH)',
|
||||
defined('SOCKET_EHOSTUNREACH') ? SOCKET_EHOSTUNREACH : 113
|
||||
);
|
||||
} elseif ($data['status'] === Server::ERROR_CONNECTION_REFUSED) {
|
||||
throw new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy reported connection refused (ECONNREFUSED)',
|
||||
defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
|
||||
);
|
||||
} elseif ($data['status'] === Server::ERROR_TTL) {
|
||||
throw new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy reported TTL/timeout expired (ETIMEDOUT)',
|
||||
defined('SOCKET_ETIMEDOUT') ? SOCKET_ETIMEDOUT : 110
|
||||
);
|
||||
} elseif ($data['status'] === Server::ERROR_COMMAND_UNSUPPORTED) {
|
||||
throw new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy does not support the CONNECT command (EPROTO)',
|
||||
defined('SOCKET_EPROTO') ? SOCKET_EPROTO : 71
|
||||
);
|
||||
} elseif ($data['status'] === Server::ERROR_ADDRESS_UNSUPPORTED) {
|
||||
throw new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy does not support this address type (EPROTO)',
|
||||
defined('SOCKET_EPROTO') ? SOCKET_EPROTO : 71
|
||||
);
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
'Connection to ' . $uri . ' failed because proxy server refused connection with unknown 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');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
393
vendor/clue/socks-react/src/Server.php
vendored
393
vendor/clue/socks-react/src/Server.php
vendored
@ -1,393 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Socks;
|
||||
|
||||
use React\Socket\ServerInterface;
|
||||
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;
|
||||
|
||||
final class Server
|
||||
{
|
||||
// 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;
|
||||
|
||||
private $loop;
|
||||
|
||||
private $connector;
|
||||
|
||||
/**
|
||||
* @var null|callable
|
||||
*/
|
||||
private $auth;
|
||||
|
||||
/**
|
||||
* @param LoopInterface $loop
|
||||
* @param null|ConnectorInterface $connector
|
||||
* @param null|array|callable $auth
|
||||
*/
|
||||
public function __construct(LoopInterface $loop, ConnectorInterface $connector = null, $auth = null)
|
||||
{
|
||||
if ($connector === null) {
|
||||
$connector = new Connector($loop);
|
||||
}
|
||||
|
||||
if (\is_array($auth)) {
|
||||
// wrap authentication array in authentication callback
|
||||
$this->auth = function ($username, $password) use ($auth) {
|
||||
return \React\Promise\resolve(
|
||||
isset($auth[$username]) && (string)$auth[$username] === $password
|
||||
);
|
||||
};
|
||||
} elseif (\is_callable($auth)) {
|
||||
// wrap authentication callback in order to cast its return value to a promise
|
||||
$this->auth = function($username, $password, $remote) use ($auth) {
|
||||
return \React\Promise\resolve(
|
||||
\call_user_func($auth, $username, $password, $remote)
|
||||
);
|
||||
};
|
||||
} elseif ($auth !== null) {
|
||||
throw new \InvalidArgumentException('Invalid authenticator given');
|
||||
}
|
||||
|
||||
$this->loop = $loop;
|
||||
$this->connector = $connector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerInterface $socket
|
||||
* @return void
|
||||
*/
|
||||
public function listen(ServerInterface $socket)
|
||||
{
|
||||
$that = $this;
|
||||
$socket->on('connection', function ($connection) use ($that) {
|
||||
$that->onConnection($connection);
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public function onConnection(ConnectionInterface $connection)
|
||||
{
|
||||
$that = $this;
|
||||
$handling = $this->handleSocks($connection)->then(null, function () use ($connection, $that) {
|
||||
// SOCKS failed => close connection
|
||||
$that->endConnection($connection);
|
||||
});
|
||||
|
||||
$connection->on('close', function () use ($handling) {
|
||||
$handling->cancel();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* [internal] gracefully shutdown connection by flushing all remaining data and closing stream
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
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;
|
||||
$auth = $this->auth;
|
||||
|
||||
return $reader->readByte()->then(function ($version) use ($stream, $that, $auth, $reader){
|
||||
if ($version === 0x04) {
|
||||
if ($auth !== null) {
|
||||
throw new UnexpectedValueException('SOCKS4 not allowed because authentication is required');
|
||||
}
|
||||
return $that->handleSocks4($stream, $reader);
|
||||
} else if ($version === 0x05) {
|
||||
return $that->handleSocks5($stream, $auth, $reader);
|
||||
}
|
||||
throw new UnexpectedValueException('Unexpected/unknown version number');
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public function handleSocks4(ConnectionInterface $stream, StreamReader $reader)
|
||||
{
|
||||
$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, $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) {
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public function handleSocks5(ConnectionInterface $stream, $auth, 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 = 'socks' . ($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 ($authenticated) use ($stream) {
|
||||
if ($authenticated) {
|
||||
// accept auth
|
||||
$stream->write(pack('C2', 0x01, 0x00));
|
||||
} else {
|
||||
// reject auth => send any code but 0x00
|
||||
$stream->end(pack('C2', 0x01, 0xFF));
|
||||
throw new UnexpectedValueException('Authentication denied');
|
||||
}
|
||||
}, function ($e) use ($stream) {
|
||||
// reject failed authentication => send any code but 0x00
|
||||
$stream->end(pack('C2', 0x01, 0xFF));
|
||||
throw new UnexpectedValueException('Authentication error', 0, $e);
|
||||
});
|
||||
});
|
||||
});
|
||||
} 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) {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
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 \React\Promise\reject(new InvalidArgumentException('Invalid target URI given'));
|
||||
}
|
||||
|
||||
if (isset($target[2])) {
|
||||
$uri .= '?source=' . rawurlencode($target[2]);
|
||||
}
|
||||
|
||||
$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) {
|
||||
$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
149
vendor/clue/socks-react/src/StreamReader.php
vendored
@ -1,149 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Socks;
|
||||
|
||||
use React\Promise\Deferred;
|
||||
use \InvalidArgumentException;
|
||||
use \UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final 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
445
vendor/composer/ClassLoader.php
vendored
@ -1,445 +0,0 @@
|
||||
<?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') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $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])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
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
21
vendor/composer/LICENSE
vendored
@ -1,21 +0,0 @@
|
||||
|
||||
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
9
vendor/composer/autoload_classmap.php
vendored
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
16
vendor/composer/autoload_files.php
vendored
16
vendor/composer/autoload_files.php
vendored
@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'ad155f8f1cf0d418fe49e248db8c661b' => $vendorDir . '/react/promise/src/functions_include.php',
|
||||
'6b06ce8ccf69c43a60a1e48495a034c9' => $vendorDir . '/react/promise-timer/src/functions.php',
|
||||
'd97a92f16d9d46b74cbec4132395f0a2' => $vendorDir . '/clue/block-react/src/functions.php',
|
||||
'ebf8799635f67b5d7248946fe2154f4a' => $vendorDir . '/ringcentral/psr7/src/functions_include.php',
|
||||
'cea474b4340aa9fa53661e887a21a316' => $vendorDir . '/react/promise-stream/src/functions_include.php',
|
||||
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
);
|
11
vendor/composer/autoload_namespaces.php
vendored
11
vendor/composer/autoload_namespaces.php
vendored
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Evenement' => array($vendorDir . '/evenement/evenement/src'),
|
||||
'Clue\\Redis\\Protocol' => array($vendorDir . '/clue/redis-protocol/src'),
|
||||
);
|
34
vendor/composer/autoload_psr4.php
vendored
34
vendor/composer/autoload_psr4.php
vendored
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
|
||||
'Socket\\Raw\\' => array($vendorDir . '/clue/socket-raw/src'),
|
||||
'RingCentral\\Psr7\\' => array($vendorDir . '/ringcentral/psr7/src'),
|
||||
'React\\Stream\\' => array($vendorDir . '/react/stream/src'),
|
||||
'React\\Socket\\' => array($vendorDir . '/react/socket/src'),
|
||||
'React\\Promise\\Timer\\' => array($vendorDir . '/react/promise-timer/src'),
|
||||
'React\\Promise\\Stream\\' => array($vendorDir . '/react/promise-stream/src'),
|
||||
'React\\Promise\\' => array($vendorDir . '/react/promise/src'),
|
||||
'React\\Http\\' => array($vendorDir . '/react/http/src'),
|
||||
'React\\HttpClient\\' => array($vendorDir . '/react/http-client/src'),
|
||||
'React\\EventLoop\\' => array($vendorDir . '/react/event-loop/src'),
|
||||
'React\\Dns\\' => array($vendorDir . '/react/dns/src'),
|
||||
'React\\Datagram\\' => array($vendorDir . '/react/datagram/src'),
|
||||
'React\\ChildProcess\\' => array($vendorDir . '/react/child-process/src'),
|
||||
'React\\Cache\\' => array($vendorDir . '/react/cache/src'),
|
||||
'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'),
|
||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
||||
'Predis\\' => array($vendorDir . '/predis/predis/src'),
|
||||
'ConnectionManager\\Extra\\' => array($vendorDir . '/clue/connection-manager-extra/src'),
|
||||
'Clue\\React\\Socks\\' => array($vendorDir . '/clue/socks-react/src'),
|
||||
'Clue\\React\\Soap\\' => array($vendorDir . '/clue/soap-react/src'),
|
||||
'Clue\\React\\Redis\\' => array($vendorDir . '/clue/redis-react/src'),
|
||||
'Clue\\React\\Mq\\' => array($vendorDir . '/clue/mq-react/src'),
|
||||
'Clue\\React\\HttpProxy\\' => array($vendorDir . '/clue/http-proxy-react/src'),
|
||||
'Clue\\React\\Buzz\\' => array($vendorDir . '/clue/buzz-react/src'),
|
||||
);
|
70
vendor/composer/autoload_real.php
vendored
70
vendor/composer/autoload_real.php
vendored
@ -1,70 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit0ee91abee730dc4eb186583ca53653ff
|
||||
{
|
||||
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('ComposerAutoloaderInit0ee91abee730dc4eb186583ca53653ff', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit0ee91abee730dc4eb186583ca53653ff', '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\ComposerStaticInit0ee91abee730dc4eb186583ca53653ff::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\ComposerStaticInit0ee91abee730dc4eb186583ca53653ff::$files;
|
||||
} else {
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
}
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire0ee91abee730dc4eb186583ca53653ff($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequire0ee91abee730dc4eb186583ca53653ff($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
}
|
||||
}
|
188
vendor/composer/autoload_static.php
vendored
188
vendor/composer/autoload_static.php
vendored
@ -1,188 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit0ee91abee730dc4eb186583ca53653ff
|
||||
{
|
||||
public static $files = array (
|
||||
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
|
||||
'6b06ce8ccf69c43a60a1e48495a034c9' => __DIR__ . '/..' . '/react/promise-timer/src/functions.php',
|
||||
'd97a92f16d9d46b74cbec4132395f0a2' => __DIR__ . '/..' . '/clue/block-react/src/functions.php',
|
||||
'ebf8799635f67b5d7248946fe2154f4a' => __DIR__ . '/..' . '/ringcentral/psr7/src/functions_include.php',
|
||||
'cea474b4340aa9fa53661e887a21a316' => __DIR__ . '/..' . '/react/promise-stream/src/functions_include.php',
|
||||
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Polyfill\\Ctype\\' => 23,
|
||||
'Socket\\Raw\\' => 11,
|
||||
),
|
||||
'R' =>
|
||||
array (
|
||||
'RingCentral\\Psr7\\' => 17,
|
||||
'React\\Stream\\' => 13,
|
||||
'React\\Socket\\' => 13,
|
||||
'React\\Promise\\Timer\\' => 20,
|
||||
'React\\Promise\\Stream\\' => 21,
|
||||
'React\\Promise\\' => 14,
|
||||
'React\\Http\\' => 11,
|
||||
'React\\HttpClient\\' => 17,
|
||||
'React\\EventLoop\\' => 16,
|
||||
'React\\Dns\\' => 10,
|
||||
'React\\Datagram\\' => 15,
|
||||
'React\\ChildProcess\\' => 19,
|
||||
'React\\Cache\\' => 12,
|
||||
'Ramsey\\Uuid\\' => 12,
|
||||
),
|
||||
'P' =>
|
||||
array (
|
||||
'Psr\\Http\\Message\\' => 17,
|
||||
'Predis\\' => 7,
|
||||
),
|
||||
'C' =>
|
||||
array (
|
||||
'ConnectionManager\\Extra\\' => 24,
|
||||
'Clue\\React\\Socks\\' => 17,
|
||||
'Clue\\React\\Soap\\' => 16,
|
||||
'Clue\\React\\Redis\\' => 17,
|
||||
'Clue\\React\\Mq\\' => 14,
|
||||
'Clue\\React\\HttpProxy\\' => 21,
|
||||
'Clue\\React\\Buzz\\' => 16,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'Symfony\\Polyfill\\Ctype\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
|
||||
),
|
||||
'Socket\\Raw\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/socket-raw/src',
|
||||
),
|
||||
'RingCentral\\Psr7\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/ringcentral/psr7/src',
|
||||
),
|
||||
'React\\Stream\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/stream/src',
|
||||
),
|
||||
'React\\Socket\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/socket/src',
|
||||
),
|
||||
'React\\Promise\\Timer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/promise-timer/src',
|
||||
),
|
||||
'React\\Promise\\Stream\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/promise-stream/src',
|
||||
),
|
||||
'React\\Promise\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/promise/src',
|
||||
),
|
||||
'React\\Http\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/http/src',
|
||||
),
|
||||
'React\\HttpClient\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/http-client/src',
|
||||
),
|
||||
'React\\EventLoop\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/event-loop/src',
|
||||
),
|
||||
'React\\Dns\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/dns/src',
|
||||
),
|
||||
'React\\Datagram\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/datagram/src',
|
||||
),
|
||||
'React\\ChildProcess\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/child-process/src',
|
||||
),
|
||||
'React\\Cache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/cache/src',
|
||||
),
|
||||
'Ramsey\\Uuid\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/ramsey/uuid/src',
|
||||
),
|
||||
'Psr\\Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/http-message/src',
|
||||
),
|
||||
'Predis\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/predis/predis/src',
|
||||
),
|
||||
'ConnectionManager\\Extra\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/connection-manager-extra/src',
|
||||
),
|
||||
'Clue\\React\\Socks\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/socks-react/src',
|
||||
),
|
||||
'Clue\\React\\Soap\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/soap-react/src',
|
||||
),
|
||||
'Clue\\React\\Redis\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/redis-react/src',
|
||||
),
|
||||
'Clue\\React\\Mq\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/mq-react/src',
|
||||
),
|
||||
'Clue\\React\\HttpProxy\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/http-proxy-react/src',
|
||||
),
|
||||
'Clue\\React\\Buzz\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/buzz-react/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixesPsr0 = array (
|
||||
'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 = ComposerStaticInit0ee91abee730dc4eb186583ca53653ff::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit0ee91abee730dc4eb186583ca53653ff::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInit0ee91abee730dc4eb186583ca53653ff::$prefixesPsr0;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
19
vendor/evenement/evenement/LICENSE
vendored
19
vendor/evenement/evenement/LICENSE
vendored
@ -1,19 +0,0 @@
|
||||
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.
|
@ -1,17 +0,0 @@
|
||||
<?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;
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
<?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 = []);
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Evenement.
|
||||
*
|
||||
* (c) Igor Wiedler <igor@wiedler.ch>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evenement;
|
||||
|
||||
trait EventEmitterTrait
|
||||
{
|
||||
protected $listeners = [];
|
||||
|
||||
public function on($event, callable $listener)
|
||||
{
|
||||
if (!isset($this->listeners[$event])) {
|
||||
$this->listeners[$event] = [];
|
||||
}
|
||||
|
||||
$this->listeners[$event][] = $listener;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function once($event, callable $listener)
|
||||
{
|
||||
$onceListener = function () use (&$onceListener, $event, $listener) {
|
||||
$this->removeListener($event, $onceListener);
|
||||
|
||||
\call_user_func_array($listener, \func_get_args());
|
||||
};
|
||||
|
||||
$this->on($event, $onceListener);
|
||||
}
|
||||
|
||||
public function removeListener($event, callable $listener)
|
||||
{
|
||||
if (isset($this->listeners[$event])) {
|
||||
$index = \array_search($listener, $this->listeners[$event], true);
|
||||
if (false !== $index) {
|
||||
unset($this->listeners[$event][$index]);
|
||||
if (\count($this->listeners[$event]) === 0) {
|
||||
unset($this->listeners[$event]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function removeAllListeners($event = null)
|
||||
{
|
||||
if ($event !== null) {
|
||||
unset($this->listeners[$event]);
|
||||
} else {
|
||||
$this->listeners = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function listeners($event)
|
||||
{
|
||||
return isset($this->listeners[$event]) ? $this->listeners[$event] : [];
|
||||
}
|
||||
|
||||
public function emit($event, array $arguments = [])
|
||||
{
|
||||
foreach ($this->listeners($event) as $listener) {
|
||||
\call_user_func_array($listener, $arguments);
|
||||
}
|
||||
}
|
||||
}
|
22
vendor/paragonie/random_compat/LICENSE
vendored
22
vendor/paragonie/random_compat/LICENSE
vendored
@ -1,22 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
|
||||
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.
|
||||
|
@ -1,195 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (!is_callable('RandomCompat_strlen')) {
|
||||
if (
|
||||
defined('MB_OVERLOAD_STRING')
|
||||
&&
|
||||
((int) ini_get('mbstring.func_overload')) & MB_OVERLOAD_STRING
|
||||
) {
|
||||
/**
|
||||
* strlen() implementation that isn't brittle to mbstring.func_overload
|
||||
*
|
||||
* This version uses mb_strlen() in '8bit' mode to treat strings as raw
|
||||
* binary rather than UTF-8, ISO-8859-1, etc
|
||||
*
|
||||
* @param string $binary_string
|
||||
*
|
||||
* @throws TypeError
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function RandomCompat_strlen($binary_string)
|
||||
{
|
||||
if (!is_string($binary_string)) {
|
||||
throw new TypeError(
|
||||
'RandomCompat_strlen() expects a string'
|
||||
);
|
||||
}
|
||||
|
||||
return (int) mb_strlen($binary_string, '8bit');
|
||||
}
|
||||
|
||||
} else {
|
||||
/**
|
||||
* strlen() implementation that isn't brittle to mbstring.func_overload
|
||||
*
|
||||
* This version just used the default strlen()
|
||||
*
|
||||
* @param string $binary_string
|
||||
*
|
||||
* @throws TypeError
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function RandomCompat_strlen($binary_string)
|
||||
{
|
||||
if (!is_string($binary_string)) {
|
||||
throw new TypeError(
|
||||
'RandomCompat_strlen() expects a string'
|
||||
);
|
||||
}
|
||||
return (int) strlen($binary_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_callable('RandomCompat_substr')) {
|
||||
|
||||
if (
|
||||
defined('MB_OVERLOAD_STRING')
|
||||
&&
|
||||
((int) ini_get('mbstring.func_overload')) & MB_OVERLOAD_STRING
|
||||
) {
|
||||
/**
|
||||
* substr() implementation that isn't brittle to mbstring.func_overload
|
||||
*
|
||||
* This version uses mb_substr() in '8bit' mode to treat strings as raw
|
||||
* binary rather than UTF-8, ISO-8859-1, etc
|
||||
*
|
||||
* @param string $binary_string
|
||||
* @param int $start
|
||||
* @param int|null $length (optional)
|
||||
*
|
||||
* @throws TypeError
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function RandomCompat_substr($binary_string, $start, $length = null)
|
||||
{
|
||||
if (!is_string($binary_string)) {
|
||||
throw new TypeError(
|
||||
'RandomCompat_substr(): First argument should be a string'
|
||||
);
|
||||
}
|
||||
|
||||
if (!is_int($start)) {
|
||||
throw new TypeError(
|
||||
'RandomCompat_substr(): Second argument should be an integer'
|
||||
);
|
||||
}
|
||||
|
||||
if ($length === null) {
|
||||
/**
|
||||
* mb_substr($str, 0, NULL, '8bit') returns an empty string on
|
||||
* PHP 5.3, so we have to find the length ourselves.
|
||||
*/
|
||||
/** @var int $length */
|
||||
$length = RandomCompat_strlen($binary_string) - $start;
|
||||
} elseif (!is_int($length)) {
|
||||
throw new TypeError(
|
||||
'RandomCompat_substr(): Third argument should be an integer, or omitted'
|
||||
);
|
||||
}
|
||||
|
||||
// Consistency with PHP's behavior
|
||||
if ($start === RandomCompat_strlen($binary_string) && $length === 0) {
|
||||
return '';
|
||||
}
|
||||
if ($start > RandomCompat_strlen($binary_string)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (string) mb_substr(
|
||||
(string) $binary_string,
|
||||
(int) $start,
|
||||
(int) $length,
|
||||
'8bit'
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/**
|
||||
* substr() implementation that isn't brittle to mbstring.func_overload
|
||||
*
|
||||
* This version just uses the default substr()
|
||||
*
|
||||
* @param string $binary_string
|
||||
* @param int $start
|
||||
* @param int|null $length (optional)
|
||||
*
|
||||
* @throws TypeError
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function RandomCompat_substr($binary_string, $start, $length = null)
|
||||
{
|
||||
if (!is_string($binary_string)) {
|
||||
throw new TypeError(
|
||||
'RandomCompat_substr(): First argument should be a string'
|
||||
);
|
||||
}
|
||||
|
||||
if (!is_int($start)) {
|
||||
throw new TypeError(
|
||||
'RandomCompat_substr(): Second argument should be an integer'
|
||||
);
|
||||
}
|
||||
|
||||
if ($length !== null) {
|
||||
if (!is_int($length)) {
|
||||
throw new TypeError(
|
||||
'RandomCompat_substr(): Third argument should be an integer, or omitted'
|
||||
);
|
||||
}
|
||||
|
||||
return (string) substr(
|
||||
(string )$binary_string,
|
||||
(int) $start,
|
||||
(int) $length
|
||||
);
|
||||
}
|
||||
|
||||
return (string) substr(
|
||||
(string) $binary_string,
|
||||
(int) $start
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (!is_callable('RandomCompat_intval')) {
|
||||
|
||||
/**
|
||||
* Cast to an integer if we can, safely.
|
||||
*
|
||||
* If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX)
|
||||
* (non-inclusive), it will sanely cast it to an int. If you it's equal to
|
||||
* ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats
|
||||
* lose precision, so the <= and => operators might accidentally let a float
|
||||
* through.
|
||||
*
|
||||
* @param int|float $number The number we want to convert to an int
|
||||
* @param bool $fail_open Set to true to not throw an exception
|
||||
*
|
||||
* @return float|int
|
||||
* @psalm-suppress InvalidReturnType
|
||||
*
|
||||
* @throws TypeError
|
||||
*/
|
||||
function RandomCompat_intval($number, $fail_open = false)
|
||||
{
|
||||
if (is_int($number) || is_float($number)) {
|
||||
$number += 0;
|
||||
} elseif (is_numeric($number)) {
|
||||
/** @psalm-suppress InvalidOperand */
|
||||
$number += 0;
|
||||
}
|
||||
/** @var int|float $number */
|
||||
|
||||
if (
|
||||
is_float($number)
|
||||
&&
|
||||
$number > ~PHP_INT_MAX
|
||||
&&
|
||||
$number < PHP_INT_MAX
|
||||
) {
|
||||
$number = (int) $number;
|
||||
}
|
||||
|
||||
if (is_int($number)) {
|
||||
return (int) $number;
|
||||
} elseif (!$fail_open) {
|
||||
throw new TypeError(
|
||||
'Expected an integer.'
|
||||
);
|
||||
}
|
||||
return $number;
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (!class_exists('Error', false)) {
|
||||
// We can't really avoid making this extend Exception in PHP 5.
|
||||
class Error extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('TypeError', false)) {
|
||||
if (is_subclass_of('Error', 'Exception')) {
|
||||
class TypeError extends Error
|
||||
{
|
||||
|
||||
}
|
||||
} else {
|
||||
class TypeError extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
225
vendor/paragonie/random_compat/lib/random.php
vendored
225
vendor/paragonie/random_compat/lib/random.php
vendored
@ -1,225 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* @version 2.0.17
|
||||
* @released 2018-07-04
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (!defined('PHP_VERSION_ID')) {
|
||||
// This constant was introduced in PHP 5.2.7
|
||||
$RandomCompatversion = array_map('intval', explode('.', PHP_VERSION));
|
||||
define(
|
||||
'PHP_VERSION_ID',
|
||||
$RandomCompatversion[0] * 10000
|
||||
+ $RandomCompatversion[1] * 100
|
||||
+ $RandomCompatversion[2]
|
||||
);
|
||||
$RandomCompatversion = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP 7.0.0 and newer have these functions natively.
|
||||
*/
|
||||
if (PHP_VERSION_ID >= 70000) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
|
||||
define('RANDOM_COMPAT_READ_BUFFER', 8);
|
||||
}
|
||||
|
||||
$RandomCompatDIR = dirname(__FILE__);
|
||||
|
||||
require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'byte_safe_strings.php';
|
||||
require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'cast_to_int.php';
|
||||
require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'error_polyfill.php';
|
||||
|
||||
if (!is_callable('random_bytes')) {
|
||||
/**
|
||||
* PHP 5.2.0 - 5.6.x way to implement random_bytes()
|
||||
*
|
||||
* We use conditional statements here to define the function in accordance
|
||||
* to the operating environment. It's a micro-optimization.
|
||||
*
|
||||
* In order of preference:
|
||||
* 1. Use libsodium if available.
|
||||
* 2. fread() /dev/urandom if available (never on Windows)
|
||||
* 3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM)
|
||||
* 4. COM('CAPICOM.Utilities.1')->GetRandom()
|
||||
*
|
||||
* See RATIONALE.md for our reasoning behind this particular order
|
||||
*/
|
||||
if (extension_loaded('libsodium')) {
|
||||
// See random_bytes_libsodium.php
|
||||
if (PHP_VERSION_ID >= 50300 && is_callable('\\Sodium\\randombytes_buf')) {
|
||||
require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_libsodium.php';
|
||||
} elseif (method_exists('Sodium', 'randombytes_buf')) {
|
||||
require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_libsodium_legacy.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading directly from /dev/urandom:
|
||||
*/
|
||||
if (DIRECTORY_SEPARATOR === '/') {
|
||||
// DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast
|
||||
// way to exclude Windows.
|
||||
$RandomCompatUrandom = true;
|
||||
$RandomCompat_basedir = ini_get('open_basedir');
|
||||
|
||||
if (!empty($RandomCompat_basedir)) {
|
||||
$RandomCompat_open_basedir = explode(
|
||||
PATH_SEPARATOR,
|
||||
strtolower($RandomCompat_basedir)
|
||||
);
|
||||
$RandomCompatUrandom = (array() !== array_intersect(
|
||||
array('/dev', '/dev/', '/dev/urandom'),
|
||||
$RandomCompat_open_basedir
|
||||
));
|
||||
$RandomCompat_open_basedir = null;
|
||||
}
|
||||
|
||||
if (
|
||||
!is_callable('random_bytes')
|
||||
&&
|
||||
$RandomCompatUrandom
|
||||
&&
|
||||
@is_readable('/dev/urandom')
|
||||
) {
|
||||
// Error suppression on is_readable() in case of an open_basedir
|
||||
// or safe_mode failure. All we care about is whether or not we
|
||||
// can read it at this point. If the PHP environment is going to
|
||||
// panic over trying to see if the file can be read in the first
|
||||
// place, that is not helpful to us here.
|
||||
|
||||
// See random_bytes_dev_urandom.php
|
||||
require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_dev_urandom.php';
|
||||
}
|
||||
// Unset variables after use
|
||||
$RandomCompat_basedir = null;
|
||||
} else {
|
||||
$RandomCompatUrandom = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* mcrypt_create_iv()
|
||||
*
|
||||
* We only want to use mcypt_create_iv() if:
|
||||
*
|
||||
* - random_bytes() hasn't already been defined
|
||||
* - the mcrypt extensions is loaded
|
||||
* - One of these two conditions is true:
|
||||
* - We're on Windows (DIRECTORY_SEPARATOR !== '/')
|
||||
* - We're not on Windows and /dev/urandom is readabale
|
||||
* (i.e. we're not in a chroot jail)
|
||||
* - Special case:
|
||||
* - If we're not on Windows, but the PHP version is between
|
||||
* 5.6.10 and 5.6.12, we don't want to use mcrypt. It will
|
||||
* hang indefinitely. This is bad.
|
||||
* - If we're on Windows, we want to use PHP >= 5.3.7 or else
|
||||
* we get insufficient entropy errors.
|
||||
*/
|
||||
if (
|
||||
!is_callable('random_bytes')
|
||||
&&
|
||||
// Windows on PHP < 5.3.7 is broken, but non-Windows is not known to be.
|
||||
(DIRECTORY_SEPARATOR === '/' || PHP_VERSION_ID >= 50307)
|
||||
&&
|
||||
// Prevent this code from hanging indefinitely on non-Windows;
|
||||
// see https://bugs.php.net/bug.php?id=69833
|
||||
(
|
||||
DIRECTORY_SEPARATOR !== '/' ||
|
||||
(PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613)
|
||||
)
|
||||
&&
|
||||
extension_loaded('mcrypt')
|
||||
) {
|
||||
// See random_bytes_mcrypt.php
|
||||
require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_mcrypt.php';
|
||||
}
|
||||
$RandomCompatUrandom = null;
|
||||
|
||||
/**
|
||||
* This is a Windows-specific fallback, for when the mcrypt extension
|
||||
* isn't loaded.
|
||||
*/
|
||||
if (
|
||||
!is_callable('random_bytes')
|
||||
&&
|
||||
extension_loaded('com_dotnet')
|
||||
&&
|
||||
class_exists('COM')
|
||||
) {
|
||||
$RandomCompat_disabled_classes = preg_split(
|
||||
'#\s*,\s*#',
|
||||
strtolower(ini_get('disable_classes'))
|
||||
);
|
||||
|
||||
if (!in_array('com', $RandomCompat_disabled_classes)) {
|
||||
try {
|
||||
$RandomCompatCOMtest = new COM('CAPICOM.Utilities.1');
|
||||
if (method_exists($RandomCompatCOMtest, 'GetRandom')) {
|
||||
// See random_bytes_com_dotnet.php
|
||||
require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_com_dotnet.php';
|
||||
}
|
||||
} catch (com_exception $e) {
|
||||
// Don't try to use it.
|
||||
}
|
||||
}
|
||||
$RandomCompat_disabled_classes = null;
|
||||
$RandomCompatCOMtest = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* throw new Exception
|
||||
*/
|
||||
if (!is_callable('random_bytes')) {
|
||||
/**
|
||||
* We don't have any more options, so let's throw an exception right now
|
||||
* and hope the developer won't let it fail silently.
|
||||
*
|
||||
* @param mixed $length
|
||||
* @psalm-suppress InvalidReturnType
|
||||
* @throws Exception
|
||||
* @return string
|
||||
*/
|
||||
function random_bytes($length)
|
||||
{
|
||||
unset($length); // Suppress "variable not used" warnings.
|
||||
throw new Exception(
|
||||
'There is no suitable CSPRNG installed on your system'
|
||||
);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_callable('random_int')) {
|
||||
require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_int.php';
|
||||
}
|
||||
|
||||
$RandomCompatDIR = null;
|
@ -1,91 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (!is_callable('random_bytes')) {
|
||||
/**
|
||||
* Windows with PHP < 5.3.0 will not have the function
|
||||
* openssl_random_pseudo_bytes() available, so let's use
|
||||
* CAPICOM to work around this deficiency.
|
||||
*
|
||||
* @param int $bytes
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function random_bytes($bytes)
|
||||
{
|
||||
try {
|
||||
/** @var int $bytes */
|
||||
$bytes = RandomCompat_intval($bytes);
|
||||
} catch (TypeError $ex) {
|
||||
throw new TypeError(
|
||||
'random_bytes(): $bytes must be an integer'
|
||||
);
|
||||
}
|
||||
|
||||
if ($bytes < 1) {
|
||||
throw new Error(
|
||||
'Length must be greater than 0'
|
||||
);
|
||||
}
|
||||
|
||||
/** @var string $buf */
|
||||
$buf = '';
|
||||
if (!class_exists('COM')) {
|
||||
throw new Error(
|
||||
'COM does not exist'
|
||||
);
|
||||
}
|
||||
/** @var COM $util */
|
||||
$util = new COM('CAPICOM.Utilities.1');
|
||||
$execCount = 0;
|
||||
|
||||
/**
|
||||
* Let's not let it loop forever. If we run N times and fail to
|
||||
* get N bytes of random data, then CAPICOM has failed us.
|
||||
*/
|
||||
do {
|
||||
$buf .= base64_decode((string) $util->GetRandom($bytes, 0));
|
||||
if (RandomCompat_strlen($buf) >= $bytes) {
|
||||
/**
|
||||
* Return our random entropy buffer here:
|
||||
*/
|
||||
return (string) RandomCompat_substr($buf, 0, $bytes);
|
||||
}
|
||||
++$execCount;
|
||||
} while ($execCount < $bytes);
|
||||
|
||||
/**
|
||||
* If we reach here, PHP has failed us.
|
||||
*/
|
||||
throw new Exception(
|
||||
'Could not gather sufficient random data'
|
||||
);
|
||||
}
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
|
||||
define('RANDOM_COMPAT_READ_BUFFER', 8);
|
||||
}
|
||||
|
||||
if (!is_callable('random_bytes')) {
|
||||
/**
|
||||
* Unless open_basedir is enabled, use /dev/urandom for
|
||||
* random numbers in accordance with best practices
|
||||
*
|
||||
* Why we use /dev/urandom and not /dev/random
|
||||
* @ref https://www.2uo.de/myths-about-urandom
|
||||
* @ref http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers
|
||||
*
|
||||
* @param int $bytes
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function random_bytes($bytes)
|
||||
{
|
||||
/** @var resource $fp */
|
||||
static $fp = null;
|
||||
|
||||
/**
|
||||
* This block should only be run once
|
||||
*/
|
||||
if (empty($fp)) {
|
||||
/**
|
||||
* We don't want to ever read C:\dev\random, only /dev/urandom on
|
||||
* Unix-like operating systems. While we guard against this
|
||||
* condition in random.php, it doesn't hurt to be defensive in depth
|
||||
* here.
|
||||
*
|
||||
* To that end, we only try to open /dev/urandom if we're on a Unix-
|
||||
* like operating system (which means the directory separator is set
|
||||
* to "/" not "\".
|
||||
*/
|
||||
if (DIRECTORY_SEPARATOR === '/') {
|
||||
if (!is_readable('/dev/urandom')) {
|
||||
throw new Exception(
|
||||
'Environment misconfiguration: ' .
|
||||
'/dev/urandom cannot be read.'
|
||||
);
|
||||
}
|
||||
/**
|
||||
* We use /dev/urandom if it is a char device.
|
||||
* We never fall back to /dev/random
|
||||
*/
|
||||
/** @var resource|bool $fp */
|
||||
$fp = fopen('/dev/urandom', 'rb');
|
||||
if (is_resource($fp)) {
|
||||
/** @var array<string, int> $st */
|
||||
$st = fstat($fp);
|
||||
if (($st['mode'] & 0170000) !== 020000) {
|
||||
fclose($fp);
|
||||
$fp = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_resource($fp)) {
|
||||
/**
|
||||
* stream_set_read_buffer() does not exist in HHVM
|
||||
*
|
||||
* If we don't set the stream's read buffer to 0, PHP will
|
||||
* internally buffer 8192 bytes, which can waste entropy
|
||||
*
|
||||
* stream_set_read_buffer returns 0 on success
|
||||
*/
|
||||
if (is_callable('stream_set_read_buffer')) {
|
||||
stream_set_read_buffer($fp, RANDOM_COMPAT_READ_BUFFER);
|
||||
}
|
||||
if (is_callable('stream_set_chunk_size')) {
|
||||
stream_set_chunk_size($fp, RANDOM_COMPAT_READ_BUFFER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var int $bytes */
|
||||
$bytes = RandomCompat_intval($bytes);
|
||||
} catch (TypeError $ex) {
|
||||
throw new TypeError(
|
||||
'random_bytes(): $bytes must be an integer'
|
||||
);
|
||||
}
|
||||
|
||||
if ($bytes < 1) {
|
||||
throw new Error(
|
||||
'Length must be greater than 0'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This if() block only runs if we managed to open a file handle
|
||||
*
|
||||
* It does not belong in an else {} block, because the above
|
||||
* if (empty($fp)) line is logic that should only be run once per
|
||||
* page load.
|
||||
*/
|
||||
if (is_resource($fp)) {
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
$remaining = $bytes;
|
||||
|
||||
/**
|
||||
* @var string|bool
|
||||
*/
|
||||
$buf = '';
|
||||
|
||||
/**
|
||||
* We use fread() in a loop to protect against partial reads
|
||||
*/
|
||||
do {
|
||||
/**
|
||||
* @var string|bool
|
||||
*/
|
||||
$read = fread($fp, $remaining);
|
||||
if (!is_string($read)) {
|
||||
/**
|
||||
* We cannot safely read from the file. Exit the
|
||||
* do-while loop and trigger the exception condition
|
||||
*
|
||||
* @var string|bool
|
||||
*/
|
||||
$buf = false;
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* Decrease the number of bytes returned from remaining
|
||||
*/
|
||||
$remaining -= RandomCompat_strlen($read);
|
||||
/**
|
||||
* @var string $buf
|
||||
*/
|
||||
$buf .= $read;
|
||||
} while ($remaining > 0);
|
||||
|
||||
/**
|
||||
* Is our result valid?
|
||||
* @var string|bool $buf
|
||||
*/
|
||||
if (is_string($buf)) {
|
||||
if (RandomCompat_strlen($buf) === $bytes) {
|
||||
/**
|
||||
* Return our random entropy buffer here:
|
||||
*/
|
||||
return $buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we reach here, PHP has failed us.
|
||||
*/
|
||||
throw new Exception(
|
||||
'Error reading from source device'
|
||||
);
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (!is_callable('random_bytes')) {
|
||||
/**
|
||||
* If the libsodium PHP extension is loaded, we'll use it above any other
|
||||
* solution.
|
||||
*
|
||||
* libsodium-php project:
|
||||
* @ref https://github.com/jedisct1/libsodium-php
|
||||
*
|
||||
* @param int $bytes
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function random_bytes($bytes)
|
||||
{
|
||||
try {
|
||||
/** @var int $bytes */
|
||||
$bytes = RandomCompat_intval($bytes);
|
||||
} catch (TypeError $ex) {
|
||||
throw new TypeError(
|
||||
'random_bytes(): $bytes must be an integer'
|
||||
);
|
||||
}
|
||||
|
||||
if ($bytes < 1) {
|
||||
throw new Error(
|
||||
'Length must be greater than 0'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be
|
||||
* generated in one invocation.
|
||||
*/
|
||||
/** @var string|bool $buf */
|
||||
if ($bytes > 2147483647) {
|
||||
$buf = '';
|
||||
for ($i = 0; $i < $bytes; $i += 1073741824) {
|
||||
$n = ($bytes - $i) > 1073741824
|
||||
? 1073741824
|
||||
: $bytes - $i;
|
||||
$buf .= \Sodium\randombytes_buf($n);
|
||||
}
|
||||
} else {
|
||||
/** @var string|bool $buf */
|
||||
$buf = \Sodium\randombytes_buf($bytes);
|
||||
}
|
||||
|
||||
if (is_string($buf)) {
|
||||
if (RandomCompat_strlen($buf) === $bytes) {
|
||||
return $buf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we reach here, PHP has failed us.
|
||||
*/
|
||||
throw new Exception(
|
||||
'Could not gather sufficient random data'
|
||||
);
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (!is_callable('random_bytes')) {
|
||||
/**
|
||||
* If the libsodium PHP extension is loaded, we'll use it above any other
|
||||
* solution.
|
||||
*
|
||||
* libsodium-php project:
|
||||
* @ref https://github.com/jedisct1/libsodium-php
|
||||
*
|
||||
* @param int $bytes
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function random_bytes($bytes)
|
||||
{
|
||||
try {
|
||||
/** @var int $bytes */
|
||||
$bytes = RandomCompat_intval($bytes);
|
||||
} catch (TypeError $ex) {
|
||||
throw new TypeError(
|
||||
'random_bytes(): $bytes must be an integer'
|
||||
);
|
||||
}
|
||||
|
||||
if ($bytes < 1) {
|
||||
throw new Error(
|
||||
'Length must be greater than 0'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
$buf = '';
|
||||
|
||||
/**
|
||||
* \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be
|
||||
* generated in one invocation.
|
||||
*/
|
||||
if ($bytes > 2147483647) {
|
||||
for ($i = 0; $i < $bytes; $i += 1073741824) {
|
||||
$n = ($bytes - $i) > 1073741824
|
||||
? 1073741824
|
||||
: $bytes - $i;
|
||||
$buf .= Sodium::randombytes_buf((int) $n);
|
||||
}
|
||||
} else {
|
||||
$buf .= Sodium::randombytes_buf((int) $bytes);
|
||||
}
|
||||
|
||||
if (is_string($buf)) {
|
||||
if (RandomCompat_strlen($buf) === $bytes) {
|
||||
return $buf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we reach here, PHP has failed us.
|
||||
*/
|
||||
throw new Exception(
|
||||
'Could not gather sufficient random data'
|
||||
);
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (!is_callable('random_bytes')) {
|
||||
/**
|
||||
* Powered by ext/mcrypt (and thankfully NOT libmcrypt)
|
||||
*
|
||||
* @ref https://bugs.php.net/bug.php?id=55169
|
||||
* @ref https://github.com/php/php-src/blob/c568ffe5171d942161fc8dda066bce844bdef676/ext/mcrypt/mcrypt.c#L1321-L1386
|
||||
*
|
||||
* @param int $bytes
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function random_bytes($bytes)
|
||||
{
|
||||
try {
|
||||
/** @var int $bytes */
|
||||
$bytes = RandomCompat_intval($bytes);
|
||||
} catch (TypeError $ex) {
|
||||
throw new TypeError(
|
||||
'random_bytes(): $bytes must be an integer'
|
||||
);
|
||||
}
|
||||
|
||||
if ($bytes < 1) {
|
||||
throw new Error(
|
||||
'Length must be greater than 0'
|
||||
);
|
||||
}
|
||||
|
||||
/** @var string|bool $buf */
|
||||
$buf = @mcrypt_create_iv((int) $bytes, (int) MCRYPT_DEV_URANDOM);
|
||||
if (
|
||||
is_string($buf)
|
||||
&&
|
||||
RandomCompat_strlen($buf) === $bytes
|
||||
) {
|
||||
/**
|
||||
* Return our random entropy buffer here:
|
||||
*/
|
||||
return $buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* If we reach here, PHP has failed us.
|
||||
*/
|
||||
throw new Exception(
|
||||
'Could not gather sufficient random data'
|
||||
);
|
||||
}
|
||||
}
|
204
vendor/paragonie/random_compat/lib/random_int.php
vendored
204
vendor/paragonie/random_compat/lib/random_int.php
vendored
@ -1,204 +0,0 @@
|
||||
<?php
|
||||
|
||||
if (!is_callable('random_int')) {
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetch a random integer between $min and $max inclusive
|
||||
*
|
||||
* @param int $min
|
||||
* @param int $max
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function random_int($min, $max)
|
||||
{
|
||||
/**
|
||||
* Type and input logic checks
|
||||
*
|
||||
* If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX)
|
||||
* (non-inclusive), it will sanely cast it to an int. If you it's equal to
|
||||
* ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats
|
||||
* lose precision, so the <= and => operators might accidentally let a float
|
||||
* through.
|
||||
*/
|
||||
|
||||
try {
|
||||
/** @var int $min */
|
||||
$min = RandomCompat_intval($min);
|
||||
} catch (TypeError $ex) {
|
||||
throw new TypeError(
|
||||
'random_int(): $min must be an integer'
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var int $max */
|
||||
$max = RandomCompat_intval($max);
|
||||
} catch (TypeError $ex) {
|
||||
throw new TypeError(
|
||||
'random_int(): $max must be an integer'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Now that we've verified our weak typing system has given us an integer,
|
||||
* let's validate the logic then we can move forward with generating random
|
||||
* integers along a given range.
|
||||
*/
|
||||
if ($min > $max) {
|
||||
throw new Error(
|
||||
'Minimum value must be less than or equal to the maximum value'
|
||||
);
|
||||
}
|
||||
|
||||
if ($max === $min) {
|
||||
return (int) $min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize variables to 0
|
||||
*
|
||||
* We want to store:
|
||||
* $bytes => the number of random bytes we need
|
||||
* $mask => an integer bitmask (for use with the &) operator
|
||||
* so we can minimize the number of discards
|
||||
*/
|
||||
$attempts = $bits = $bytes = $mask = $valueShift = 0;
|
||||
/** @var int $attempts */
|
||||
/** @var int $bits */
|
||||
/** @var int $bytes */
|
||||
/** @var int $mask */
|
||||
/** @var int $valueShift */
|
||||
|
||||
/**
|
||||
* At this point, $range is a positive number greater than 0. It might
|
||||
* overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to
|
||||
* a float and we will lose some precision.
|
||||
*
|
||||
* @var int|float $range
|
||||
*/
|
||||
$range = $max - $min;
|
||||
|
||||
/**
|
||||
* Test for integer overflow:
|
||||
*/
|
||||
if (!is_int($range)) {
|
||||
|
||||
/**
|
||||
* Still safely calculate wider ranges.
|
||||
* Provided by @CodesInChaos, @oittaa
|
||||
*
|
||||
* @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435
|
||||
*
|
||||
* We use ~0 as a mask in this case because it generates all 1s
|
||||
*
|
||||
* @ref https://eval.in/400356 (32-bit)
|
||||
* @ref http://3v4l.org/XX9r5 (64-bit)
|
||||
*/
|
||||
$bytes = PHP_INT_SIZE;
|
||||
/** @var int $mask */
|
||||
$mask = ~0;
|
||||
|
||||
} else {
|
||||
|
||||
/**
|
||||
* $bits is effectively ceil(log($range, 2)) without dealing with
|
||||
* type juggling
|
||||
*/
|
||||
while ($range > 0) {
|
||||
if ($bits % 8 === 0) {
|
||||
++$bytes;
|
||||
}
|
||||
++$bits;
|
||||
$range >>= 1;
|
||||
/** @var int $mask */
|
||||
$mask = $mask << 1 | 1;
|
||||
}
|
||||
$valueShift = $min;
|
||||
}
|
||||
|
||||
/** @var int $val */
|
||||
$val = 0;
|
||||
/**
|
||||
* Now that we have our parameters set up, let's begin generating
|
||||
* random integers until one falls between $min and $max
|
||||
*/
|
||||
/** @psalm-suppress RedundantCondition */
|
||||
do {
|
||||
/**
|
||||
* The rejection probability is at most 0.5, so this corresponds
|
||||
* to a failure probability of 2^-128 for a working RNG
|
||||
*/
|
||||
if ($attempts > 128) {
|
||||
throw new Exception(
|
||||
'random_int: RNG is broken - too many rejections'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Let's grab the necessary number of random bytes
|
||||
*/
|
||||
$randomByteString = random_bytes($bytes);
|
||||
|
||||
/**
|
||||
* Let's turn $randomByteString into an integer
|
||||
*
|
||||
* This uses bitwise operators (<< and |) to build an integer
|
||||
* out of the values extracted from ord()
|
||||
*
|
||||
* Example: [9F] | [6D] | [32] | [0C] =>
|
||||
* 159 + 27904 + 3276800 + 201326592 =>
|
||||
* 204631455
|
||||
*/
|
||||
$val &= 0;
|
||||
for ($i = 0; $i < $bytes; ++$i) {
|
||||
$val |= ord($randomByteString[$i]) << ($i * 8);
|
||||
}
|
||||
/** @var int $val */
|
||||
|
||||
/**
|
||||
* Apply mask
|
||||
*/
|
||||
$val &= $mask;
|
||||
$val += $valueShift;
|
||||
|
||||
++$attempts;
|
||||
/**
|
||||
* If $val overflows to a floating point number,
|
||||
* ... or is larger than $max,
|
||||
* ... or smaller than $min,
|
||||
* then try again.
|
||||
*/
|
||||
} while (!is_int($val) || $val > $max || $val < $min);
|
||||
|
||||
return (int) $val;
|
||||
}
|
||||
}
|
22
vendor/predis/predis/LICENSE
vendored
22
vendor/predis/predis/LICENSE
vendored
@ -1,22 +0,0 @@
|
||||
Copyright (c) 2009-2016 Daniele Alessandri
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
14
vendor/predis/predis/autoload.php
vendored
14
vendor/predis/predis/autoload.php
vendored
@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__.'/src/Autoloader.php';
|
||||
|
||||
Predis\Autoloader::register();
|
62
vendor/predis/predis/src/Autoloader.php
vendored
62
vendor/predis/predis/src/Autoloader.php
vendored
@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
/**
|
||||
* Implements a lightweight PSR-0 compliant autoloader for Predis.
|
||||
*
|
||||
* @author Eric Naeseth <eric@thumbtack.com>
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class Autoloader
|
||||
{
|
||||
private $directory;
|
||||
private $prefix;
|
||||
private $prefixLength;
|
||||
|
||||
/**
|
||||
* @param string $baseDirectory Base directory where the source files are located.
|
||||
*/
|
||||
public function __construct($baseDirectory = __DIR__)
|
||||
{
|
||||
$this->directory = $baseDirectory;
|
||||
$this->prefix = __NAMESPACE__.'\\';
|
||||
$this->prefixLength = strlen($this->prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the autoloader class with the PHP SPL autoloader.
|
||||
*
|
||||
* @param bool $prepend Prepend the autoloader on the stack instead of appending it.
|
||||
*/
|
||||
public static function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array(new self(), 'autoload'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a class from a file using its fully qualified name.
|
||||
*
|
||||
* @param string $className Fully qualified name of a class.
|
||||
*/
|
||||
public function autoload($className)
|
||||
{
|
||||
if (0 === strpos($className, $this->prefix)) {
|
||||
$parts = explode('\\', substr($className, $this->prefixLength));
|
||||
$filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
|
||||
|
||||
if (is_file($filepath)) {
|
||||
require $filepath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
547
vendor/predis/predis/src/Client.php
vendored
547
vendor/predis/predis/src/Client.php
vendored
@ -1,547 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\RawCommand;
|
||||
use Predis\Command\ScriptCommand;
|
||||
use Predis\Configuration\Options;
|
||||
use Predis\Configuration\OptionsInterface;
|
||||
use Predis\Connection\AggregateConnectionInterface;
|
||||
use Predis\Connection\ConnectionInterface;
|
||||
use Predis\Connection\ParametersInterface;
|
||||
use Predis\Monitor\Consumer as MonitorConsumer;
|
||||
use Predis\Pipeline\Pipeline;
|
||||
use Predis\PubSub\Consumer as PubSubConsumer;
|
||||
use Predis\Response\ErrorInterface as ErrorResponseInterface;
|
||||
use Predis\Response\ResponseInterface;
|
||||
use Predis\Response\ServerException;
|
||||
use Predis\Transaction\MultiExec as MultiExecTransaction;
|
||||
|
||||
/**
|
||||
* Client class used for connecting and executing commands on Redis.
|
||||
*
|
||||
* This is the main high-level abstraction of Predis upon which various other
|
||||
* abstractions are built. Internally it aggregates various other classes each
|
||||
* one with its own responsibility and scope.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class Client implements ClientInterface, \IteratorAggregate
|
||||
{
|
||||
const VERSION = '1.1.1';
|
||||
|
||||
protected $connection;
|
||||
protected $options;
|
||||
private $profile;
|
||||
|
||||
/**
|
||||
* @param mixed $parameters Connection parameters for one or more servers.
|
||||
* @param mixed $options Options to configure some behaviours of the client.
|
||||
*/
|
||||
public function __construct($parameters = null, $options = null)
|
||||
{
|
||||
$this->options = $this->createOptions($options ?: array());
|
||||
$this->connection = $this->createConnection($parameters ?: array());
|
||||
$this->profile = $this->options->profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of Predis\Configuration\Options from different
|
||||
* types of arguments or simply returns the passed argument if it is an
|
||||
* instance of Predis\Configuration\OptionsInterface.
|
||||
*
|
||||
* @param mixed $options Client options.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return OptionsInterface
|
||||
*/
|
||||
protected function createOptions($options)
|
||||
{
|
||||
if (is_array($options)) {
|
||||
return new Options($options);
|
||||
}
|
||||
|
||||
if ($options instanceof OptionsInterface) {
|
||||
return $options;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Invalid type for client options.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates single or aggregate connections from different types of arguments
|
||||
* (string, array) or returns the passed argument if it is an instance of a
|
||||
* class implementing Predis\Connection\ConnectionInterface.
|
||||
*
|
||||
* Accepted types for connection parameters are:
|
||||
*
|
||||
* - Instance of Predis\Connection\ConnectionInterface.
|
||||
* - Instance of Predis\Connection\ParametersInterface.
|
||||
* - Array
|
||||
* - String
|
||||
* - Callable
|
||||
*
|
||||
* @param mixed $parameters Connection parameters or connection instance.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
protected function createConnection($parameters)
|
||||
{
|
||||
if ($parameters instanceof ConnectionInterface) {
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
if ($parameters instanceof ParametersInterface || is_string($parameters)) {
|
||||
return $this->options->connections->create($parameters);
|
||||
}
|
||||
|
||||
if (is_array($parameters)) {
|
||||
if (!isset($parameters[0])) {
|
||||
return $this->options->connections->create($parameters);
|
||||
}
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
if ($options->defined('aggregate')) {
|
||||
$initializer = $this->getConnectionInitializerWrapper($options->aggregate);
|
||||
$connection = $initializer($parameters, $options);
|
||||
} elseif ($options->defined('replication')) {
|
||||
$replication = $options->replication;
|
||||
|
||||
if ($replication instanceof AggregateConnectionInterface) {
|
||||
$connection = $replication;
|
||||
$options->connections->aggregate($connection, $parameters);
|
||||
} else {
|
||||
$initializer = $this->getConnectionInitializerWrapper($replication);
|
||||
$connection = $initializer($parameters, $options);
|
||||
}
|
||||
} else {
|
||||
$connection = $options->cluster;
|
||||
$options->connections->aggregate($connection, $parameters);
|
||||
}
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
if (is_callable($parameters)) {
|
||||
$initializer = $this->getConnectionInitializerWrapper($parameters);
|
||||
$connection = $initializer($this->options);
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Invalid type for connection parameters.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a callable to make sure that its returned value represents a valid
|
||||
* connection type.
|
||||
*
|
||||
* @param mixed $callable
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function getConnectionInitializerWrapper($callable)
|
||||
{
|
||||
return function () use ($callable) {
|
||||
$connection = call_user_func_array($callable, func_get_args());
|
||||
|
||||
if (!$connection instanceof ConnectionInterface) {
|
||||
throw new \UnexpectedValueException(
|
||||
'The callable connection initializer returned an invalid type.'
|
||||
);
|
||||
}
|
||||
|
||||
return $connection;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProfile()
|
||||
{
|
||||
return $this->profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new client instance for the specified connection ID or alias,
|
||||
* only when working with an aggregate connection (cluster, replication).
|
||||
* The new client instances uses the same options of the original one.
|
||||
*
|
||||
* @param string $connectionID Identifier of a connection.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function getClientFor($connectionID)
|
||||
{
|
||||
if (!$connection = $this->getConnectionById($connectionID)) {
|
||||
throw new \InvalidArgumentException("Invalid connection ID: $connectionID.");
|
||||
}
|
||||
|
||||
return new static($connection, $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the underlying connection and connects to the server.
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
$this->connection->connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying connection and disconnects from the server.
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
$this->connection->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying connection and disconnects from the server.
|
||||
*
|
||||
* This is the same as `Client::disconnect()` as it does not actually send
|
||||
* the `QUIT` command to Redis, but simply closes the connection.
|
||||
*/
|
||||
public function quit()
|
||||
{
|
||||
$this->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state of the underlying connection.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnected()
|
||||
{
|
||||
return $this->connection->isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the specified connection from the aggregate connection when the
|
||||
* client is in cluster or replication mode.
|
||||
*
|
||||
* @param string $connectionID Index or alias of the single connection.
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
*
|
||||
* @return Connection\NodeConnectionInterface
|
||||
*/
|
||||
public function getConnectionById($connectionID)
|
||||
{
|
||||
if (!$this->connection instanceof AggregateConnectionInterface) {
|
||||
throw new NotSupportedException(
|
||||
'Retrieving connections by ID is supported only by aggregate connections.'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->connection->getConnectionById($connectionID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a command without filtering its arguments, parsing the response,
|
||||
* applying any prefix to keys or throwing exceptions on Redis errors even
|
||||
* regardless of client options.
|
||||
*
|
||||
* It is possible to identify Redis error responses from normal responses
|
||||
* using the second optional argument which is populated by reference.
|
||||
*
|
||||
* @param array $arguments Command arguments as defined by the command signature.
|
||||
* @param bool $error Set to TRUE when Redis returned an error response.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeRaw(array $arguments, &$error = null)
|
||||
{
|
||||
$error = false;
|
||||
|
||||
$response = $this->connection->executeCommand(
|
||||
new RawCommand($arguments)
|
||||
);
|
||||
|
||||
if ($response instanceof ResponseInterface) {
|
||||
if ($response instanceof ErrorResponseInterface) {
|
||||
$error = true;
|
||||
}
|
||||
|
||||
return (string) $response;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __call($commandID, $arguments)
|
||||
{
|
||||
return $this->executeCommand(
|
||||
$this->createCommand($commandID, $arguments)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createCommand($commandID, $arguments = array())
|
||||
{
|
||||
return $this->profile->createCommand($commandID, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function executeCommand(CommandInterface $command)
|
||||
{
|
||||
$response = $this->connection->executeCommand($command);
|
||||
|
||||
if ($response instanceof ResponseInterface) {
|
||||
if ($response instanceof ErrorResponseInterface) {
|
||||
$response = $this->onErrorResponse($command, $response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
return $command->parseResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles -ERR responses returned by Redis.
|
||||
*
|
||||
* @param CommandInterface $command Redis command that generated the error.
|
||||
* @param ErrorResponseInterface $response Instance of the error response.
|
||||
*
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
|
||||
{
|
||||
if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
|
||||
$eval = $this->createCommand('EVAL');
|
||||
$eval->setRawArguments($command->getEvalArguments());
|
||||
|
||||
$response = $this->executeCommand($eval);
|
||||
|
||||
if (!$response instanceof ResponseInterface) {
|
||||
$response = $command->parseResponse($response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($this->options->exceptions) {
|
||||
throw new ServerException($response->getMessage());
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the specified initializer method on `$this` by adjusting the
|
||||
* actual invokation depending on the arity (0, 1 or 2 arguments). This is
|
||||
* simply an utility method to create Redis contexts instances since they
|
||||
* follow a common initialization path.
|
||||
*
|
||||
* @param string $initializer Method name.
|
||||
* @param array $argv Arguments for the method.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function sharedContextFactory($initializer, $argv = null)
|
||||
{
|
||||
switch (count($argv)) {
|
||||
case 0:
|
||||
return $this->$initializer();
|
||||
|
||||
case 1:
|
||||
return is_array($argv[0])
|
||||
? $this->$initializer($argv[0])
|
||||
: $this->$initializer(null, $argv[0]);
|
||||
|
||||
case 2:
|
||||
list($arg0, $arg1) = $argv;
|
||||
|
||||
return $this->$initializer($arg0, $arg1);
|
||||
|
||||
default:
|
||||
return $this->$initializer($this, $argv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new pipeline context and returns it, or returns the results of
|
||||
* a pipeline executed inside the optionally provided callable object.
|
||||
*
|
||||
* @param mixed ... Array of options, a callable for execution, or both.
|
||||
*
|
||||
* @return Pipeline|array
|
||||
*/
|
||||
public function pipeline(/* arguments */)
|
||||
{
|
||||
return $this->sharedContextFactory('createPipeline', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual pipeline context initializer method.
|
||||
*
|
||||
* @param array $options Options for the context.
|
||||
* @param mixed $callable Optional callable used to execute the context.
|
||||
*
|
||||
* @return Pipeline|array
|
||||
*/
|
||||
protected function createPipeline(array $options = null, $callable = null)
|
||||
{
|
||||
if (isset($options['atomic']) && $options['atomic']) {
|
||||
$class = 'Predis\Pipeline\Atomic';
|
||||
} elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) {
|
||||
$class = 'Predis\Pipeline\FireAndForget';
|
||||
} else {
|
||||
$class = 'Predis\Pipeline\Pipeline';
|
||||
}
|
||||
|
||||
/*
|
||||
* @var ClientContextInterface
|
||||
*/
|
||||
$pipeline = new $class($this);
|
||||
|
||||
if (isset($callable)) {
|
||||
return $pipeline->execute($callable);
|
||||
}
|
||||
|
||||
return $pipeline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new transaction context and returns it, or returns the results
|
||||
* of a transaction executed inside the optionally provided callable object.
|
||||
*
|
||||
* @param mixed ... Array of options, a callable for execution, or both.
|
||||
*
|
||||
* @return MultiExecTransaction|array
|
||||
*/
|
||||
public function transaction(/* arguments */)
|
||||
{
|
||||
return $this->sharedContextFactory('createTransaction', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual transaction context initializer method.
|
||||
*
|
||||
* @param array $options Options for the context.
|
||||
* @param mixed $callable Optional callable used to execute the context.
|
||||
*
|
||||
* @return MultiExecTransaction|array
|
||||
*/
|
||||
protected function createTransaction(array $options = null, $callable = null)
|
||||
{
|
||||
$transaction = new MultiExecTransaction($this, $options);
|
||||
|
||||
if (isset($callable)) {
|
||||
return $transaction->execute($callable);
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new publish/subscribe context and returns it, or starts its loop
|
||||
* inside the optionally provided callable object.
|
||||
*
|
||||
* @param mixed ... Array of options, a callable for execution, or both.
|
||||
*
|
||||
* @return PubSubConsumer|null
|
||||
*/
|
||||
public function pubSubLoop(/* arguments */)
|
||||
{
|
||||
return $this->sharedContextFactory('createPubSub', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual publish/subscribe context initializer method.
|
||||
*
|
||||
* @param array $options Options for the context.
|
||||
* @param mixed $callable Optional callable used to execute the context.
|
||||
*
|
||||
* @return PubSubConsumer|null
|
||||
*/
|
||||
protected function createPubSub(array $options = null, $callable = null)
|
||||
{
|
||||
$pubsub = new PubSubConsumer($this, $options);
|
||||
|
||||
if (!isset($callable)) {
|
||||
return $pubsub;
|
||||
}
|
||||
|
||||
foreach ($pubsub as $message) {
|
||||
if (call_user_func($callable, $pubsub, $message) === false) {
|
||||
$pubsub->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new monitor consumer and returns it.
|
||||
*
|
||||
* @return MonitorConsumer
|
||||
*/
|
||||
public function monitor()
|
||||
{
|
||||
return new MonitorConsumer($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
$clients = array();
|
||||
$connection = $this->getConnection();
|
||||
|
||||
if (!$connection instanceof \Traversable) {
|
||||
throw new ClientException('The underlying connection is not traversable');
|
||||
}
|
||||
|
||||
foreach ($connection as $node) {
|
||||
$clients[(string) $node] = new static($node, $this->getOptions());
|
||||
}
|
||||
|
||||
return new \ArrayIterator($clients);
|
||||
}
|
||||
}
|
198
vendor/predis/predis/src/ClientContextInterface.php
vendored
198
vendor/predis/predis/src/ClientContextInterface.php
vendored
@ -1,198 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
use Predis\Command\CommandInterface;
|
||||
|
||||
/**
|
||||
* Interface defining a client-side context such as a pipeline or transaction.
|
||||
*
|
||||
* @method $this del(array $keys)
|
||||
* @method $this dump($key)
|
||||
* @method $this exists($key)
|
||||
* @method $this expire($key, $seconds)
|
||||
* @method $this expireat($key, $timestamp)
|
||||
* @method $this keys($pattern)
|
||||
* @method $this move($key, $db)
|
||||
* @method $this object($subcommand, $key)
|
||||
* @method $this persist($key)
|
||||
* @method $this pexpire($key, $milliseconds)
|
||||
* @method $this pexpireat($key, $timestamp)
|
||||
* @method $this pttl($key)
|
||||
* @method $this randomkey()
|
||||
* @method $this rename($key, $target)
|
||||
* @method $this renamenx($key, $target)
|
||||
* @method $this scan($cursor, array $options = null)
|
||||
* @method $this sort($key, array $options = null)
|
||||
* @method $this ttl($key)
|
||||
* @method $this type($key)
|
||||
* @method $this append($key, $value)
|
||||
* @method $this bitcount($key, $start = null, $end = null)
|
||||
* @method $this bitop($operation, $destkey, $key)
|
||||
* @method $this bitfield($key, $subcommand, ...$subcommandArg)
|
||||
* @method $this decr($key)
|
||||
* @method $this decrby($key, $decrement)
|
||||
* @method $this get($key)
|
||||
* @method $this getbit($key, $offset)
|
||||
* @method $this getrange($key, $start, $end)
|
||||
* @method $this getset($key, $value)
|
||||
* @method $this incr($key)
|
||||
* @method $this incrby($key, $increment)
|
||||
* @method $this incrbyfloat($key, $increment)
|
||||
* @method $this mget(array $keys)
|
||||
* @method $this mset(array $dictionary)
|
||||
* @method $this msetnx(array $dictionary)
|
||||
* @method $this psetex($key, $milliseconds, $value)
|
||||
* @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method $this setbit($key, $offset, $value)
|
||||
* @method $this setex($key, $seconds, $value)
|
||||
* @method $this setnx($key, $value)
|
||||
* @method $this setrange($key, $offset, $value)
|
||||
* @method $this strlen($key)
|
||||
* @method $this hdel($key, array $fields)
|
||||
* @method $this hexists($key, $field)
|
||||
* @method $this hget($key, $field)
|
||||
* @method $this hgetall($key)
|
||||
* @method $this hincrby($key, $field, $increment)
|
||||
* @method $this hincrbyfloat($key, $field, $increment)
|
||||
* @method $this hkeys($key)
|
||||
* @method $this hlen($key)
|
||||
* @method $this hmget($key, array $fields)
|
||||
* @method $this hmset($key, array $dictionary)
|
||||
* @method $this hscan($key, $cursor, array $options = null)
|
||||
* @method $this hset($key, $field, $value)
|
||||
* @method $this hsetnx($key, $field, $value)
|
||||
* @method $this hvals($key)
|
||||
* @method $this hstrlen($key, $field)
|
||||
* @method $this blpop(array $keys, $timeout)
|
||||
* @method $this brpop(array $keys, $timeout)
|
||||
* @method $this brpoplpush($source, $destination, $timeout)
|
||||
* @method $this lindex($key, $index)
|
||||
* @method $this linsert($key, $whence, $pivot, $value)
|
||||
* @method $this llen($key)
|
||||
* @method $this lpop($key)
|
||||
* @method $this lpush($key, array $values)
|
||||
* @method $this lpushx($key, $value)
|
||||
* @method $this lrange($key, $start, $stop)
|
||||
* @method $this lrem($key, $count, $value)
|
||||
* @method $this lset($key, $index, $value)
|
||||
* @method $this ltrim($key, $start, $stop)
|
||||
* @method $this rpop($key)
|
||||
* @method $this rpoplpush($source, $destination)
|
||||
* @method $this rpush($key, array $values)
|
||||
* @method $this rpushx($key, $value)
|
||||
* @method $this sadd($key, array $members)
|
||||
* @method $this scard($key)
|
||||
* @method $this sdiff(array $keys)
|
||||
* @method $this sdiffstore($destination, array $keys)
|
||||
* @method $this sinter(array $keys)
|
||||
* @method $this sinterstore($destination, array $keys)
|
||||
* @method $this sismember($key, $member)
|
||||
* @method $this smembers($key)
|
||||
* @method $this smove($source, $destination, $member)
|
||||
* @method $this spop($key, $count = null)
|
||||
* @method $this srandmember($key, $count = null)
|
||||
* @method $this srem($key, $member)
|
||||
* @method $this sscan($key, $cursor, array $options = null)
|
||||
* @method $this sunion(array $keys)
|
||||
* @method $this sunionstore($destination, array $keys)
|
||||
* @method $this zadd($key, array $membersAndScoresDictionary)
|
||||
* @method $this zcard($key)
|
||||
* @method $this zcount($key, $min, $max)
|
||||
* @method $this zincrby($key, $increment, $member)
|
||||
* @method $this zinterstore($destination, array $keys, array $options = null)
|
||||
* @method $this zrange($key, $start, $stop, array $options = null)
|
||||
* @method $this zrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method $this zrank($key, $member)
|
||||
* @method $this zrem($key, $member)
|
||||
* @method $this zremrangebyrank($key, $start, $stop)
|
||||
* @method $this zremrangebyscore($key, $min, $max)
|
||||
* @method $this zrevrange($key, $start, $stop, array $options = null)
|
||||
* @method $this zrevrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method $this zrevrank($key, $member)
|
||||
* @method $this zunionstore($destination, array $keys, array $options = null)
|
||||
* @method $this zscore($key, $member)
|
||||
* @method $this zscan($key, $cursor, array $options = null)
|
||||
* @method $this zrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method $this zrevrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method $this zremrangebylex($key, $min, $max)
|
||||
* @method $this zlexcount($key, $min, $max)
|
||||
* @method $this pfadd($key, array $elements)
|
||||
* @method $this pfmerge($destinationKey, array $sourceKeys)
|
||||
* @method $this pfcount(array $keys)
|
||||
* @method $this pubsub($subcommand, $argument)
|
||||
* @method $this publish($channel, $message)
|
||||
* @method $this discard()
|
||||
* @method $this exec()
|
||||
* @method $this multi()
|
||||
* @method $this unwatch()
|
||||
* @method $this watch($key)
|
||||
* @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method $this script($subcommand, $argument = null)
|
||||
* @method $this auth($password)
|
||||
* @method $this echo($message)
|
||||
* @method $this ping($message = null)
|
||||
* @method $this select($database)
|
||||
* @method $this bgrewriteaof()
|
||||
* @method $this bgsave()
|
||||
* @method $this client($subcommand, $argument = null)
|
||||
* @method $this config($subcommand, $argument = null)
|
||||
* @method $this dbsize()
|
||||
* @method $this flushall()
|
||||
* @method $this flushdb()
|
||||
* @method $this info($section = null)
|
||||
* @method $this lastsave()
|
||||
* @method $this save()
|
||||
* @method $this slaveof($host, $port)
|
||||
* @method $this slowlog($subcommand, $argument = null)
|
||||
* @method $this time()
|
||||
* @method $this command()
|
||||
* @method $this geoadd($key, $longitude, $latitude, $member)
|
||||
* @method $this geohash($key, array $members)
|
||||
* @method $this geopos($key, array $members)
|
||||
* @method $this geodist($key, $member1, $member2, $unit = null)
|
||||
* @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
|
||||
* @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null)
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface ClientContextInterface
|
||||
{
|
||||
/**
|
||||
* Sends the specified command instance to Redis.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeCommand(CommandInterface $command);
|
||||
|
||||
/**
|
||||
* Sends the specified command with its arguments to Redis.
|
||||
*
|
||||
* @param string $method Command ID.
|
||||
* @param array $arguments Arguments for the command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $arguments);
|
||||
|
||||
/**
|
||||
* Starts the execution of the context.
|
||||
*
|
||||
* @param mixed $callable Optional callback for execution.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function execute($callable = null);
|
||||
}
|
21
vendor/predis/predis/src/ClientException.php
vendored
21
vendor/predis/predis/src/ClientException.php
vendored
@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
/**
|
||||
* Exception class that identifies client-side errors.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ClientException extends PredisException
|
||||
{
|
||||
}
|
239
vendor/predis/predis/src/ClientInterface.php
vendored
239
vendor/predis/predis/src/ClientInterface.php
vendored
@ -1,239 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Configuration\OptionsInterface;
|
||||
use Predis\Connection\ConnectionInterface;
|
||||
use Predis\Profile\ProfileInterface;
|
||||
|
||||
/**
|
||||
* Interface defining a client able to execute commands against Redis.
|
||||
*
|
||||
* All the commands exposed by the client generally have the same signature as
|
||||
* described by the Redis documentation, but some of them offer an additional
|
||||
* and more friendly interface to ease programming which is described in the
|
||||
* following list of methods:
|
||||
*
|
||||
* @method int del(array $keys)
|
||||
* @method string dump($key)
|
||||
* @method int exists($key)
|
||||
* @method int expire($key, $seconds)
|
||||
* @method int expireat($key, $timestamp)
|
||||
* @method array keys($pattern)
|
||||
* @method int move($key, $db)
|
||||
* @method mixed object($subcommand, $key)
|
||||
* @method int persist($key)
|
||||
* @method int pexpire($key, $milliseconds)
|
||||
* @method int pexpireat($key, $timestamp)
|
||||
* @method int pttl($key)
|
||||
* @method string randomkey()
|
||||
* @method mixed rename($key, $target)
|
||||
* @method int renamenx($key, $target)
|
||||
* @method array scan($cursor, array $options = null)
|
||||
* @method array sort($key, array $options = null)
|
||||
* @method int ttl($key)
|
||||
* @method mixed type($key)
|
||||
* @method int append($key, $value)
|
||||
* @method int bitcount($key, $start = null, $end = null)
|
||||
* @method int bitop($operation, $destkey, $key)
|
||||
* @method array bitfield($key, $subcommand, ...$subcommandArg)
|
||||
* @method int decr($key)
|
||||
* @method int decrby($key, $decrement)
|
||||
* @method string get($key)
|
||||
* @method int getbit($key, $offset)
|
||||
* @method string getrange($key, $start, $end)
|
||||
* @method string getset($key, $value)
|
||||
* @method int incr($key)
|
||||
* @method int incrby($key, $increment)
|
||||
* @method string incrbyfloat($key, $increment)
|
||||
* @method array mget(array $keys)
|
||||
* @method mixed mset(array $dictionary)
|
||||
* @method int msetnx(array $dictionary)
|
||||
* @method mixed psetex($key, $milliseconds, $value)
|
||||
* @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method int setbit($key, $offset, $value)
|
||||
* @method int setex($key, $seconds, $value)
|
||||
* @method int setnx($key, $value)
|
||||
* @method int setrange($key, $offset, $value)
|
||||
* @method int strlen($key)
|
||||
* @method int hdel($key, array $fields)
|
||||
* @method int hexists($key, $field)
|
||||
* @method string hget($key, $field)
|
||||
* @method array hgetall($key)
|
||||
* @method int hincrby($key, $field, $increment)
|
||||
* @method string hincrbyfloat($key, $field, $increment)
|
||||
* @method array hkeys($key)
|
||||
* @method int hlen($key)
|
||||
* @method array hmget($key, array $fields)
|
||||
* @method mixed hmset($key, array $dictionary)
|
||||
* @method array hscan($key, $cursor, array $options = null)
|
||||
* @method int hset($key, $field, $value)
|
||||
* @method int hsetnx($key, $field, $value)
|
||||
* @method array hvals($key)
|
||||
* @method int hstrlen($key, $field)
|
||||
* @method array blpop(array $keys, $timeout)
|
||||
* @method array brpop(array $keys, $timeout)
|
||||
* @method array brpoplpush($source, $destination, $timeout)
|
||||
* @method string lindex($key, $index)
|
||||
* @method int linsert($key, $whence, $pivot, $value)
|
||||
* @method int llen($key)
|
||||
* @method string lpop($key)
|
||||
* @method int lpush($key, array $values)
|
||||
* @method int lpushx($key, $value)
|
||||
* @method array lrange($key, $start, $stop)
|
||||
* @method int lrem($key, $count, $value)
|
||||
* @method mixed lset($key, $index, $value)
|
||||
* @method mixed ltrim($key, $start, $stop)
|
||||
* @method string rpop($key)
|
||||
* @method string rpoplpush($source, $destination)
|
||||
* @method int rpush($key, array $values)
|
||||
* @method int rpushx($key, $value)
|
||||
* @method int sadd($key, array $members)
|
||||
* @method int scard($key)
|
||||
* @method array sdiff(array $keys)
|
||||
* @method int sdiffstore($destination, array $keys)
|
||||
* @method array sinter(array $keys)
|
||||
* @method int sinterstore($destination, array $keys)
|
||||
* @method int sismember($key, $member)
|
||||
* @method array smembers($key)
|
||||
* @method int smove($source, $destination, $member)
|
||||
* @method string spop($key, $count = null)
|
||||
* @method string srandmember($key, $count = null)
|
||||
* @method int srem($key, $member)
|
||||
* @method array sscan($key, $cursor, array $options = null)
|
||||
* @method array sunion(array $keys)
|
||||
* @method int sunionstore($destination, array $keys)
|
||||
* @method int zadd($key, array $membersAndScoresDictionary)
|
||||
* @method int zcard($key)
|
||||
* @method string zcount($key, $min, $max)
|
||||
* @method string zincrby($key, $increment, $member)
|
||||
* @method int zinterstore($destination, array $keys, array $options = null)
|
||||
* @method array zrange($key, $start, $stop, array $options = null)
|
||||
* @method array zrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method int zrank($key, $member)
|
||||
* @method int zrem($key, $member)
|
||||
* @method int zremrangebyrank($key, $start, $stop)
|
||||
* @method int zremrangebyscore($key, $min, $max)
|
||||
* @method array zrevrange($key, $start, $stop, array $options = null)
|
||||
* @method array zrevrangebyscore($key, $max, $min, array $options = null)
|
||||
* @method int zrevrank($key, $member)
|
||||
* @method int zunionstore($destination, array $keys, array $options = null)
|
||||
* @method string zscore($key, $member)
|
||||
* @method array zscan($key, $cursor, array $options = null)
|
||||
* @method array zrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method array zrevrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method int zremrangebylex($key, $min, $max)
|
||||
* @method int zlexcount($key, $min, $max)
|
||||
* @method int pfadd($key, array $elements)
|
||||
* @method mixed pfmerge($destinationKey, array $sourceKeys)
|
||||
* @method int pfcount(array $keys)
|
||||
* @method mixed pubsub($subcommand, $argument)
|
||||
* @method int publish($channel, $message)
|
||||
* @method mixed discard()
|
||||
* @method array exec()
|
||||
* @method mixed multi()
|
||||
* @method mixed unwatch()
|
||||
* @method mixed watch($key)
|
||||
* @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method mixed script($subcommand, $argument = null)
|
||||
* @method mixed auth($password)
|
||||
* @method string echo($message)
|
||||
* @method mixed ping($message = null)
|
||||
* @method mixed select($database)
|
||||
* @method mixed bgrewriteaof()
|
||||
* @method mixed bgsave()
|
||||
* @method mixed client($subcommand, $argument = null)
|
||||
* @method mixed config($subcommand, $argument = null)
|
||||
* @method int dbsize()
|
||||
* @method mixed flushall()
|
||||
* @method mixed flushdb()
|
||||
* @method array info($section = null)
|
||||
* @method int lastsave()
|
||||
* @method mixed save()
|
||||
* @method mixed slaveof($host, $port)
|
||||
* @method mixed slowlog($subcommand, $argument = null)
|
||||
* @method array time()
|
||||
* @method array command()
|
||||
* @method int geoadd($key, $longitude, $latitude, $member)
|
||||
* @method array geohash($key, array $members)
|
||||
* @method array geopos($key, array $members)
|
||||
* @method string geodist($key, $member1, $member2, $unit = null)
|
||||
* @method array georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
|
||||
* @method array georadiusbymember($key, $member, $radius, $unit, array $options = null)
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface ClientInterface
|
||||
{
|
||||
/**
|
||||
* Returns the server profile used by the client.
|
||||
*
|
||||
* @return ProfileInterface
|
||||
*/
|
||||
public function getProfile();
|
||||
|
||||
/**
|
||||
* Returns the client options specified upon initialization.
|
||||
*
|
||||
* @return OptionsInterface
|
||||
*/
|
||||
public function getOptions();
|
||||
|
||||
/**
|
||||
* Opens the underlying connection to the server.
|
||||
*/
|
||||
public function connect();
|
||||
|
||||
/**
|
||||
* Closes the underlying connection from the server.
|
||||
*/
|
||||
public function disconnect();
|
||||
|
||||
/**
|
||||
* Returns the underlying connection instance.
|
||||
*
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
public function getConnection();
|
||||
|
||||
/**
|
||||
* Creates a new instance of the specified Redis command.
|
||||
*
|
||||
* @param string $method Command ID.
|
||||
* @param array $arguments Arguments for the command.
|
||||
*
|
||||
* @return CommandInterface
|
||||
*/
|
||||
public function createCommand($method, $arguments = array());
|
||||
|
||||
/**
|
||||
* Executes the specified Redis command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeCommand(CommandInterface $command);
|
||||
|
||||
/**
|
||||
* Creates a Redis command with the specified arguments and sends a request
|
||||
* to the server.
|
||||
*
|
||||
* @param string $method Command ID.
|
||||
* @param array $arguments Arguments for the command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $arguments);
|
||||
}
|
469
vendor/predis/predis/src/Cluster/ClusterStrategy.php
vendored
469
vendor/predis/predis/src/Cluster/ClusterStrategy.php
vendored
@ -1,469 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\ScriptCommand;
|
||||
|
||||
/**
|
||||
* Common class implementing the logic needed to support clustering strategies.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
abstract class ClusterStrategy implements StrategyInterface
|
||||
{
|
||||
protected $commands;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->commands = $this->getDefaultCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default map of supported commands with their handlers.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaultCommands()
|
||||
{
|
||||
$getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument');
|
||||
$getKeyFromAllArguments = array($this, 'getKeyFromAllArguments');
|
||||
|
||||
return array(
|
||||
/* commands operating on the key space */
|
||||
'EXISTS' => $getKeyFromAllArguments,
|
||||
'DEL' => $getKeyFromAllArguments,
|
||||
'TYPE' => $getKeyFromFirstArgument,
|
||||
'EXPIRE' => $getKeyFromFirstArgument,
|
||||
'EXPIREAT' => $getKeyFromFirstArgument,
|
||||
'PERSIST' => $getKeyFromFirstArgument,
|
||||
'PEXPIRE' => $getKeyFromFirstArgument,
|
||||
'PEXPIREAT' => $getKeyFromFirstArgument,
|
||||
'TTL' => $getKeyFromFirstArgument,
|
||||
'PTTL' => $getKeyFromFirstArgument,
|
||||
'SORT' => array($this, 'getKeyFromSortCommand'),
|
||||
'DUMP' => $getKeyFromFirstArgument,
|
||||
'RESTORE' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on string values */
|
||||
'APPEND' => $getKeyFromFirstArgument,
|
||||
'DECR' => $getKeyFromFirstArgument,
|
||||
'DECRBY' => $getKeyFromFirstArgument,
|
||||
'GET' => $getKeyFromFirstArgument,
|
||||
'GETBIT' => $getKeyFromFirstArgument,
|
||||
'MGET' => $getKeyFromAllArguments,
|
||||
'SET' => $getKeyFromFirstArgument,
|
||||
'GETRANGE' => $getKeyFromFirstArgument,
|
||||
'GETSET' => $getKeyFromFirstArgument,
|
||||
'INCR' => $getKeyFromFirstArgument,
|
||||
'INCRBY' => $getKeyFromFirstArgument,
|
||||
'INCRBYFLOAT' => $getKeyFromFirstArgument,
|
||||
'SETBIT' => $getKeyFromFirstArgument,
|
||||
'SETEX' => $getKeyFromFirstArgument,
|
||||
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
|
||||
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
|
||||
'SETNX' => $getKeyFromFirstArgument,
|
||||
'SETRANGE' => $getKeyFromFirstArgument,
|
||||
'STRLEN' => $getKeyFromFirstArgument,
|
||||
'SUBSTR' => $getKeyFromFirstArgument,
|
||||
'BITOP' => array($this, 'getKeyFromBitOp'),
|
||||
'BITCOUNT' => $getKeyFromFirstArgument,
|
||||
'BITFIELD' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on lists */
|
||||
'LINSERT' => $getKeyFromFirstArgument,
|
||||
'LINDEX' => $getKeyFromFirstArgument,
|
||||
'LLEN' => $getKeyFromFirstArgument,
|
||||
'LPOP' => $getKeyFromFirstArgument,
|
||||
'RPOP' => $getKeyFromFirstArgument,
|
||||
'RPOPLPUSH' => $getKeyFromAllArguments,
|
||||
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
|
||||
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
|
||||
'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'),
|
||||
'LPUSH' => $getKeyFromFirstArgument,
|
||||
'LPUSHX' => $getKeyFromFirstArgument,
|
||||
'RPUSH' => $getKeyFromFirstArgument,
|
||||
'RPUSHX' => $getKeyFromFirstArgument,
|
||||
'LRANGE' => $getKeyFromFirstArgument,
|
||||
'LREM' => $getKeyFromFirstArgument,
|
||||
'LSET' => $getKeyFromFirstArgument,
|
||||
'LTRIM' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on sets */
|
||||
'SADD' => $getKeyFromFirstArgument,
|
||||
'SCARD' => $getKeyFromFirstArgument,
|
||||
'SDIFF' => $getKeyFromAllArguments,
|
||||
'SDIFFSTORE' => $getKeyFromAllArguments,
|
||||
'SINTER' => $getKeyFromAllArguments,
|
||||
'SINTERSTORE' => $getKeyFromAllArguments,
|
||||
'SUNION' => $getKeyFromAllArguments,
|
||||
'SUNIONSTORE' => $getKeyFromAllArguments,
|
||||
'SISMEMBER' => $getKeyFromFirstArgument,
|
||||
'SMEMBERS' => $getKeyFromFirstArgument,
|
||||
'SSCAN' => $getKeyFromFirstArgument,
|
||||
'SPOP' => $getKeyFromFirstArgument,
|
||||
'SRANDMEMBER' => $getKeyFromFirstArgument,
|
||||
'SREM' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on sorted sets */
|
||||
'ZADD' => $getKeyFromFirstArgument,
|
||||
'ZCARD' => $getKeyFromFirstArgument,
|
||||
'ZCOUNT' => $getKeyFromFirstArgument,
|
||||
'ZINCRBY' => $getKeyFromFirstArgument,
|
||||
'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
|
||||
'ZRANGE' => $getKeyFromFirstArgument,
|
||||
'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZRANK' => $getKeyFromFirstArgument,
|
||||
'ZREM' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANK' => $getKeyFromFirstArgument,
|
||||
'ZSCORE' => $getKeyFromFirstArgument,
|
||||
'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
|
||||
'ZSCAN' => $getKeyFromFirstArgument,
|
||||
'ZLEXCOUNT' => $getKeyFromFirstArgument,
|
||||
'ZRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on hashes */
|
||||
'HDEL' => $getKeyFromFirstArgument,
|
||||
'HEXISTS' => $getKeyFromFirstArgument,
|
||||
'HGET' => $getKeyFromFirstArgument,
|
||||
'HGETALL' => $getKeyFromFirstArgument,
|
||||
'HMGET' => $getKeyFromFirstArgument,
|
||||
'HMSET' => $getKeyFromFirstArgument,
|
||||
'HINCRBY' => $getKeyFromFirstArgument,
|
||||
'HINCRBYFLOAT' => $getKeyFromFirstArgument,
|
||||
'HKEYS' => $getKeyFromFirstArgument,
|
||||
'HLEN' => $getKeyFromFirstArgument,
|
||||
'HSET' => $getKeyFromFirstArgument,
|
||||
'HSETNX' => $getKeyFromFirstArgument,
|
||||
'HVALS' => $getKeyFromFirstArgument,
|
||||
'HSCAN' => $getKeyFromFirstArgument,
|
||||
'HSTRLEN' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on HyperLogLog */
|
||||
'PFADD' => $getKeyFromFirstArgument,
|
||||
'PFCOUNT' => $getKeyFromAllArguments,
|
||||
'PFMERGE' => $getKeyFromAllArguments,
|
||||
|
||||
/* scripting */
|
||||
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
|
||||
'EVALSHA' => array($this, 'getKeyFromScriptingCommands'),
|
||||
|
||||
/* commands performing geospatial operations */
|
||||
'GEOADD' => $getKeyFromFirstArgument,
|
||||
'GEOHASH' => $getKeyFromFirstArgument,
|
||||
'GEOPOS' => $getKeyFromFirstArgument,
|
||||
'GEODIST' => $getKeyFromFirstArgument,
|
||||
'GEORADIUS' => array($this, 'getKeyFromGeoradiusCommands'),
|
||||
'GEORADIUSBYMEMBER' => array($this, 'getKeyFromGeoradiusCommands'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of IDs for the supported commands.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedCommands()
|
||||
{
|
||||
return array_keys($this->commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an handler for the specified command ID.
|
||||
*
|
||||
* The signature of the callback must have a single parameter of type
|
||||
* Predis\Command\CommandInterface.
|
||||
*
|
||||
* When the callback argument is omitted or NULL, the previously associated
|
||||
* handler for the specified command ID is removed.
|
||||
*
|
||||
* @param string $commandID Command ID.
|
||||
* @param mixed $callback A valid callable object, or NULL to unset the handler.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setCommandHandler($commandID, $callback = null)
|
||||
{
|
||||
$commandID = strtoupper($commandID);
|
||||
|
||||
if (!isset($callback)) {
|
||||
unset($this->commands[$commandID]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_callable($callback)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'The argument must be a callable object or NULL.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->commands[$commandID] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from the first argument of a command instance.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getKeyFromFirstArgument(CommandInterface $command)
|
||||
{
|
||||
return $command->getArgument(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from a command with multiple keys only when all keys in
|
||||
* the arguments array produce the same hash.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromAllArguments(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
|
||||
if ($this->checkSameSlotForKeys($arguments)) {
|
||||
return $arguments[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from a command with multiple keys only when all keys in
|
||||
* the arguments array produce the same hash.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromInterleavedArguments(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$keys = array();
|
||||
|
||||
for ($i = 0; $i < count($arguments); $i += 2) {
|
||||
$keys[] = $arguments[$i];
|
||||
}
|
||||
|
||||
if ($this->checkSameSlotForKeys($keys)) {
|
||||
return $arguments[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from SORT command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromSortCommand(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$firstKey = $arguments[0];
|
||||
|
||||
if (1 === $argc = count($arguments)) {
|
||||
return $firstKey;
|
||||
}
|
||||
|
||||
$keys = array($firstKey);
|
||||
|
||||
for ($i = 1; $i < $argc; ++$i) {
|
||||
if (strtoupper($arguments[$i]) === 'STORE') {
|
||||
$keys[] = $arguments[++$i];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->checkSameSlotForKeys($keys)) {
|
||||
return $firstKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from BLPOP and BRPOP commands.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromBlockingListCommands(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
|
||||
if ($this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) {
|
||||
return $arguments[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from BITOP command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromBitOp(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
|
||||
if ($this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) {
|
||||
return $arguments[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from GEORADIUS and GEORADIUSBYMEMBER commands.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromGeoradiusCommands(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$argc = count($arguments);
|
||||
$startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
|
||||
|
||||
if ($argc > $startIndex) {
|
||||
$keys = array($arguments[0]);
|
||||
|
||||
for ($i = $startIndex; $i < $argc; ++$i) {
|
||||
$argument = strtoupper($arguments[$i]);
|
||||
if ($argument === 'STORE' || $argument === 'STOREDIST') {
|
||||
$keys[] = $arguments[++$i];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->checkSameSlotForKeys($keys)) {
|
||||
return $arguments[0];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return $arguments[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from ZINTERSTORE and ZUNIONSTORE commands.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromZsetAggregationCommands(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$keys = array_merge(array($arguments[0]), array_slice($arguments, 2, $arguments[1]));
|
||||
|
||||
if ($this->checkSameSlotForKeys($keys)) {
|
||||
return $arguments[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from EVAL and EVALSHA commands.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromScriptingCommands(CommandInterface $command)
|
||||
{
|
||||
if ($command instanceof ScriptCommand) {
|
||||
$keys = $command->getKeys();
|
||||
} else {
|
||||
$keys = array_slice($args = $command->getArguments(), 2, $args[1]);
|
||||
}
|
||||
|
||||
if ($keys && $this->checkSameSlotForKeys($keys)) {
|
||||
return $keys[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSlot(CommandInterface $command)
|
||||
{
|
||||
$slot = $command->getSlot();
|
||||
|
||||
if (!isset($slot) && isset($this->commands[$cmdID = $command->getId()])) {
|
||||
$key = call_user_func($this->commands[$cmdID], $command);
|
||||
|
||||
if (isset($key)) {
|
||||
$slot = $this->getSlotByKey($key);
|
||||
$command->setSlot($slot);
|
||||
}
|
||||
}
|
||||
|
||||
return $slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified array of keys will generate the same hash.
|
||||
*
|
||||
* @param array $keys Array of keys.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkSameSlotForKeys(array $keys)
|
||||
{
|
||||
if (!$count = count($keys)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentSlot = $this->getSlotByKey($keys[0]);
|
||||
|
||||
for ($i = 1; $i < $count; ++$i) {
|
||||
$nextSlot = $this->getSlotByKey($keys[$i]);
|
||||
|
||||
if ($currentSlot !== $nextSlot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentSlot = $nextSlot;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only the hashable part of a key (delimited by "{...}"), or the
|
||||
* whole key if a key tag is not found in the string.
|
||||
*
|
||||
* @param string $key A key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function extractKeyTag($key)
|
||||
{
|
||||
if (false !== $start = strpos($key, '{')) {
|
||||
if (false !== ($end = strpos($key, '}', $start)) && $end !== ++$start) {
|
||||
$key = substr($key, $start, $end - $start);
|
||||
}
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Distributor;
|
||||
|
||||
use Predis\Cluster\Hash\HashGeneratorInterface;
|
||||
|
||||
/**
|
||||
* A distributor implements the logic to automatically distribute keys among
|
||||
* several nodes for client-side sharding.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface DistributorInterface
|
||||
{
|
||||
/**
|
||||
* Adds a node to the distributor with an optional weight.
|
||||
*
|
||||
* @param mixed $node Node object.
|
||||
* @param int $weight Weight for the node.
|
||||
*/
|
||||
public function add($node, $weight = null);
|
||||
|
||||
/**
|
||||
* Removes a node from the distributor.
|
||||
*
|
||||
* @param mixed $node Node object.
|
||||
*/
|
||||
public function remove($node);
|
||||
|
||||
/**
|
||||
* Returns the corresponding slot of a node from the distributor using the
|
||||
* computed hash of a key.
|
||||
*
|
||||
* @param mixed $hash
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSlot($hash);
|
||||
|
||||
/**
|
||||
* Returns a node from the distributor using its assigned slot ID.
|
||||
*
|
||||
* @param mixed $slot
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getBySlot($slot);
|
||||
|
||||
/**
|
||||
* Returns a node from the distributor using the computed hash of a key.
|
||||
*
|
||||
* @param mixed $hash
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getByHash($hash);
|
||||
|
||||
/**
|
||||
* Returns a node from the distributor mapping to the specified value.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($value);
|
||||
|
||||
/**
|
||||
* Returns the underlying hash generator instance.
|
||||
*
|
||||
* @return HashGeneratorInterface
|
||||
*/
|
||||
public function getHashGenerator();
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Distributor;
|
||||
|
||||
/**
|
||||
* Exception class that identifies empty rings.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class EmptyRingException extends \Exception
|
||||
{
|
||||
}
|
@ -1,270 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Distributor;
|
||||
|
||||
use Predis\Cluster\Hash\HashGeneratorInterface;
|
||||
|
||||
/**
|
||||
* This class implements an hashring-based distributor that uses the same
|
||||
* algorithm of memcache to distribute keys in a cluster using client-side
|
||||
* sharding.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
* @author Lorenzo Castelli <lcastelli@gmail.com>
|
||||
*/
|
||||
class HashRing implements DistributorInterface, HashGeneratorInterface
|
||||
{
|
||||
const DEFAULT_REPLICAS = 128;
|
||||
const DEFAULT_WEIGHT = 100;
|
||||
|
||||
private $ring;
|
||||
private $ringKeys;
|
||||
private $ringKeysCount;
|
||||
private $replicas;
|
||||
private $nodeHashCallback;
|
||||
private $nodes = array();
|
||||
|
||||
/**
|
||||
* @param int $replicas Number of replicas in the ring.
|
||||
* @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
|
||||
*/
|
||||
public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null)
|
||||
{
|
||||
$this->replicas = $replicas;
|
||||
$this->nodeHashCallback = $nodeHashCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a node to the ring with an optional weight.
|
||||
*
|
||||
* @param mixed $node Node object.
|
||||
* @param int $weight Weight for the node.
|
||||
*/
|
||||
public function add($node, $weight = null)
|
||||
{
|
||||
// In case of collisions in the hashes of the nodes, the node added
|
||||
// last wins, thus the order in which nodes are added is significant.
|
||||
$this->nodes[] = array(
|
||||
'object' => $node,
|
||||
'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT,
|
||||
);
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function remove($node)
|
||||
{
|
||||
// A node is removed by resetting the ring so that it's recreated from
|
||||
// scratch, in order to reassign possible hashes with collisions to the
|
||||
// right node according to the order in which they were added in the
|
||||
// first place.
|
||||
for ($i = 0; $i < count($this->nodes); ++$i) {
|
||||
if ($this->nodes[$i]['object'] === $node) {
|
||||
array_splice($this->nodes, $i, 1);
|
||||
$this->reset();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the distributor.
|
||||
*/
|
||||
private function reset()
|
||||
{
|
||||
unset(
|
||||
$this->ring,
|
||||
$this->ringKeys,
|
||||
$this->ringKeysCount
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the initialization status of the distributor.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isInitialized()
|
||||
{
|
||||
return isset($this->ringKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the total weight of all the nodes in the distributor.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function computeTotalWeight()
|
||||
{
|
||||
$totalWeight = 0;
|
||||
|
||||
foreach ($this->nodes as $node) {
|
||||
$totalWeight += $node['weight'];
|
||||
}
|
||||
|
||||
return $totalWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the distributor.
|
||||
*/
|
||||
private function initialize()
|
||||
{
|
||||
if ($this->isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->nodes) {
|
||||
throw new EmptyRingException('Cannot initialize an empty hashring.');
|
||||
}
|
||||
|
||||
$this->ring = array();
|
||||
$totalWeight = $this->computeTotalWeight();
|
||||
$nodesCount = count($this->nodes);
|
||||
|
||||
foreach ($this->nodes as $node) {
|
||||
$weightRatio = $node['weight'] / $totalWeight;
|
||||
$this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio);
|
||||
}
|
||||
|
||||
ksort($this->ring, SORT_NUMERIC);
|
||||
$this->ringKeys = array_keys($this->ring);
|
||||
$this->ringKeysCount = count($this->ringKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the logic needed to add a node to the hashring.
|
||||
*
|
||||
* @param array $ring Source hashring.
|
||||
* @param mixed $node Node object to be added.
|
||||
* @param int $totalNodes Total number of nodes.
|
||||
* @param int $replicas Number of replicas in the ring.
|
||||
* @param float $weightRatio Weight ratio for the node.
|
||||
*/
|
||||
protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
|
||||
{
|
||||
$nodeObject = $node['object'];
|
||||
$nodeHash = $this->getNodeHash($nodeObject);
|
||||
$replicas = (int) round($weightRatio * $totalNodes * $replicas);
|
||||
|
||||
for ($i = 0; $i < $replicas; ++$i) {
|
||||
$key = crc32("$nodeHash:$i");
|
||||
$ring[$key] = $nodeObject;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNodeHash($nodeObject)
|
||||
{
|
||||
if (!isset($this->nodeHashCallback)) {
|
||||
return (string) $nodeObject;
|
||||
}
|
||||
|
||||
return call_user_func($this->nodeHashCallback, $nodeObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hash($value)
|
||||
{
|
||||
return crc32($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getByHash($hash)
|
||||
{
|
||||
return $this->ring[$this->getSlot($hash)];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBySlot($slot)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
if (isset($this->ring[$slot])) {
|
||||
return $this->ring[$slot];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSlot($hash)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
$ringKeys = $this->ringKeys;
|
||||
$upper = $this->ringKeysCount - 1;
|
||||
$lower = 0;
|
||||
|
||||
while ($lower <= $upper) {
|
||||
$index = ($lower + $upper) >> 1;
|
||||
$item = $ringKeys[$index];
|
||||
|
||||
if ($item > $hash) {
|
||||
$upper = $index - 1;
|
||||
} elseif ($item < $hash) {
|
||||
$lower = $index + 1;
|
||||
} else {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $ringKeys[$this->wrapAroundStrategy($upper, $lower, $this->ringKeysCount)];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($value)
|
||||
{
|
||||
$hash = $this->hash($value);
|
||||
$node = $this->getByHash($hash);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a strategy to deal with wrap-around errors during binary searches.
|
||||
*
|
||||
* @param int $upper
|
||||
* @param int $lower
|
||||
* @param int $ringKeysCount
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
|
||||
{
|
||||
// Binary search for the last item in ringkeys with a value less or
|
||||
// equal to the key. If no such item exists, return the last item.
|
||||
return $upper >= 0 ? $upper : $ringKeysCount - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHashGenerator()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Distributor;
|
||||
|
||||
/**
|
||||
* This class implements an hashring-based distributor that uses the same
|
||||
* algorithm of libketama to distribute keys in a cluster using client-side
|
||||
* sharding.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
* @author Lorenzo Castelli <lcastelli@gmail.com>
|
||||
*/
|
||||
class KetamaRing extends HashRing
|
||||
{
|
||||
const DEFAULT_REPLICAS = 160;
|
||||
|
||||
/**
|
||||
* @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
|
||||
*/
|
||||
public function __construct($nodeHashCallback = null)
|
||||
{
|
||||
parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
|
||||
{
|
||||
$nodeObject = $node['object'];
|
||||
$nodeHash = $this->getNodeHash($nodeObject);
|
||||
$replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4));
|
||||
|
||||
for ($i = 0; $i < $replicas; ++$i) {
|
||||
$unpackedDigest = unpack('V4', md5("$nodeHash-$i", true));
|
||||
|
||||
foreach ($unpackedDigest as $key) {
|
||||
$ring[$key] = $nodeObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hash($value)
|
||||
{
|
||||
$hash = unpack('V', md5($value, true));
|
||||
|
||||
return $hash[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
|
||||
{
|
||||
// Binary search for the first item in ringkeys with a value greater
|
||||
// or equal to the key. If no such item exists, return the first item.
|
||||
return $lower < $ringKeysCount ? $lower : 0;
|
||||
}
|
||||
}
|
72
vendor/predis/predis/src/Cluster/Hash/CRC16.php
vendored
72
vendor/predis/predis/src/Cluster/Hash/CRC16.php
vendored
@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Hash;
|
||||
|
||||
/**
|
||||
* Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class CRC16 implements HashGeneratorInterface
|
||||
{
|
||||
private static $CCITT_16 = array(
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
|
||||
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
|
||||
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
|
||||
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
|
||||
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
|
||||
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
|
||||
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
|
||||
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
|
||||
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
|
||||
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
|
||||
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
|
||||
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
|
||||
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
|
||||
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
|
||||
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
|
||||
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
|
||||
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||||
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
|
||||
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
|
||||
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
|
||||
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
|
||||
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
||||
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
|
||||
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
|
||||
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
|
||||
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
|
||||
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hash($value)
|
||||
{
|
||||
// CRC-CCITT-16 algorithm
|
||||
$crc = 0;
|
||||
$CCITT_16 = self::$CCITT_16;
|
||||
$strlen = strlen($value);
|
||||
|
||||
for ($i = 0; $i < $strlen; ++$i) {
|
||||
$crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF;
|
||||
}
|
||||
|
||||
return $crc;
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Hash;
|
||||
|
||||
/**
|
||||
* An hash generator implements the logic used to calculate the hash of a key to
|
||||
* distribute operations among Redis nodes.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface HashGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* Generates an hash from a string to be used for distribution.
|
||||
*
|
||||
* @param string $value String value.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function hash($value);
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use Predis\Cluster\Distributor\DistributorInterface;
|
||||
use Predis\Cluster\Distributor\HashRing;
|
||||
|
||||
/**
|
||||
* Default cluster strategy used by Predis to handle client-side sharding.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class PredisStrategy extends ClusterStrategy
|
||||
{
|
||||
protected $distributor;
|
||||
|
||||
/**
|
||||
* @param DistributorInterface $distributor Optional distributor instance.
|
||||
*/
|
||||
public function __construct(DistributorInterface $distributor = null)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->distributor = $distributor ?: new HashRing();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSlotByKey($key)
|
||||
{
|
||||
$key = $this->extractKeyTag($key);
|
||||
$hash = $this->distributor->hash($key);
|
||||
$slot = $this->distributor->getSlot($hash);
|
||||
|
||||
return $slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkSameSlotForKeys(array $keys)
|
||||
{
|
||||
if (!$count = count($keys)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentKey = $this->extractKeyTag($keys[0]);
|
||||
|
||||
for ($i = 1; $i < $count; ++$i) {
|
||||
$nextKey = $this->extractKeyTag($keys[$i]);
|
||||
|
||||
if ($currentKey !== $nextKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentKey = $nextKey;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDistributor()
|
||||
{
|
||||
return $this->distributor;
|
||||
}
|
||||
}
|
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