Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
74
core/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php
vendored
Normal file
74
core/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
/**
|
||||
* Client specific utility functions.
|
||||
*/
|
||||
class ClientUtils
|
||||
{
|
||||
/**
|
||||
* Returns the default cacert bundle for the current system.
|
||||
*
|
||||
* First, the openssl.cafile and curl.cainfo php.ini settings are checked.
|
||||
* If those settings are not configured, then the common locations for
|
||||
* bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X
|
||||
* and Windows are checked. If any of these file locations are found on
|
||||
* disk, they will be utilized.
|
||||
*
|
||||
* Note: the result of this function is cached for subsequent calls.
|
||||
*
|
||||
* @return string
|
||||
* @throws \RuntimeException if no bundle can be found.
|
||||
*/
|
||||
public static function getDefaultCaBundle()
|
||||
{
|
||||
static $cached = null;
|
||||
static $cafiles = [
|
||||
// Red Hat, CentOS, Fedora (provided by the ca-certificates package)
|
||||
'/etc/pki/tls/certs/ca-bundle.crt',
|
||||
// Ubuntu, Debian (provided by the ca-certificates package)
|
||||
'/etc/ssl/certs/ca-certificates.crt',
|
||||
// FreeBSD (provided by the ca_root_nss package)
|
||||
'/usr/local/share/certs/ca-root-nss.crt',
|
||||
// OS X provided by homebrew (using the default path)
|
||||
'/usr/local/etc/openssl/cert.pem',
|
||||
// Windows?
|
||||
'C:\\windows\\system32\\curl-ca-bundle.crt',
|
||||
'C:\\windows\\curl-ca-bundle.crt',
|
||||
];
|
||||
|
||||
if ($cached) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
if ($ca = ini_get('openssl.cafile')) {
|
||||
return $cached = $ca;
|
||||
}
|
||||
|
||||
if ($ca = ini_get('curl.cainfo')) {
|
||||
return $cached = $ca;
|
||||
}
|
||||
|
||||
foreach ($cafiles as $filename) {
|
||||
if (file_exists($filename)) {
|
||||
return $cached = $filename;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException(self::CA_ERR);
|
||||
}
|
||||
|
||||
const CA_ERR = "
|
||||
No system CA bundle could be found in any of the the common system locations.
|
||||
PHP versions earlier than 5.6 are not properly configured to use the system's
|
||||
CA bundle by default. In order to verify peer certificates, you will need to
|
||||
supply the path on disk to a certificate bundle to the 'verify' request
|
||||
option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not
|
||||
need a specific certificate bundle, then Mozilla provides a commonly used CA
|
||||
bundle which can be downloaded here (provided by the maintainer of cURL):
|
||||
https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once
|
||||
you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP
|
||||
ini setting to point to the path to the file, allowing you to omit the 'verify'
|
||||
request option. See http://curl.haxx.se/docs/sslcerts.html for more
|
||||
information.";
|
||||
}
|
546
core/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php
vendored
Normal file
546
core/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php
vendored
Normal file
|
@ -0,0 +1,546 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Core;
|
||||
use GuzzleHttp\Ring\Exception\ConnectException;
|
||||
use GuzzleHttp\Ring\Exception\RingException;
|
||||
use GuzzleHttp\Stream\LazyOpenStream;
|
||||
use GuzzleHttp\Stream\StreamInterface;
|
||||
|
||||
/**
|
||||
* Creates curl resources from a request
|
||||
*/
|
||||
class CurlFactory
|
||||
{
|
||||
/**
|
||||
* Creates a cURL handle, header resource, and body resource based on a
|
||||
* transaction.
|
||||
*
|
||||
* @param array $request Request hash
|
||||
* @param null|resource $handle Optionally provide a curl handle to modify
|
||||
*
|
||||
* @return array Returns an array of the curl handle, headers array, and
|
||||
* response body handle.
|
||||
* @throws \RuntimeException when an option cannot be applied
|
||||
*/
|
||||
public function __invoke(array $request, $handle = null)
|
||||
{
|
||||
$headers = [];
|
||||
$options = $this->getDefaultOptions($request, $headers);
|
||||
$this->applyMethod($request, $options);
|
||||
|
||||
if (isset($request['client'])) {
|
||||
$this->applyHandlerOptions($request, $options);
|
||||
}
|
||||
|
||||
$this->applyHeaders($request, $options);
|
||||
unset($options['_headers']);
|
||||
|
||||
// Add handler options from the request's configuration options
|
||||
if (isset($request['client']['curl'])) {
|
||||
$options = $this->applyCustomCurlOptions(
|
||||
$request['client']['curl'],
|
||||
$options
|
||||
);
|
||||
}
|
||||
|
||||
if (!$handle) {
|
||||
$handle = curl_init();
|
||||
}
|
||||
|
||||
$body = $this->getOutputBody($request, $options);
|
||||
curl_setopt_array($handle, $options);
|
||||
|
||||
return [$handle, &$headers, $body];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a response hash from a cURL result.
|
||||
*
|
||||
* @param callable $handler Handler that was used.
|
||||
* @param array $request Request that sent.
|
||||
* @param array $response Response hash to update.
|
||||
* @param array $headers Headers received during transfer.
|
||||
* @param resource $body Body fopen response.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function createResponse(
|
||||
callable $handler,
|
||||
array $request,
|
||||
array $response,
|
||||
array $headers,
|
||||
$body
|
||||
) {
|
||||
if (isset($response['transfer_stats']['url'])) {
|
||||
$response['effective_url'] = $response['transfer_stats']['url'];
|
||||
}
|
||||
|
||||
if (!empty($headers)) {
|
||||
$startLine = explode(' ', array_shift($headers), 3);
|
||||
$headerList = Core::headersFromLines($headers);
|
||||
$response['headers'] = $headerList;
|
||||
$response['status'] = isset($startLine[1]) ? (int) $startLine[1] : null;
|
||||
$response['reason'] = isset($startLine[2]) ? $startLine[2] : null;
|
||||
$response['body'] = $body;
|
||||
Core::rewindBody($response);
|
||||
}
|
||||
|
||||
return !empty($response['curl']['errno']) || !isset($response['status'])
|
||||
? self::createErrorResponse($handler, $request, $response)
|
||||
: $response;
|
||||
}
|
||||
|
||||
private static function createErrorResponse(
|
||||
callable $handler,
|
||||
array $request,
|
||||
array $response
|
||||
) {
|
||||
static $connectionErrors = [
|
||||
CURLE_OPERATION_TIMEOUTED => true,
|
||||
CURLE_COULDNT_RESOLVE_HOST => true,
|
||||
CURLE_COULDNT_CONNECT => true,
|
||||
CURLE_SSL_CONNECT_ERROR => true,
|
||||
CURLE_GOT_NOTHING => true,
|
||||
];
|
||||
|
||||
// Retry when nothing is present or when curl failed to rewind.
|
||||
if (!isset($response['err_message'])
|
||||
&& (empty($response['curl']['errno'])
|
||||
|| $response['curl']['errno'] == 65)
|
||||
) {
|
||||
return self::retryFailedRewind($handler, $request, $response);
|
||||
}
|
||||
|
||||
$message = isset($response['err_message'])
|
||||
? $response['err_message']
|
||||
: sprintf('cURL error %s: %s',
|
||||
$response['curl']['errno'],
|
||||
isset($response['curl']['error'])
|
||||
? $response['curl']['error']
|
||||
: 'See http://curl.haxx.se/libcurl/c/libcurl-errors.html');
|
||||
|
||||
$error = isset($response['curl']['errno'])
|
||||
&& isset($connectionErrors[$response['curl']['errno']])
|
||||
? new ConnectException($message)
|
||||
: new RingException($message);
|
||||
|
||||
return $response + [
|
||||
'status' => null,
|
||||
'reason' => null,
|
||||
'body' => null,
|
||||
'headers' => [],
|
||||
'error' => $error,
|
||||
];
|
||||
}
|
||||
|
||||
private function getOutputBody(array $request, array &$options)
|
||||
{
|
||||
// Determine where the body of the response (if any) will be streamed.
|
||||
if (isset($options[CURLOPT_WRITEFUNCTION])) {
|
||||
return $request['client']['save_to'];
|
||||
}
|
||||
|
||||
if (isset($options[CURLOPT_FILE])) {
|
||||
return $options[CURLOPT_FILE];
|
||||
}
|
||||
|
||||
if ($request['http_method'] != 'HEAD') {
|
||||
// Create a default body if one was not provided
|
||||
return $options[CURLOPT_FILE] = fopen('php://temp', 'w+');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getDefaultOptions(array $request, array &$headers)
|
||||
{
|
||||
$url = Core::url($request);
|
||||
$startingResponse = false;
|
||||
|
||||
$options = [
|
||||
'_headers' => $request['headers'],
|
||||
CURLOPT_CUSTOMREQUEST => $request['http_method'],
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => false,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_CONNECTTIMEOUT => 150,
|
||||
CURLOPT_HEADERFUNCTION => function ($ch, $h) use (&$headers, &$startingResponse) {
|
||||
$value = trim($h);
|
||||
if ($value === '') {
|
||||
$startingResponse = true;
|
||||
} elseif ($startingResponse) {
|
||||
$startingResponse = false;
|
||||
$headers = [$value];
|
||||
} else {
|
||||
$headers[] = $value;
|
||||
}
|
||||
return strlen($h);
|
||||
},
|
||||
];
|
||||
|
||||
if (isset($request['version'])) {
|
||||
$options[CURLOPT_HTTP_VERSION] = $request['version'] == 1.1 ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0;
|
||||
}
|
||||
|
||||
if (defined('CURLOPT_PROTOCOLS')) {
|
||||
$options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
private function applyMethod(array $request, array &$options)
|
||||
{
|
||||
if (isset($request['body'])) {
|
||||
$this->applyBody($request, $options);
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($request['http_method']) {
|
||||
case 'PUT':
|
||||
case 'POST':
|
||||
// See http://tools.ietf.org/html/rfc7230#section-3.3.2
|
||||
if (!Core::hasHeader($request, 'Content-Length')) {
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
|
||||
}
|
||||
break;
|
||||
case 'HEAD':
|
||||
$options[CURLOPT_NOBODY] = true;
|
||||
unset(
|
||||
$options[CURLOPT_WRITEFUNCTION],
|
||||
$options[CURLOPT_READFUNCTION],
|
||||
$options[CURLOPT_FILE],
|
||||
$options[CURLOPT_INFILE]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function applyBody(array $request, array &$options)
|
||||
{
|
||||
$contentLength = Core::firstHeader($request, 'Content-Length');
|
||||
$size = $contentLength !== null ? (int) $contentLength : null;
|
||||
|
||||
// Send the body as a string if the size is less than 1MB OR if the
|
||||
// [client][curl][body_as_string] request value is set.
|
||||
if (($size !== null && $size < 1000000) ||
|
||||
isset($request['client']['curl']['body_as_string']) ||
|
||||
is_string($request['body'])
|
||||
) {
|
||||
$options[CURLOPT_POSTFIELDS] = Core::body($request);
|
||||
// Don't duplicate the Content-Length header
|
||||
$this->removeHeader('Content-Length', $options);
|
||||
$this->removeHeader('Transfer-Encoding', $options);
|
||||
} else {
|
||||
$options[CURLOPT_UPLOAD] = true;
|
||||
if ($size !== null) {
|
||||
// Let cURL handle setting the Content-Length header
|
||||
$options[CURLOPT_INFILESIZE] = $size;
|
||||
$this->removeHeader('Content-Length', $options);
|
||||
}
|
||||
$this->addStreamingBody($request, $options);
|
||||
}
|
||||
|
||||
// If the Expect header is not present, prevent curl from adding it
|
||||
if (!Core::hasHeader($request, 'Expect')) {
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Expect:';
|
||||
}
|
||||
|
||||
// cURL sometimes adds a content-type by default. Prevent this.
|
||||
if (!Core::hasHeader($request, 'Content-Type')) {
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Content-Type:';
|
||||
}
|
||||
}
|
||||
|
||||
private function addStreamingBody(array $request, array &$options)
|
||||
{
|
||||
$body = $request['body'];
|
||||
|
||||
if ($body instanceof StreamInterface) {
|
||||
$options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
|
||||
return (string) $body->read($length);
|
||||
};
|
||||
if (!isset($options[CURLOPT_INFILESIZE])) {
|
||||
if ($size = $body->getSize()) {
|
||||
$options[CURLOPT_INFILESIZE] = $size;
|
||||
}
|
||||
}
|
||||
} elseif (is_resource($body)) {
|
||||
$options[CURLOPT_INFILE] = $body;
|
||||
} elseif ($body instanceof \Iterator) {
|
||||
$buf = '';
|
||||
$options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body, &$buf) {
|
||||
if ($body->valid()) {
|
||||
$buf .= $body->current();
|
||||
$body->next();
|
||||
}
|
||||
$result = (string) substr($buf, 0, $length);
|
||||
$buf = substr($buf, $length);
|
||||
return $result;
|
||||
};
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Invalid request body provided');
|
||||
}
|
||||
}
|
||||
|
||||
private function applyHeaders(array $request, array &$options)
|
||||
{
|
||||
foreach ($options['_headers'] as $name => $values) {
|
||||
foreach ($values as $value) {
|
||||
$options[CURLOPT_HTTPHEADER][] = "$name: $value";
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the Accept header if one was not set
|
||||
if (!Core::hasHeader($request, 'Accept')) {
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Accept:';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of curl options specified in the 'curl' option of a
|
||||
* request's configuration array and maps them to CURLOPT_* options.
|
||||
*
|
||||
* This method is only called when a request has a 'curl' config setting.
|
||||
*
|
||||
* @param array $config Configuration array of custom curl option
|
||||
* @param array $options Array of existing curl options
|
||||
*
|
||||
* @return array Returns a new array of curl options
|
||||
*/
|
||||
private function applyCustomCurlOptions(array $config, array $options)
|
||||
{
|
||||
$curlOptions = [];
|
||||
foreach ($config as $key => $value) {
|
||||
if (is_int($key)) {
|
||||
$curlOptions[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $curlOptions + $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a header from the options array.
|
||||
*
|
||||
* @param string $name Case-insensitive header to remove
|
||||
* @param array $options Array of options to modify
|
||||
*/
|
||||
private function removeHeader($name, array &$options)
|
||||
{
|
||||
foreach (array_keys($options['_headers']) as $key) {
|
||||
if (!strcasecmp($key, $name)) {
|
||||
unset($options['_headers'][$key]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies an array of request client options to a the options array.
|
||||
*
|
||||
* This method uses a large switch rather than double-dispatch to save on
|
||||
* high overhead of calling functions in PHP.
|
||||
*/
|
||||
private function applyHandlerOptions(array $request, array &$options)
|
||||
{
|
||||
foreach ($request['client'] as $key => $value) {
|
||||
switch ($key) {
|
||||
// Violating PSR-4 to provide more room.
|
||||
case 'verify':
|
||||
|
||||
if ($value === false) {
|
||||
unset($options[CURLOPT_CAINFO]);
|
||||
$options[CURLOPT_SSL_VERIFYHOST] = 0;
|
||||
$options[CURLOPT_SSL_VERIFYPEER] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
$options[CURLOPT_SSL_VERIFYHOST] = 2;
|
||||
$options[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
|
||||
if (is_string($value)) {
|
||||
$options[CURLOPT_CAINFO] = $value;
|
||||
if (!file_exists($value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"SSL CA bundle not found: $value"
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'decode_content':
|
||||
|
||||
if ($value === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$accept = Core::firstHeader($request, 'Accept-Encoding');
|
||||
if ($accept) {
|
||||
$options[CURLOPT_ENCODING] = $accept;
|
||||
} else {
|
||||
$options[CURLOPT_ENCODING] = '';
|
||||
// Don't let curl send the header over the wire
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'save_to':
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = new LazyOpenStream($value, 'w+');
|
||||
}
|
||||
|
||||
if ($value instanceof StreamInterface) {
|
||||
$options[CURLOPT_WRITEFUNCTION] =
|
||||
function ($ch, $write) use ($value) {
|
||||
return $value->write($write);
|
||||
};
|
||||
} elseif (is_resource($value)) {
|
||||
$options[CURLOPT_FILE] = $value;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('save_to must be a '
|
||||
. 'GuzzleHttp\Stream\StreamInterface or resource');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'timeout':
|
||||
|
||||
if (defined('CURLOPT_TIMEOUT_MS')) {
|
||||
$options[CURLOPT_TIMEOUT_MS] = $value * 1000;
|
||||
} else {
|
||||
$options[CURLOPT_TIMEOUT] = $value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'connect_timeout':
|
||||
|
||||
if (defined('CURLOPT_CONNECTTIMEOUT_MS')) {
|
||||
$options[CURLOPT_CONNECTTIMEOUT_MS] = $value * 1000;
|
||||
} else {
|
||||
$options[CURLOPT_CONNECTTIMEOUT] = $value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'proxy':
|
||||
|
||||
if (!is_array($value)) {
|
||||
$options[CURLOPT_PROXY] = $value;
|
||||
} elseif (isset($request['scheme'])) {
|
||||
$scheme = $request['scheme'];
|
||||
if (isset($value[$scheme])) {
|
||||
$options[CURLOPT_PROXY] = $value[$scheme];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'cert':
|
||||
|
||||
if (is_array($value)) {
|
||||
$options[CURLOPT_SSLCERTPASSWD] = $value[1];
|
||||
$value = $value[0];
|
||||
}
|
||||
|
||||
if (!file_exists($value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"SSL certificate not found: {$value}"
|
||||
);
|
||||
}
|
||||
|
||||
$options[CURLOPT_SSLCERT] = $value;
|
||||
break;
|
||||
|
||||
case 'ssl_key':
|
||||
|
||||
if (is_array($value)) {
|
||||
$options[CURLOPT_SSLKEYPASSWD] = $value[1];
|
||||
$value = $value[0];
|
||||
}
|
||||
|
||||
if (!file_exists($value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"SSL private key not found: {$value}"
|
||||
);
|
||||
}
|
||||
|
||||
$options[CURLOPT_SSLKEY] = $value;
|
||||
break;
|
||||
|
||||
case 'progress':
|
||||
|
||||
if (!is_callable($value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'progress client option must be callable'
|
||||
);
|
||||
}
|
||||
|
||||
$options[CURLOPT_NOPROGRESS] = false;
|
||||
$options[CURLOPT_PROGRESSFUNCTION] =
|
||||
function () use ($value) {
|
||||
$args = func_get_args();
|
||||
// PHP 5.5 pushed the handle onto the start of the args
|
||||
if (is_resource($args[0])) {
|
||||
array_shift($args);
|
||||
}
|
||||
call_user_func_array($value, $args);
|
||||
};
|
||||
break;
|
||||
|
||||
case 'debug':
|
||||
|
||||
if ($value) {
|
||||
$options[CURLOPT_STDERR] = Core::getDebugResource($value);
|
||||
$options[CURLOPT_VERBOSE] = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function ensures that a response was set on a transaction. If one
|
||||
* was not set, then the request is retried if possible. This error
|
||||
* typically means you are sending a payload, curl encountered a
|
||||
* "Connection died, retrying a fresh connect" error, tried to rewind the
|
||||
* stream, and then encountered a "necessary data rewind wasn't possible"
|
||||
* error, causing the request to be sent through curl_multi_info_read()
|
||||
* without an error status.
|
||||
*/
|
||||
private static function retryFailedRewind(
|
||||
callable $handler,
|
||||
array $request,
|
||||
array $response
|
||||
) {
|
||||
// If there is no body, then there is some other kind of issue. This
|
||||
// is weird and should probably never happen.
|
||||
if (!isset($request['body'])) {
|
||||
$response['err_message'] = 'No response was received for a request '
|
||||
. 'with no body. This could mean that you are saturating your '
|
||||
. 'network.';
|
||||
return self::createErrorResponse($handler, $request, $response);
|
||||
}
|
||||
|
||||
if (!Core::rewindBody($request)) {
|
||||
$response['err_message'] = 'The connection unexpectedly failed '
|
||||
. 'without providing an error. The request would have been '
|
||||
. 'retried, but attempting to rewind the request body failed.';
|
||||
return self::createErrorResponse($handler, $request, $response);
|
||||
}
|
||||
|
||||
// Retry no more than 3 times before giving up.
|
||||
if (!isset($request['curl']['retries'])) {
|
||||
$request['curl']['retries'] = 1;
|
||||
} elseif ($request['curl']['retries'] == 2) {
|
||||
$response['err_message'] = 'The cURL request was retried 3 times '
|
||||
. 'and did no succeed. cURL was unable to rewind the body of '
|
||||
. 'the request and subsequent retries resulted in the same '
|
||||
. 'error. Turn on the debug option to see what went wrong. '
|
||||
. 'See https://bugs.php.net/bug.php?id=47204 for more information.';
|
||||
return self::createErrorResponse($handler, $request, $response);
|
||||
} else {
|
||||
$request['curl']['retries']++;
|
||||
}
|
||||
|
||||
return $handler($request);
|
||||
}
|
||||
}
|
118
core/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php
vendored
Normal file
118
core/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Future\CompletedFutureArray;
|
||||
use GuzzleHttp\Ring\Core;
|
||||
|
||||
/**
|
||||
* HTTP handler that uses cURL easy handles as a transport layer.
|
||||
*
|
||||
* Requires PHP 5.5+
|
||||
*
|
||||
* When using the CurlHandler, custom curl options can be specified as an
|
||||
* associative array of curl option constants mapping to values in the
|
||||
* **curl** key of the "client" key of the request.
|
||||
*/
|
||||
class CurlHandler
|
||||
{
|
||||
/** @var callable */
|
||||
private $factory;
|
||||
|
||||
/** @var array Array of curl easy handles */
|
||||
private $handles = [];
|
||||
|
||||
/** @var array Array of owned curl easy handles */
|
||||
private $ownedHandles = [];
|
||||
|
||||
/** @var int Total number of idle handles to keep in cache */
|
||||
private $maxHandles;
|
||||
|
||||
/**
|
||||
* Accepts an associative array of options:
|
||||
*
|
||||
* - factory: Optional callable factory used to create cURL handles.
|
||||
* The callable is passed a request hash when invoked, and returns an
|
||||
* array of the curl handle, headers resource, and body resource.
|
||||
* - max_handles: Maximum number of idle handles (defaults to 5).
|
||||
*
|
||||
* @param array $options Array of options to use with the handler
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->handles = $this->ownedHandles = [];
|
||||
$this->factory = isset($options['handle_factory'])
|
||||
? $options['handle_factory']
|
||||
: new CurlFactory();
|
||||
$this->maxHandles = isset($options['max_handles'])
|
||||
? $options['max_handles']
|
||||
: 5;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
foreach ($this->handles as $handle) {
|
||||
if (is_resource($handle)) {
|
||||
curl_close($handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __invoke(array $request)
|
||||
{
|
||||
$factory = $this->factory;
|
||||
|
||||
// Ensure headers are by reference. They're updated elsewhere.
|
||||
$result = $factory($request, $this->checkoutEasyHandle());
|
||||
$h = $result[0];
|
||||
$hd =& $result[1];
|
||||
$bd = $result[2];
|
||||
Core::doSleep($request);
|
||||
curl_exec($h);
|
||||
$response = ['transfer_stats' => curl_getinfo($h)];
|
||||
$response['curl']['error'] = curl_error($h);
|
||||
$response['curl']['errno'] = curl_errno($h);
|
||||
$response['transfer_stats'] = array_merge($response['transfer_stats'], $response['curl']);
|
||||
$this->releaseEasyHandle($h);
|
||||
|
||||
return new CompletedFutureArray(
|
||||
CurlFactory::createResponse($this, $request, $response, $hd, $bd)
|
||||
);
|
||||
}
|
||||
|
||||
private function checkoutEasyHandle()
|
||||
{
|
||||
// Find an unused handle in the cache
|
||||
if (false !== ($key = array_search(false, $this->ownedHandles, true))) {
|
||||
$this->ownedHandles[$key] = true;
|
||||
return $this->handles[$key];
|
||||
}
|
||||
|
||||
// Add a new handle
|
||||
$handle = curl_init();
|
||||
$id = (int) $handle;
|
||||
$this->handles[$id] = $handle;
|
||||
$this->ownedHandles[$id] = true;
|
||||
|
||||
return $handle;
|
||||
}
|
||||
|
||||
private function releaseEasyHandle($handle)
|
||||
{
|
||||
$id = (int) $handle;
|
||||
if (count($this->ownedHandles) > $this->maxHandles) {
|
||||
curl_close($this->handles[$id]);
|
||||
unset($this->handles[$id], $this->ownedHandles[$id]);
|
||||
} else {
|
||||
// curl_reset doesn't clear these out for some reason
|
||||
static $unsetValues = [
|
||||
CURLOPT_HEADERFUNCTION => null,
|
||||
CURLOPT_WRITEFUNCTION => null,
|
||||
CURLOPT_READFUNCTION => null,
|
||||
CURLOPT_PROGRESSFUNCTION => null,
|
||||
];
|
||||
curl_setopt_array($handle, $unsetValues);
|
||||
curl_reset($handle);
|
||||
$this->ownedHandles[$id] = false;
|
||||
}
|
||||
}
|
||||
}
|
250
core/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php
vendored
Normal file
250
core/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php
vendored
Normal file
|
@ -0,0 +1,250 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Future\FutureArray;
|
||||
use React\Promise\Deferred;
|
||||
|
||||
/**
|
||||
* Returns an asynchronous response using curl_multi_* functions.
|
||||
*
|
||||
* This handler supports future responses and the "delay" request client
|
||||
* option that can be used to delay before sending a request.
|
||||
*
|
||||
* When using the CurlMultiHandler, custom curl options can be specified as an
|
||||
* associative array of curl option constants mapping to values in the
|
||||
* **curl** key of the "client" key of the request.
|
||||
*
|
||||
* @property resource $_mh Internal use only. Lazy loaded multi-handle.
|
||||
*/
|
||||
class CurlMultiHandler
|
||||
{
|
||||
/** @var callable */
|
||||
private $factory;
|
||||
private $selectTimeout;
|
||||
private $active;
|
||||
private $handles = [];
|
||||
private $delays = [];
|
||||
private $maxHandles;
|
||||
|
||||
/**
|
||||
* This handler accepts the following options:
|
||||
*
|
||||
* - mh: An optional curl_multi resource
|
||||
* - handle_factory: An optional callable used to generate curl handle
|
||||
* resources. the callable accepts a request hash and returns an array
|
||||
* of the handle, headers file resource, and the body resource.
|
||||
* - select_timeout: Optional timeout (in seconds) to block before timing
|
||||
* out while selecting curl handles. Defaults to 1 second.
|
||||
* - max_handles: Optional integer representing the maximum number of
|
||||
* open requests. When this number is reached, the queued futures are
|
||||
* flushed.
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
if (isset($options['mh'])) {
|
||||
$this->_mh = $options['mh'];
|
||||
}
|
||||
$this->factory = isset($options['handle_factory'])
|
||||
? $options['handle_factory'] : new CurlFactory();
|
||||
$this->selectTimeout = isset($options['select_timeout'])
|
||||
? $options['select_timeout'] : 1;
|
||||
$this->maxHandles = isset($options['max_handles'])
|
||||
? $options['max_handles'] : 100;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if ($name === '_mh') {
|
||||
return $this->_mh = curl_multi_init();
|
||||
}
|
||||
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// Finish any open connections before terminating the script.
|
||||
if ($this->handles) {
|
||||
$this->execute();
|
||||
}
|
||||
|
||||
if (isset($this->_mh)) {
|
||||
curl_multi_close($this->_mh);
|
||||
unset($this->_mh);
|
||||
}
|
||||
}
|
||||
|
||||
public function __invoke(array $request)
|
||||
{
|
||||
$factory = $this->factory;
|
||||
$result = $factory($request);
|
||||
$entry = [
|
||||
'request' => $request,
|
||||
'response' => [],
|
||||
'handle' => $result[0],
|
||||
'headers' => &$result[1],
|
||||
'body' => $result[2],
|
||||
'deferred' => new Deferred(),
|
||||
];
|
||||
|
||||
$id = (int) $result[0];
|
||||
|
||||
$future = new FutureArray(
|
||||
$entry['deferred']->promise(),
|
||||
[$this, 'execute'],
|
||||
function () use ($id) {
|
||||
return $this->cancel($id);
|
||||
}
|
||||
);
|
||||
|
||||
$this->addRequest($entry);
|
||||
|
||||
// Transfer outstanding requests if there are too many open handles.
|
||||
if (count($this->handles) >= $this->maxHandles) {
|
||||
$this->execute();
|
||||
}
|
||||
|
||||
return $future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs until all outstanding connections have completed.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
do {
|
||||
|
||||
if ($this->active &&
|
||||
curl_multi_select($this->_mh, $this->selectTimeout) === -1
|
||||
) {
|
||||
// Perform a usleep if a select returns -1.
|
||||
// See: https://bugs.php.net/bug.php?id=61141
|
||||
usleep(250);
|
||||
}
|
||||
|
||||
// Add any delayed futures if needed.
|
||||
if ($this->delays) {
|
||||
$this->addDelays();
|
||||
}
|
||||
|
||||
do {
|
||||
$mrc = curl_multi_exec($this->_mh, $this->active);
|
||||
} while ($mrc === CURLM_CALL_MULTI_PERFORM);
|
||||
|
||||
$this->processMessages();
|
||||
|
||||
// If there are delays but no transfers, then sleep for a bit.
|
||||
if (!$this->active && $this->delays) {
|
||||
usleep(500);
|
||||
}
|
||||
|
||||
} while ($this->active || $this->handles);
|
||||
}
|
||||
|
||||
private function addRequest(array &$entry)
|
||||
{
|
||||
$id = (int) $entry['handle'];
|
||||
$this->handles[$id] = $entry;
|
||||
|
||||
// If the request is a delay, then add the reques to the curl multi
|
||||
// pool only after the specified delay.
|
||||
if (isset($entry['request']['client']['delay'])) {
|
||||
$this->delays[$id] = microtime(true) + ($entry['request']['client']['delay'] / 1000);
|
||||
} elseif (empty($entry['request']['future'])) {
|
||||
curl_multi_add_handle($this->_mh, $entry['handle']);
|
||||
} else {
|
||||
curl_multi_add_handle($this->_mh, $entry['handle']);
|
||||
// "lazy" futures are only sent once the pool has many requests.
|
||||
if ($entry['request']['future'] !== 'lazy') {
|
||||
do {
|
||||
$mrc = curl_multi_exec($this->_mh, $this->active);
|
||||
} while ($mrc === CURLM_CALL_MULTI_PERFORM);
|
||||
$this->processMessages();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function removeProcessed($id)
|
||||
{
|
||||
if (isset($this->handles[$id])) {
|
||||
curl_multi_remove_handle(
|
||||
$this->_mh,
|
||||
$this->handles[$id]['handle']
|
||||
);
|
||||
curl_close($this->handles[$id]['handle']);
|
||||
unset($this->handles[$id], $this->delays[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a handle from sending and removes references to it.
|
||||
*
|
||||
* @param int $id Handle ID to cancel and remove.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
private function cancel($id)
|
||||
{
|
||||
// Cannot cancel if it has been processed.
|
||||
if (!isset($this->handles[$id])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$handle = $this->handles[$id]['handle'];
|
||||
unset($this->delays[$id], $this->handles[$id]);
|
||||
curl_multi_remove_handle($this->_mh, $handle);
|
||||
curl_close($handle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function addDelays()
|
||||
{
|
||||
$currentTime = microtime(true);
|
||||
|
||||
foreach ($this->delays as $id => $delay) {
|
||||
if ($currentTime >= $delay) {
|
||||
unset($this->delays[$id]);
|
||||
curl_multi_add_handle(
|
||||
$this->_mh,
|
||||
$this->handles[$id]['handle']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function processMessages()
|
||||
{
|
||||
while ($done = curl_multi_info_read($this->_mh)) {
|
||||
$id = (int) $done['handle'];
|
||||
|
||||
if (!isset($this->handles[$id])) {
|
||||
// Probably was cancelled.
|
||||
continue;
|
||||
}
|
||||
|
||||
$entry = $this->handles[$id];
|
||||
$entry['response']['transfer_stats'] = curl_getinfo($done['handle']);
|
||||
|
||||
if ($done['result'] !== CURLM_OK) {
|
||||
$entry['response']['curl']['errno'] = $done['result'];
|
||||
if (function_exists('curl_strerror')) {
|
||||
$entry['response']['curl']['error'] = curl_strerror($done['result']);
|
||||
}
|
||||
}
|
||||
|
||||
$result = CurlFactory::createResponse(
|
||||
$this,
|
||||
$entry['request'],
|
||||
$entry['response'],
|
||||
$entry['headers'],
|
||||
$entry['body']
|
||||
);
|
||||
|
||||
$this->removeProcessed($id);
|
||||
$entry['deferred']->resolve($result);
|
||||
}
|
||||
}
|
||||
}
|
58
core/vendor/guzzlehttp/ringphp/src/Client/Middleware.php
vendored
Normal file
58
core/vendor/guzzlehttp/ringphp/src/Client/Middleware.php
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
/**
|
||||
* Provides basic middleware wrappers.
|
||||
*
|
||||
* If a middleware is more complex than a few lines of code, then it should
|
||||
* be implemented in a class rather than a static method.
|
||||
*/
|
||||
class Middleware
|
||||
{
|
||||
/**
|
||||
* Sends future requests to a future compatible handler while sending all
|
||||
* other requests to a default handler.
|
||||
*
|
||||
* When the "future" option is not provided on a request, any future responses
|
||||
* are automatically converted to synchronous responses and block.
|
||||
*
|
||||
* @param callable $default Handler used for non-streaming responses
|
||||
* @param callable $future Handler used for future responses
|
||||
*
|
||||
* @return callable Returns the composed handler.
|
||||
*/
|
||||
public static function wrapFuture(
|
||||
callable $default,
|
||||
callable $future
|
||||
) {
|
||||
return function (array $request) use ($default, $future) {
|
||||
return empty($request['client']['future'])
|
||||
? $default($request)
|
||||
: $future($request);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends streaming requests to a streaming compatible handler while sendin
|
||||
* all other requests to a default handler.
|
||||
*
|
||||
* This, for example, could be useful for taking advantage of the
|
||||
* performance benefits of curl while still supporting true streaming
|
||||
* through the StreamHandler.
|
||||
*
|
||||
* @param callable $default Handler used for non-streaming responses
|
||||
* @param callable $streaming Handler used for streaming responses
|
||||
*
|
||||
* @return callable Returns the composed handler.
|
||||
*/
|
||||
public static function wrapStreaming(
|
||||
callable $default,
|
||||
callable $streaming
|
||||
) {
|
||||
return function (array $request) use ($default, $streaming) {
|
||||
return empty($request['client']['stream'])
|
||||
? $default($request)
|
||||
: $streaming($request);
|
||||
};
|
||||
}
|
||||
}
|
52
core/vendor/guzzlehttp/ringphp/src/Client/MockHandler.php
vendored
Normal file
52
core/vendor/guzzlehttp/ringphp/src/Client/MockHandler.php
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Core;
|
||||
use GuzzleHttp\Ring\Future\CompletedFutureArray;
|
||||
use GuzzleHttp\Ring\Future\FutureArrayInterface;
|
||||
|
||||
/**
|
||||
* Ring handler that returns a canned response or evaluated function result.
|
||||
*/
|
||||
class MockHandler
|
||||
{
|
||||
/** @var callable|array|FutureArrayInterface */
|
||||
private $result;
|
||||
|
||||
/**
|
||||
* Provide an array or future to always return the same value. Provide a
|
||||
* callable that accepts a request object and returns an array or future
|
||||
* to dynamically create a response.
|
||||
*
|
||||
* @param array|FutureArrayInterface|callable $result Mock return value.
|
||||
*/
|
||||
public function __construct($result)
|
||||
{
|
||||
$this->result = $result;
|
||||
}
|
||||
|
||||
public function __invoke(array $request)
|
||||
{
|
||||
Core::doSleep($request);
|
||||
$response = is_callable($this->result)
|
||||
? call_user_func($this->result, $request)
|
||||
: $this->result;
|
||||
|
||||
if (is_array($response)) {
|
||||
$response = new CompletedFutureArray($response + [
|
||||
'status' => null,
|
||||
'body' => null,
|
||||
'headers' => [],
|
||||
'reason' => null,
|
||||
'effective_url' => null,
|
||||
]);
|
||||
} elseif (!$response instanceof FutureArrayInterface) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Response must be an array or FutureArrayInterface. Found '
|
||||
. Core::describeType($request)
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
412
core/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php
vendored
Normal file
412
core/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php
vendored
Normal file
|
@ -0,0 +1,412 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Core;
|
||||
use GuzzleHttp\Ring\Exception\ConnectException;
|
||||
use GuzzleHttp\Ring\Exception\RingException;
|
||||
use GuzzleHttp\Ring\Future\CompletedFutureArray;
|
||||
use GuzzleHttp\Stream\InflateStream;
|
||||
use GuzzleHttp\Stream\StreamInterface;
|
||||
use GuzzleHttp\Stream\Stream;
|
||||
use GuzzleHttp\Stream\Utils;
|
||||
|
||||
/**
|
||||
* RingPHP client handler that uses PHP's HTTP stream wrapper.
|
||||
*/
|
||||
class StreamHandler
|
||||
{
|
||||
private $options;
|
||||
private $lastHeaders;
|
||||
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
public function __invoke(array $request)
|
||||
{
|
||||
$url = Core::url($request);
|
||||
Core::doSleep($request);
|
||||
|
||||
try {
|
||||
// Does not support the expect header.
|
||||
$request = Core::removeHeader($request, 'Expect');
|
||||
$stream = $this->createStream($url, $request);
|
||||
return $this->createResponse($request, $url, $stream);
|
||||
} catch (RingException $e) {
|
||||
return $this->createErrorResponse($url, $e);
|
||||
}
|
||||
}
|
||||
|
||||
private function createResponse(array $request, $url, $stream)
|
||||
{
|
||||
$hdrs = $this->lastHeaders;
|
||||
$this->lastHeaders = null;
|
||||
$parts = explode(' ', array_shift($hdrs), 3);
|
||||
$response = [
|
||||
'status' => $parts[1],
|
||||
'reason' => isset($parts[2]) ? $parts[2] : null,
|
||||
'headers' => Core::headersFromLines($hdrs),
|
||||
'effective_url' => $url,
|
||||
];
|
||||
|
||||
$stream = $this->checkDecode($request, $response, $stream);
|
||||
|
||||
// If not streaming, then drain the response into a stream.
|
||||
if (empty($request['client']['stream'])) {
|
||||
$dest = isset($request['client']['save_to'])
|
||||
? $request['client']['save_to']
|
||||
: fopen('php://temp', 'r+');
|
||||
$stream = $this->drain($stream, $dest);
|
||||
}
|
||||
|
||||
$response['body'] = $stream;
|
||||
|
||||
return new CompletedFutureArray($response);
|
||||
}
|
||||
|
||||
private function checkDecode(array $request, array $response, $stream)
|
||||
{
|
||||
// Automatically decode responses when instructed.
|
||||
if (!empty($request['client']['decode_content'])) {
|
||||
switch (Core::firstHeader($response, 'Content-Encoding', true)) {
|
||||
case 'gzip':
|
||||
case 'deflate':
|
||||
$stream = new InflateStream(Stream::factory($stream));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drains the stream into the "save_to" client option.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param string|resource|StreamInterface $dest
|
||||
*
|
||||
* @return Stream
|
||||
* @throws \RuntimeException when the save_to option is invalid.
|
||||
*/
|
||||
private function drain($stream, $dest)
|
||||
{
|
||||
if (is_resource($stream)) {
|
||||
if (!is_resource($dest)) {
|
||||
$stream = Stream::factory($stream);
|
||||
} else {
|
||||
stream_copy_to_stream($stream, $dest);
|
||||
fclose($stream);
|
||||
rewind($dest);
|
||||
return $dest;
|
||||
}
|
||||
}
|
||||
|
||||
// Stream the response into the destination stream
|
||||
$dest = is_string($dest)
|
||||
? new Stream(Utils::open($dest, 'r+'))
|
||||
: Stream::factory($dest);
|
||||
|
||||
Utils::copyToStream($stream, $dest);
|
||||
$dest->seek(0);
|
||||
$stream->close();
|
||||
|
||||
return $dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an error response for the given stream.
|
||||
*
|
||||
* @param string $url
|
||||
* @param RingException $e
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function createErrorResponse($url, RingException $e)
|
||||
{
|
||||
// Determine if the error was a networking error.
|
||||
$message = $e->getMessage();
|
||||
|
||||
// This list can probably get more comprehensive.
|
||||
if (strpos($message, 'getaddrinfo') // DNS lookup failed
|
||||
|| strpos($message, 'Connection refused')
|
||||
) {
|
||||
$e = new ConnectException($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
return new CompletedFutureArray([
|
||||
'status' => null,
|
||||
'body' => null,
|
||||
'headers' => [],
|
||||
'effective_url' => $url,
|
||||
'error' => $e
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a resource and check to ensure it was created successfully
|
||||
*
|
||||
* @param callable $callback Callable that returns stream resource
|
||||
*
|
||||
* @return resource
|
||||
* @throws \RuntimeException on error
|
||||
*/
|
||||
private function createResource(callable $callback)
|
||||
{
|
||||
$errors = null;
|
||||
set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
|
||||
$errors[] = [
|
||||
'message' => $msg,
|
||||
'file' => $file,
|
||||
'line' => $line
|
||||
];
|
||||
return true;
|
||||
});
|
||||
|
||||
$resource = $callback();
|
||||
restore_error_handler();
|
||||
|
||||
if (!$resource) {
|
||||
$message = 'Error creating resource: ';
|
||||
foreach ($errors as $err) {
|
||||
foreach ($err as $key => $value) {
|
||||
$message .= "[$key] $value" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
throw new RingException(trim($message));
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
private function createStream($url, array $request)
|
||||
{
|
||||
static $methods;
|
||||
if (!$methods) {
|
||||
$methods = array_flip(get_class_methods(__CLASS__));
|
||||
}
|
||||
|
||||
// HTTP/1.1 streams using the PHP stream wrapper require a
|
||||
// Connection: close header
|
||||
if ((!isset($request['version']) || $request['version'] == '1.1')
|
||||
&& !Core::hasHeader($request, 'Connection')
|
||||
) {
|
||||
$request['headers']['Connection'] = ['close'];
|
||||
}
|
||||
|
||||
// Ensure SSL is verified by default
|
||||
if (!isset($request['client']['verify'])) {
|
||||
$request['client']['verify'] = true;
|
||||
}
|
||||
|
||||
$params = [];
|
||||
$options = $this->getDefaultOptions($request);
|
||||
|
||||
if (isset($request['client'])) {
|
||||
foreach ($request['client'] as $key => $value) {
|
||||
$method = "add_{$key}";
|
||||
if (isset($methods[$method])) {
|
||||
$this->{$method}($request, $options, $value, $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createStreamResource(
|
||||
$url,
|
||||
$request,
|
||||
$options,
|
||||
$this->createContext($request, $options, $params)
|
||||
);
|
||||
}
|
||||
|
||||
private function getDefaultOptions(array $request)
|
||||
{
|
||||
$headers = "";
|
||||
foreach ($request['headers'] as $name => $value) {
|
||||
foreach ((array) $value as $val) {
|
||||
$headers .= "$name: $val\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
$context = [
|
||||
'http' => [
|
||||
'method' => $request['http_method'],
|
||||
'header' => $headers,
|
||||
'protocol_version' => isset($request['version']) ? $request['version'] : 1.1,
|
||||
'ignore_errors' => true,
|
||||
'follow_location' => 0,
|
||||
],
|
||||
];
|
||||
|
||||
$body = Core::body($request);
|
||||
if (isset($body)) {
|
||||
$context['http']['content'] = $body;
|
||||
// Prevent the HTTP handler from adding a Content-Type header.
|
||||
if (!Core::hasHeader($request, 'Content-Type')) {
|
||||
$context['http']['header'] .= "Content-Type:\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
$context['http']['header'] = rtrim($context['http']['header']);
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
private function add_proxy(array $request, &$options, $value, &$params)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
$options['http']['proxy'] = $value;
|
||||
} else {
|
||||
$scheme = isset($request['scheme']) ? $request['scheme'] : 'http';
|
||||
if (isset($value[$scheme])) {
|
||||
$options['http']['proxy'] = $value[$scheme];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function add_timeout(array $request, &$options, $value, &$params)
|
||||
{
|
||||
$options['http']['timeout'] = $value;
|
||||
}
|
||||
|
||||
private function add_verify(array $request, &$options, $value, &$params)
|
||||
{
|
||||
if ($value === true) {
|
||||
// PHP 5.6 or greater will find the system cert by default. When
|
||||
// < 5.6, use the Guzzle bundled cacert.
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
$options['ssl']['cafile'] = ClientUtils::getDefaultCaBundle();
|
||||
}
|
||||
} elseif (is_string($value)) {
|
||||
$options['ssl']['cafile'] = $value;
|
||||
if (!file_exists($value)) {
|
||||
throw new RingException("SSL CA bundle not found: $value");
|
||||
}
|
||||
} elseif ($value === false) {
|
||||
$options['ssl']['verify_peer'] = false;
|
||||
return;
|
||||
} else {
|
||||
throw new RingException('Invalid verify request option');
|
||||
}
|
||||
|
||||
$options['ssl']['verify_peer'] = true;
|
||||
$options['ssl']['allow_self_signed'] = true;
|
||||
}
|
||||
|
||||
private function add_cert(array $request, &$options, $value, &$params)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$options['ssl']['passphrase'] = $value[1];
|
||||
$value = $value[0];
|
||||
}
|
||||
|
||||
if (!file_exists($value)) {
|
||||
throw new RingException("SSL certificate not found: {$value}");
|
||||
}
|
||||
|
||||
$options['ssl']['local_cert'] = $value;
|
||||
}
|
||||
|
||||
private function add_progress(array $request, &$options, $value, &$params)
|
||||
{
|
||||
$fn = function ($code, $_1, $_2, $_3, $transferred, $total) use ($value) {
|
||||
if ($code == STREAM_NOTIFY_PROGRESS) {
|
||||
$value($total, $transferred, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
// Wrap the existing function if needed.
|
||||
$params['notification'] = isset($params['notification'])
|
||||
? Core::callArray([$params['notification'], $fn])
|
||||
: $fn;
|
||||
}
|
||||
|
||||
private function add_debug(array $request, &$options, $value, &$params)
|
||||
{
|
||||
if ($value === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
static $map = [
|
||||
STREAM_NOTIFY_CONNECT => 'CONNECT',
|
||||
STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
|
||||
STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
|
||||
STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
|
||||
STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
|
||||
STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
|
||||
STREAM_NOTIFY_PROGRESS => 'PROGRESS',
|
||||
STREAM_NOTIFY_FAILURE => 'FAILURE',
|
||||
STREAM_NOTIFY_COMPLETED => 'COMPLETED',
|
||||
STREAM_NOTIFY_RESOLVE => 'RESOLVE',
|
||||
];
|
||||
|
||||
static $args = ['severity', 'message', 'message_code',
|
||||
'bytes_transferred', 'bytes_max'];
|
||||
|
||||
$value = Core::getDebugResource($value);
|
||||
$ident = $request['http_method'] . ' ' . Core::url($request);
|
||||
$fn = function () use ($ident, $value, $map, $args) {
|
||||
$passed = func_get_args();
|
||||
$code = array_shift($passed);
|
||||
fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
|
||||
foreach (array_filter($passed) as $i => $v) {
|
||||
fwrite($value, $args[$i] . ': "' . $v . '" ');
|
||||
}
|
||||
fwrite($value, "\n");
|
||||
};
|
||||
|
||||
// Wrap the existing function if needed.
|
||||
$params['notification'] = isset($params['notification'])
|
||||
? Core::callArray([$params['notification'], $fn])
|
||||
: $fn;
|
||||
}
|
||||
|
||||
private function applyCustomOptions(array $request, array &$options)
|
||||
{
|
||||
if (!isset($request['client']['stream_context'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_array($request['client']['stream_context'])) {
|
||||
throw new RingException('stream_context must be an array');
|
||||
}
|
||||
|
||||
$options = array_replace_recursive(
|
||||
$options,
|
||||
$request['client']['stream_context']
|
||||
);
|
||||
}
|
||||
|
||||
private function createContext(array $request, array $options, array $params)
|
||||
{
|
||||
$this->applyCustomOptions($request, $options);
|
||||
return $this->createResource(
|
||||
function () use ($request, $options, $params) {
|
||||
return stream_context_create($options, $params);
|
||||
},
|
||||
$request,
|
||||
$options
|
||||
);
|
||||
}
|
||||
|
||||
private function createStreamResource(
|
||||
$url,
|
||||
array $request,
|
||||
array $options,
|
||||
$context
|
||||
) {
|
||||
return $this->createResource(
|
||||
function () use ($url, $context) {
|
||||
if (false === strpos($url, 'http')) {
|
||||
trigger_error("URL is invalid: {$url}", E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
$resource = fopen($url, 'r', null, $context);
|
||||
$this->lastHeaders = $http_response_header;
|
||||
return $resource;
|
||||
},
|
||||
$request,
|
||||
$options
|
||||
);
|
||||
}
|
||||
}
|
364
core/vendor/guzzlehttp/ringphp/src/Core.php
vendored
Normal file
364
core/vendor/guzzlehttp/ringphp/src/Core.php
vendored
Normal file
|
@ -0,0 +1,364 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring;
|
||||
|
||||
use GuzzleHttp\Stream\StreamInterface;
|
||||
use GuzzleHttp\Ring\Future\FutureArrayInterface;
|
||||
use GuzzleHttp\Ring\Future\FutureArray;
|
||||
|
||||
/**
|
||||
* Provides core functionality of Ring handlers and middleware.
|
||||
*/
|
||||
class Core
|
||||
{
|
||||
/**
|
||||
* Returns a function that calls all of the provided functions, in order,
|
||||
* passing the arguments provided to the composed function to each function.
|
||||
*
|
||||
* @param callable[] $functions Array of functions to proxy to.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public static function callArray(array $functions)
|
||||
{
|
||||
return function () use ($functions) {
|
||||
$args = func_get_args();
|
||||
foreach ($functions as $fn) {
|
||||
call_user_func_array($fn, $args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of header line values from a message for a specific header
|
||||
*
|
||||
* This method searches through the "headers" key of a message for a header
|
||||
* using a case-insensitive search.
|
||||
*
|
||||
* @param array $message Request or response hash.
|
||||
* @param string $header Header to retrieve
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function headerLines($message, $header)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
if (!empty($message['headers'])) {
|
||||
foreach ($message['headers'] as $name => $value) {
|
||||
if (!strcasecmp($name, $header)) {
|
||||
$result = array_merge($result, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a header value from a message as a string or null
|
||||
*
|
||||
* This method searches through the "headers" key of a message for a header
|
||||
* using a case-insensitive search. The lines of the header are imploded
|
||||
* using commas into a single string return value.
|
||||
*
|
||||
* @param array $message Request or response hash.
|
||||
* @param string $header Header to retrieve
|
||||
*
|
||||
* @return string|null Returns the header string if found, or null if not.
|
||||
*/
|
||||
public static function header($message, $header)
|
||||
{
|
||||
$match = self::headerLines($message, $header);
|
||||
return $match ? implode(', ', $match) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first header value from a message as a string or null. If
|
||||
* a header line contains multiple values separated by a comma, then this
|
||||
* function will return the first value in the list.
|
||||
*
|
||||
* @param array $message Request or response hash.
|
||||
* @param string $header Header to retrieve
|
||||
*
|
||||
* @return string|null Returns the value as a string if found.
|
||||
*/
|
||||
public static function firstHeader($message, $header)
|
||||
{
|
||||
if (!empty($message['headers'])) {
|
||||
foreach ($message['headers'] as $name => $value) {
|
||||
if (!strcasecmp($name, $header)) {
|
||||
// Return the match itself if it is a single value.
|
||||
$pos = strpos($value[0], ',');
|
||||
return $pos ? substr($value[0], 0, $pos) : $value[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a message has the provided case-insensitive header.
|
||||
*
|
||||
* @param array $message Request or response hash.
|
||||
* @param string $header Header to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasHeader($message, $header)
|
||||
{
|
||||
if (!empty($message['headers'])) {
|
||||
foreach ($message['headers'] as $name => $value) {
|
||||
if (!strcasecmp($name, $header)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an array of header lines into an associative array of headers.
|
||||
*
|
||||
* @param array $lines Header lines array of strings in the following
|
||||
* format: "Name: Value"
|
||||
* @return array
|
||||
*/
|
||||
public static function headersFromLines($lines)
|
||||
{
|
||||
$headers = [];
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$parts = explode(':', $line, 2);
|
||||
$headers[trim($parts[0])][] = isset($parts[1])
|
||||
? trim($parts[1])
|
||||
: null;
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a header from a message using a case-insensitive comparison.
|
||||
*
|
||||
* @param array $message Message that contains 'headers'
|
||||
* @param string $header Header to remove
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function removeHeader(array $message, $header)
|
||||
{
|
||||
if (isset($message['headers'])) {
|
||||
foreach (array_keys($message['headers']) as $key) {
|
||||
if (!strcasecmp($header, $key)) {
|
||||
unset($message['headers'][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces any existing case insensitive headers with the given value.
|
||||
*
|
||||
* @param array $message Message that contains 'headers'
|
||||
* @param string $header Header to set.
|
||||
* @param array $value Value to set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function setHeader(array $message, $header, array $value)
|
||||
{
|
||||
$message = self::removeHeader($message, $header);
|
||||
$message['headers'][$header] = $value;
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a URL string from a request.
|
||||
*
|
||||
* If the "url" key is present on the request, it is returned, otherwise
|
||||
* the url is built up based on the scheme, host, uri, and query_string
|
||||
* request values.
|
||||
*
|
||||
* @param array $request Request to get the URL from
|
||||
*
|
||||
* @return string Returns the request URL as a string.
|
||||
* @throws \InvalidArgumentException if no Host header is present.
|
||||
*/
|
||||
public static function url(array $request)
|
||||
{
|
||||
if (isset($request['url'])) {
|
||||
return $request['url'];
|
||||
}
|
||||
|
||||
$uri = (isset($request['scheme'])
|
||||
? $request['scheme'] : 'http') . '://';
|
||||
|
||||
if ($host = self::header($request, 'host')) {
|
||||
$uri .= $host;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('No Host header was provided');
|
||||
}
|
||||
|
||||
if (isset($request['uri'])) {
|
||||
$uri .= $request['uri'];
|
||||
}
|
||||
|
||||
if (isset($request['query_string'])) {
|
||||
$uri .= '?' . $request['query_string'];
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the body of a message into a string.
|
||||
*
|
||||
* @param array|FutureArrayInterface $message Array containing a "body" key
|
||||
*
|
||||
* @return null|string Returns the body as a string or null if not set.
|
||||
* @throws \InvalidArgumentException if a request body is invalid.
|
||||
*/
|
||||
public static function body($message)
|
||||
{
|
||||
if (!isset($message['body'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($message['body'] instanceof StreamInterface) {
|
||||
return (string) $message['body'];
|
||||
}
|
||||
|
||||
switch (gettype($message['body'])) {
|
||||
case 'string':
|
||||
return $message['body'];
|
||||
case 'resource':
|
||||
return stream_get_contents($message['body']);
|
||||
case 'object':
|
||||
if ($message['body'] instanceof \Iterator) {
|
||||
return implode('', iterator_to_array($message['body']));
|
||||
} elseif (method_exists($message['body'], '__toString')) {
|
||||
return (string) $message['body'];
|
||||
}
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid request body: '
|
||||
. self::describeType($message['body']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the body of the provided message if possible.
|
||||
*
|
||||
* @param array $message Message that contains a 'body' field.
|
||||
*
|
||||
* @return bool Returns true on success, false on failure
|
||||
*/
|
||||
public static function rewindBody($message)
|
||||
{
|
||||
if ($message['body'] instanceof StreamInterface) {
|
||||
return $message['body']->seek(0);
|
||||
}
|
||||
|
||||
if ($message['body'] instanceof \Generator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($message['body'] instanceof \Iterator) {
|
||||
$message['body']->rewind();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_resource($message['body'])) {
|
||||
return rewind($message['body']);
|
||||
}
|
||||
|
||||
return is_string($message['body'])
|
||||
|| (is_object($message['body'])
|
||||
&& method_exists($message['body'], '__toString'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug function used to describe the provided value type and class.
|
||||
*
|
||||
* @param mixed $input
|
||||
*
|
||||
* @return string Returns a string containing the type of the variable and
|
||||
* if a class is provided, the class name.
|
||||
*/
|
||||
public static function describeType($input)
|
||||
{
|
||||
switch (gettype($input)) {
|
||||
case 'object':
|
||||
return 'object(' . get_class($input) . ')';
|
||||
case 'array':
|
||||
return 'array(' . count($input) . ')';
|
||||
default:
|
||||
ob_start();
|
||||
var_dump($input);
|
||||
// normalize float vs double
|
||||
return str_replace('double(', 'float(', rtrim(ob_get_clean()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep for the specified amount of time specified in the request's
|
||||
* ['client']['delay'] option if present.
|
||||
*
|
||||
* This function should only be used when a non-blocking sleep is not
|
||||
* possible.
|
||||
*
|
||||
* @param array $request Request to sleep
|
||||
*/
|
||||
public static function doSleep(array $request)
|
||||
{
|
||||
if (isset($request['client']['delay'])) {
|
||||
usleep($request['client']['delay'] * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a proxied future that modifies the dereferenced value of another
|
||||
* future using a promise.
|
||||
*
|
||||
* @param FutureArrayInterface $future Future to wrap with a new future
|
||||
* @param callable $onFulfilled Invoked when the future fulfilled
|
||||
* @param callable $onRejected Invoked when the future rejected
|
||||
* @param callable $onProgress Invoked when the future progresses
|
||||
*
|
||||
* @return FutureArray
|
||||
*/
|
||||
public static function proxy(
|
||||
FutureArrayInterface $future,
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null,
|
||||
callable $onProgress = null
|
||||
) {
|
||||
return new FutureArray(
|
||||
$future->then($onFulfilled, $onRejected, $onProgress),
|
||||
[$future, 'wait'],
|
||||
[$future, 'cancel']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a debug stream based on the provided variable.
|
||||
*
|
||||
* @param mixed $value Optional value
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public static function getDebugResource($value = null)
|
||||
{
|
||||
if (is_resource($value)) {
|
||||
return $value;
|
||||
} elseif (defined('STDOUT')) {
|
||||
return STDOUT;
|
||||
} else {
|
||||
return fopen('php://output', 'w');
|
||||
}
|
||||
}
|
||||
}
|
7
core/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php
vendored
Normal file
7
core/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Exception;
|
||||
|
||||
/**
|
||||
* Marker interface for cancelled exceptions.
|
||||
*/
|
||||
interface CancelledException {}
|
4
core/vendor/guzzlehttp/ringphp/src/Exception/CancelledFutureAccessException.php
vendored
Normal file
4
core/vendor/guzzlehttp/ringphp/src/Exception/CancelledFutureAccessException.php
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Exception;
|
||||
|
||||
class CancelledFutureAccessException extends RingException implements CancelledException {}
|
7
core/vendor/guzzlehttp/ringphp/src/Exception/ConnectException.php
vendored
Normal file
7
core/vendor/guzzlehttp/ringphp/src/Exception/ConnectException.php
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Exception;
|
||||
|
||||
/**
|
||||
* Occurs when the connection failed.
|
||||
*/
|
||||
class ConnectException extends RingException {}
|
4
core/vendor/guzzlehttp/ringphp/src/Exception/RingException.php
vendored
Normal file
4
core/vendor/guzzlehttp/ringphp/src/Exception/RingException.php
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Exception;
|
||||
|
||||
class RingException extends \RuntimeException {};
|
125
core/vendor/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php
vendored
Normal file
125
core/vendor/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php
vendored
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
use GuzzleHttp\Ring\Exception\CancelledFutureAccessException;
|
||||
use GuzzleHttp\Ring\Exception\RingException;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* Implements common future functionality built on top of promises.
|
||||
*/
|
||||
trait BaseFutureTrait
|
||||
{
|
||||
/** @var callable */
|
||||
private $waitfn;
|
||||
|
||||
/** @var callable */
|
||||
private $cancelfn;
|
||||
|
||||
/** @var PromiseInterface */
|
||||
private $wrappedPromise;
|
||||
|
||||
/** @var \Exception Error encountered. */
|
||||
private $error;
|
||||
|
||||
/** @var mixed Result of the future */
|
||||
private $result;
|
||||
|
||||
private $isRealized = false;
|
||||
|
||||
/**
|
||||
* @param PromiseInterface $promise Promise to shadow with the future.
|
||||
* @param callable $wait Function that blocks until the deferred
|
||||
* computation has been resolved. This
|
||||
* function MUST resolve the deferred value
|
||||
* associated with the supplied promise.
|
||||
* @param callable $cancel If possible and reasonable, provide a
|
||||
* function that can be used to cancel the
|
||||
* future from completing.
|
||||
*/
|
||||
public function __construct(
|
||||
PromiseInterface $promise,
|
||||
callable $wait = null,
|
||||
callable $cancel = null
|
||||
) {
|
||||
$this->wrappedPromise = $promise;
|
||||
$this->waitfn = $wait;
|
||||
$this->cancelfn = $cancel;
|
||||
}
|
||||
|
||||
public function wait()
|
||||
{
|
||||
if (!$this->isRealized) {
|
||||
$this->addShadow();
|
||||
if (!$this->isRealized && $this->waitfn) {
|
||||
$this->invokeWait();
|
||||
}
|
||||
if (!$this->isRealized) {
|
||||
$this->error = new RingException('Waiting did not resolve future');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->error) {
|
||||
throw $this->error;
|
||||
}
|
||||
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function promise()
|
||||
{
|
||||
return $this->wrappedPromise;
|
||||
}
|
||||
|
||||
public function then(
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null,
|
||||
callable $onProgress = null
|
||||
) {
|
||||
return $this->wrappedPromise->then($onFulfilled, $onRejected, $onProgress);
|
||||
}
|
||||
|
||||
public function cancel()
|
||||
{
|
||||
if (!$this->isRealized) {
|
||||
$cancelfn = $this->cancelfn;
|
||||
$this->waitfn = $this->cancelfn = null;
|
||||
$this->isRealized = true;
|
||||
$this->error = new CancelledFutureAccessException();
|
||||
if ($cancelfn) {
|
||||
$cancelfn($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function addShadow()
|
||||
{
|
||||
// Get the result and error when the promise is resolved. Note that
|
||||
// calling this function might trigger the resolution immediately.
|
||||
$this->wrappedPromise->then(
|
||||
function ($value) {
|
||||
$this->isRealized = true;
|
||||
$this->result = $value;
|
||||
$this->waitfn = $this->cancelfn = null;
|
||||
},
|
||||
function ($error) {
|
||||
$this->isRealized = true;
|
||||
$this->error = $error;
|
||||
$this->waitfn = $this->cancelfn = null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function invokeWait()
|
||||
{
|
||||
try {
|
||||
$wait = $this->waitfn;
|
||||
$this->waitfn = null;
|
||||
$wait();
|
||||
} catch (\Exception $e) {
|
||||
// Defer can throw to reject.
|
||||
$this->error = $e;
|
||||
$this->isRealized = true;
|
||||
}
|
||||
}
|
||||
}
|
43
core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php
vendored
Normal file
43
core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
/**
|
||||
* Represents a future array that has been completed successfully.
|
||||
*/
|
||||
class CompletedFutureArray extends CompletedFutureValue implements FutureArrayInterface
|
||||
{
|
||||
public function __construct(array $result)
|
||||
{
|
||||
parent::__construct($result);
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->result[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->result[$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->result[$offset] = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->result[$offset]);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->result);
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->result);
|
||||
}
|
||||
}
|
57
core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php
vendored
Normal file
57
core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
use React\Promise\FulfilledPromise;
|
||||
use React\Promise\RejectedPromise;
|
||||
|
||||
/**
|
||||
* Represents a future value that has been resolved or rejected.
|
||||
*/
|
||||
class CompletedFutureValue implements FutureInterface
|
||||
{
|
||||
protected $result;
|
||||
protected $error;
|
||||
|
||||
private $cachedPromise;
|
||||
|
||||
/**
|
||||
* @param mixed $result Resolved result
|
||||
* @param \Exception $e Error. Pass a GuzzleHttp\Ring\Exception\CancelledFutureAccessException
|
||||
* to mark the future as cancelled.
|
||||
*/
|
||||
public function __construct($result, \Exception $e = null)
|
||||
{
|
||||
$this->result = $result;
|
||||
$this->error = $e;
|
||||
}
|
||||
|
||||
public function wait()
|
||||
{
|
||||
if ($this->error) {
|
||||
throw $this->error;
|
||||
}
|
||||
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function cancel() {}
|
||||
|
||||
public function promise()
|
||||
{
|
||||
if (!$this->cachedPromise) {
|
||||
$this->cachedPromise = $this->error
|
||||
? new RejectedPromise($this->error)
|
||||
: new FulfilledPromise($this->result);
|
||||
}
|
||||
|
||||
return $this->cachedPromise;
|
||||
}
|
||||
|
||||
public function then(
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null,
|
||||
callable $onProgress = null
|
||||
) {
|
||||
return $this->promise()->then($onFulfilled, $onRejected, $onProgress);
|
||||
}
|
||||
}
|
40
core/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php
vendored
Normal file
40
core/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
/**
|
||||
* Represents a future array value that when dereferenced returns an array.
|
||||
*/
|
||||
class FutureArray implements FutureArrayInterface
|
||||
{
|
||||
use MagicFutureTrait;
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->_value[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->_value[$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->_value[$offset] = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->_value[$offset]);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->_value);
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->_value);
|
||||
}
|
||||
}
|
11
core/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php
vendored
Normal file
11
core/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
/**
|
||||
* Future that provides array-like access.
|
||||
*/
|
||||
interface FutureArrayInterface extends
|
||||
FutureInterface,
|
||||
\ArrayAccess,
|
||||
\Countable,
|
||||
\IteratorAggregate {};
|
40
core/vendor/guzzlehttp/ringphp/src/Future/FutureInterface.php
vendored
Normal file
40
core/vendor/guzzlehttp/ringphp/src/Future/FutureInterface.php
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
use React\Promise\PromiseInterface;
|
||||
use React\Promise\PromisorInterface;
|
||||
|
||||
/**
|
||||
* Represents the result of a computation that may not have completed yet.
|
||||
*
|
||||
* You can use the future in a blocking manner using the wait() function, or
|
||||
* you can use a promise from the future to receive the result when the future
|
||||
* has been resolved.
|
||||
*
|
||||
* When the future is dereferenced using wait(), the result of the computation
|
||||
* is cached and returned for subsequent calls to wait(). If the result of the
|
||||
* computation has not yet completed when wait() is called, the call to wait()
|
||||
* will block until the future has completed.
|
||||
*/
|
||||
interface FutureInterface extends PromiseInterface, PromisorInterface
|
||||
{
|
||||
/**
|
||||
* Returns the result of the future either from cache or by blocking until
|
||||
* it is complete.
|
||||
*
|
||||
* This method must block until the future has a result or is cancelled.
|
||||
* Throwing an exception in the wait() method will mark the future as
|
||||
* realized and will throw the exception each time wait() is called.
|
||||
* Throwing an instance of GuzzleHttp\Ring\CancelledException will mark
|
||||
* the future as realized, will not throw immediately, but will throw the
|
||||
* exception if the future's wait() method is called again.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function wait();
|
||||
|
||||
/**
|
||||
* Cancels the future, if possible.
|
||||
*/
|
||||
public function cancel();
|
||||
}
|
12
core/vendor/guzzlehttp/ringphp/src/Future/FutureValue.php
vendored
Normal file
12
core/vendor/guzzlehttp/ringphp/src/Future/FutureValue.php
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
/**
|
||||
* Represents a future value that responds to wait() to retrieve the promised
|
||||
* value, but can also return promises that are delivered the value when it is
|
||||
* available.
|
||||
*/
|
||||
class FutureValue implements FutureInterface
|
||||
{
|
||||
use BaseFutureTrait;
|
||||
}
|
32
core/vendor/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php
vendored
Normal file
32
core/vendor/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
/**
|
||||
* Implements common future functionality that is triggered when the result
|
||||
* property is accessed via a magic __get method.
|
||||
*
|
||||
* @property mixed $_value Actual data used by the future. Accessing this
|
||||
* property will cause the future to block if needed.
|
||||
*/
|
||||
trait MagicFutureTrait
|
||||
{
|
||||
use BaseFutureTrait;
|
||||
|
||||
/**
|
||||
* This function handles retrieving the dereferenced result when requested.
|
||||
*
|
||||
* @param string $name Should always be "data" or an exception is thrown.
|
||||
*
|
||||
* @return mixed Returns the dereferenced data.
|
||||
* @throws \RuntimeException
|
||||
* @throws \GuzzleHttp\Ring\Exception\CancelledException
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if ($name !== '_value') {
|
||||
throw new \RuntimeException("Class has no {$name} property");
|
||||
}
|
||||
|
||||
return $this->_value = $this->wait();
|
||||
}
|
||||
}
|
Reference in a new issue