summaryrefslogtreecommitdiff
path: root/vendor/guzzle/guzzle/src/Guzzle/Plugin
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/guzzle/guzzle/src/Guzzle/Plugin')
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php84
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json27
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php91
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php40
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php76
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php126
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php30
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php47
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php34
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php28
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php25
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php30
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php36
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php25
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php36
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json28
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php11
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php353
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php43
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php53
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php30
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php46
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php266
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php32
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php174
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php19
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.php32
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php19
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/composer.json28
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php538
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php237
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php85
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php65
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php70
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php7
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/composer.json27
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php46
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json27
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php22
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php72
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php7
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/composer.json27
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php163
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json27
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php161
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json28
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php57
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php88
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json27
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php245
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json27
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php306
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json27
-rw-r--r--vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json44
54 files changed, 4299 insertions, 0 deletions
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php
new file mode 100644
index 0000000..ae59418
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Guzzle\Plugin\Async;
+
+use Guzzle\Common\Event;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\CurlException;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Sends requests but does not wait for the response
+ */
+class AsyncPlugin implements EventSubscriberInterface
+{
+ public static function getSubscribedEvents()
+ {
+ return array(
+ 'request.before_send' => 'onBeforeSend',
+ 'request.exception' => 'onRequestTimeout',
+ 'request.sent' => 'onRequestSent',
+ 'curl.callback.progress' => 'onCurlProgress'
+ );
+ }
+
+ /**
+ * Event used to ensure that progress callback are emitted from the curl handle's request mediator.
+ *
+ * @param Event $event
+ */
+ public function onBeforeSend(Event $event)
+ {
+ // Ensure that progress callbacks are dispatched
+ $event['request']->getCurlOptions()->set('progress', true);
+ }
+
+ /**
+ * Event emitted when a curl progress function is called. When the amount of data uploaded == the amount of data to
+ * upload OR any bytes have been downloaded, then time the request out after 1ms because we're done with
+ * transmitting the request, and tell curl not download a body.
+ *
+ * @param Event $event
+ */
+ public function onCurlProgress(Event $event)
+ {
+ if ($event['handle'] &&
+ ($event['downloaded'] || (isset($event['uploaded']) && $event['upload_size'] === $event['uploaded']))
+ ) {
+ // Timeout after 1ms
+ curl_setopt($event['handle'], CURLOPT_TIMEOUT_MS, 1);
+ // Even if the response is quick, tell curl not to download the body.
+ // - Note that we can only perform this shortcut if the request transmitted a body so as to ensure that the
+ // request method is not converted to a HEAD request before the request was sent via curl.
+ if ($event['uploaded']) {
+ curl_setopt($event['handle'], CURLOPT_NOBODY, true);
+ }
+ }
+ }
+
+ /**
+ * Event emitted when a curl exception occurs. Ignore the exception and set a mock response.
+ *
+ * @param Event $event
+ */
+ public function onRequestTimeout(Event $event)
+ {
+ if ($event['exception'] instanceof CurlException) {
+ $event['request']->setResponse(new Response(200, array(
+ 'X-Guzzle-Async' => 'Did not wait for the response'
+ )));
+ }
+ }
+
+ /**
+ * Event emitted when a request completes because it took less than 1ms. Add an X-Guzzle-Async header to notify the
+ * caller that there is no body in the message.
+ *
+ * @param Event $event
+ */
+ public function onRequestSent(Event $event)
+ {
+ // Let the caller know this was meant to be async
+ $event['request']->getResponse()->setHeader('X-Guzzle-Async', 'Did not wait for the response');
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json
new file mode 100644
index 0000000..dc3fc5b
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "guzzle/plugin-async",
+ "description": "Guzzle async request plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Async": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Async",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php
new file mode 100644
index 0000000..0a85983
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\HttpException;
+
+/**
+ * Abstract backoff strategy that allows for a chain of responsibility
+ */
+abstract class AbstractBackoffStrategy implements BackoffStrategyInterface
+{
+ /** @var AbstractBackoffStrategy Next strategy in the chain */
+ protected $next;
+
+ /** @param AbstractBackoffStrategy $next Next strategy in the chain */
+ public function setNext(AbstractBackoffStrategy $next)
+ {
+ $this->next = $next;
+ }
+
+ /**
+ * Get the next backoff strategy in the chain
+ *
+ * @return AbstractBackoffStrategy|null
+ */
+ public function getNext()
+ {
+ return $this->next;
+ }
+
+ public function getBackoffPeriod(
+ $retries,
+ RequestInterface $request,
+ Response $response = null,
+ HttpException $e = null
+ ) {
+ $delay = $this->getDelay($retries, $request, $response, $e);
+ if ($delay === false) {
+ // The strategy knows that this must not be retried
+ return false;
+ } elseif ($delay === null) {
+ // If the strategy is deferring a decision and the next strategy will not make a decision then return false
+ return !$this->next || !$this->next->makesDecision()
+ ? false
+ : $this->next->getBackoffPeriod($retries, $request, $response, $e);
+ } elseif ($delay === true) {
+ // if the strategy knows that it must retry but is deferring to the next to determine the delay
+ if (!$this->next) {
+ return 0;
+ } else {
+ $next = $this->next;
+ while ($next->makesDecision() && $next->getNext()) {
+ $next = $next->getNext();
+ }
+ return !$next->makesDecision() ? $next->getBackoffPeriod($retries, $request, $response, $e) : 0;
+ }
+ } else {
+ return $delay;
+ }
+ }
+
+ /**
+ * Check if the strategy does filtering and makes decisions on whether or not to retry.
+ *
+ * Strategies that return false will never retry if all of the previous strategies in a chain defer on a backoff
+ * decision.
+ *
+ * @return bool
+ */
+ abstract public function makesDecision();
+
+ /**
+ * Implement the concrete strategy
+ *
+ * @param int $retries Number of retries of the request
+ * @param RequestInterface $request Request that was sent
+ * @param Response $response Response that was received. Note that there may not be a response
+ * @param HttpException $e Exception that was encountered if any
+ *
+ * @return bool|int|null Returns false to not retry or the number of seconds to delay between retries. Return true
+ * or null to defer to the next strategy if available, and if not, return 0.
+ */
+ abstract protected function getDelay(
+ $retries,
+ RequestInterface $request,
+ Response $response = null,
+ HttpException $e = null
+ );
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php
new file mode 100644
index 0000000..6ebee6c
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+/**
+ * Strategy used to retry when certain error codes are encountered
+ */
+abstract class AbstractErrorCodeBackoffStrategy extends AbstractBackoffStrategy
+{
+ /** @var array Default cURL errors to retry */
+ protected static $defaultErrorCodes = array();
+
+ /** @var array Error codes that can be retried */
+ protected $errorCodes;
+
+ /**
+ * @param array $codes Array of codes that should be retried
+ * @param BackoffStrategyInterface $next The optional next strategy
+ */
+ public function __construct(array $codes = null, BackoffStrategyInterface $next = null)
+ {
+ $this->errorCodes = array_fill_keys($codes ?: static::$defaultErrorCodes, 1);
+ $this->next = $next;
+ }
+
+ /**
+ * Get the default failure codes to retry
+ *
+ * @return array
+ */
+ public static function getDefaultFailureCodes()
+ {
+ return static::$defaultErrorCodes;
+ }
+
+ public function makesDecision()
+ {
+ return true;
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php
new file mode 100644
index 0000000..ec54c28
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Common\Event;
+use Guzzle\Log\LogAdapterInterface;
+use Guzzle\Log\MessageFormatter;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Logs backoff retries triggered from the BackoffPlugin
+ *
+ * Format your log messages using a template that can contain template substitutions found in {@see MessageFormatter}.
+ * In addition to the default template substitutions, there is also:
+ *
+ * - retries: The number of times the request has been retried
+ * - delay: The amount of time the request is being delayed
+ */
+class BackoffLogger implements EventSubscriberInterface
+{
+ /** @var string Default log message template */
+ const DEFAULT_FORMAT = '[{ts}] {method} {url} - {code} {phrase} - Retries: {retries}, Delay: {delay}, Time: {connect_time}, {total_time}, cURL: {curl_code} {curl_error}';
+
+ /** @var LogAdapterInterface Logger used to log retries */
+ protected $logger;
+
+ /** @var MessageFormatter Formatter used to format log messages */
+ protected $formatter;
+
+ /**
+ * @param LogAdapterInterface $logger Logger used to log the retries
+ * @param MessageFormatter $formatter Formatter used to format log messages
+ */
+ public function __construct(LogAdapterInterface $logger, MessageFormatter $formatter = null)
+ {
+ $this->logger = $logger;
+ $this->formatter = $formatter ?: new MessageFormatter(self::DEFAULT_FORMAT);
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry');
+ }
+
+ /**
+ * Set the template to use for logging
+ *
+ * @param string $template Log message template
+ *
+ * @return self
+ */
+ public function setTemplate($template)
+ {
+ $this->formatter->setTemplate($template);
+
+ return $this;
+ }
+
+ /**
+ * Called when a request is being retried
+ *
+ * @param Event $event Event emitted
+ */
+ public function onRequestRetry(Event $event)
+ {
+ $this->logger->log($this->formatter->format(
+ $event['request'],
+ $event['response'],
+ $event['handle'],
+ array(
+ 'retries' => $event['retries'],
+ 'delay' => $event['delay']
+ )
+ ));
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php
new file mode 100644
index 0000000..99ace05
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Common\Event;
+use Guzzle\Common\AbstractHasDispatcher;
+use Guzzle\Http\Message\EntityEnclosingRequestInterface;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Curl\CurlMultiInterface;
+use Guzzle\Http\Exception\CurlException;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Plugin to automatically retry failed HTTP requests using a backoff strategy
+ */
+class BackoffPlugin extends AbstractHasDispatcher implements EventSubscriberInterface
+{
+ const DELAY_PARAM = CurlMultiInterface::BLOCKING;
+ const RETRY_PARAM = 'plugins.backoff.retry_count';
+ const RETRY_EVENT = 'plugins.backoff.retry';
+
+ /** @var BackoffStrategyInterface Backoff strategy */
+ protected $strategy;
+
+ /**
+ * @param BackoffStrategyInterface $strategy The backoff strategy used to determine whether or not to retry and
+ * the amount of delay between retries.
+ */
+ public function __construct(BackoffStrategyInterface $strategy = null)
+ {
+ $this->strategy = $strategy;
+ }
+
+ /**
+ * Retrieve a basic truncated exponential backoff plugin that will retry HTTP errors and cURL errors
+ *
+ * @param int $maxRetries Maximum number of retries
+ * @param array $httpCodes HTTP response codes to retry
+ * @param array $curlCodes cURL error codes to retry
+ *
+ * @return self
+ */
+ public static function getExponentialBackoff(
+ $maxRetries = 3,
+ array $httpCodes = null,
+ array $curlCodes = null
+ ) {
+ return new self(new TruncatedBackoffStrategy($maxRetries,
+ new HttpBackoffStrategy($httpCodes,
+ new CurlBackoffStrategy($curlCodes,
+ new ExponentialBackoffStrategy()
+ )
+ )
+ ));
+ }
+
+ public static function getAllEvents()
+ {
+ return array(self::RETRY_EVENT);
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ 'request.sent' => 'onRequestSent',
+ 'request.exception' => 'onRequestSent',
+ CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll'
+ );
+ }
+
+ /**
+ * Called when a request has been sent and isn't finished processing
+ *
+ * @param Event $event
+ */
+ public function onRequestSent(Event $event)
+ {
+ $request = $event['request'];
+ $response = $event['response'];
+ $exception = $event['exception'];
+
+ $params = $request->getParams();
+ $retries = (int) $params->get(self::RETRY_PARAM);
+ $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception);
+
+ if ($delay !== false) {
+ // Calculate how long to wait until the request should be retried
+ $params->set(self::RETRY_PARAM, ++$retries)
+ ->set(self::DELAY_PARAM, microtime(true) + $delay);
+ // Send the request again
+ $request->setState(RequestInterface::STATE_TRANSFER);
+ $this->dispatch(self::RETRY_EVENT, array(
+ 'request' => $request,
+ 'response' => $response,
+ 'handle' => ($exception && $exception instanceof CurlException) ? $exception->getCurlHandle() : null,
+ 'retries' => $retries,
+ 'delay' => $delay
+ ));
+ }
+ }
+
+ /**
+ * Called when a request is polling in the curl multi object
+ *
+ * @param Event $event
+ */
+ public function onRequestPoll(Event $event)
+ {
+ $request = $event['request'];
+ $delay = $request->getParams()->get(self::DELAY_PARAM);
+
+ // If the duration of the delay has passed, retry the request using the pool
+ if (null !== $delay && microtime(true) >= $delay) {
+ // Remove the request from the pool and then add it back again. This is required for cURL to know that we
+ // want to retry sending the easy handle.
+ $request->getParams()->remove(self::DELAY_PARAM);
+ // Rewind the request body if possible
+ if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) {
+ $request->getBody()->seek(0);
+ }
+ $multi = $event['curl_multi'];
+ $multi->remove($request);
+ $multi->add($request);
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php
new file mode 100644
index 0000000..4e590db
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\HttpException;
+
+/**
+ * Strategy to determine if a request should be retried and how long to delay between retries
+ */
+interface BackoffStrategyInterface
+{
+ /**
+ * Get the amount of time to delay in seconds before retrying a request
+ *
+ * @param int $retries Number of retries of the request
+ * @param RequestInterface $request Request that was sent
+ * @param Response $response Response that was received. Note that there may not be a response
+ * @param HttpException $e Exception that was encountered if any
+ *
+ * @return bool|int Returns false to not retry or the number of seconds to delay between retries
+ */
+ public function getBackoffPeriod(
+ $retries,
+ RequestInterface $request,
+ Response $response = null,
+ HttpException $e = null
+ );
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php
new file mode 100644
index 0000000..b4f77c3
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Common\Exception\InvalidArgumentException;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\HttpException;
+
+/**
+ * Strategy that will invoke a closure to determine whether or not to retry with a delay
+ */
+class CallbackBackoffStrategy extends AbstractBackoffStrategy
+{
+ /** @var \Closure|array|mixed Callable method to invoke */
+ protected $callback;
+
+ /** @var bool Whether or not this strategy makes a retry decision */
+ protected $decision;
+
+ /**
+ * @param \Closure|array|mixed $callback Callable method to invoke
+ * @param bool $decision Set to true if this strategy makes a backoff decision
+ * @param BackoffStrategyInterface $next The optional next strategy
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct($callback, $decision, BackoffStrategyInterface $next = null)
+ {
+ if (!is_callable($callback)) {
+ throw new InvalidArgumentException('The callback must be callable');
+ }
+ $this->callback = $callback;
+ $this->decision = (bool) $decision;
+ $this->next = $next;
+ }
+
+ public function makesDecision()
+ {
+ return $this->decision;
+ }
+
+ protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
+ {
+ return call_user_func($this->callback, $retries, $request, $response, $e);
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php
new file mode 100644
index 0000000..061d2a4
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\HttpException;
+
+/**
+ * Will retry the request using the same amount of delay for each retry.
+ *
+ * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
+ */
+class ConstantBackoffStrategy extends AbstractBackoffStrategy
+{
+ /** @var int Amount of time for each delay */
+ protected $delay;
+
+ /** @param int $delay Amount of time to delay between each additional backoff */
+ public function __construct($delay)
+ {
+ $this->delay = $delay;
+ }
+
+ public function makesDecision()
+ {
+ return false;
+ }
+
+ protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
+ {
+ return $this->delay;
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php
new file mode 100644
index 0000000..a584ed4
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\HttpException;
+use Guzzle\Http\Exception\CurlException;
+
+/**
+ * Strategy used to retry when certain cURL error codes are encountered.
+ */
+class CurlBackoffStrategy extends AbstractErrorCodeBackoffStrategy
+{
+ /** @var array Default cURL errors to retry */
+ protected static $defaultErrorCodes = array(
+ CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_CONNECT, CURLE_PARTIAL_FILE, CURLE_WRITE_ERROR, CURLE_READ_ERROR,
+ CURLE_OPERATION_TIMEOUTED, CURLE_SSL_CONNECT_ERROR, CURLE_HTTP_PORT_FAILED, CURLE_GOT_NOTHING,
+ CURLE_SEND_ERROR, CURLE_RECV_ERROR
+ );
+
+ protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
+ {
+ if ($e && $e instanceof CurlException) {
+ return isset($this->errorCodes[$e->getErrorNo()]) ? true : null;
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php
new file mode 100644
index 0000000..fb2912d
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\HttpException;
+
+/**
+ * Implements an exponential backoff retry strategy.
+ *
+ * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
+ */
+class ExponentialBackoffStrategy extends AbstractBackoffStrategy
+{
+ public function makesDecision()
+ {
+ return false;
+ }
+
+ protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
+ {
+ return (int) pow(2, $retries);
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php
new file mode 100644
index 0000000..9c63a14
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\HttpException;
+
+/**
+ * Strategy used to retry HTTP requests based on the response code.
+ *
+ * Retries 500 and 503 error by default.
+ */
+class HttpBackoffStrategy extends AbstractErrorCodeBackoffStrategy
+{
+ /** @var array Default cURL errors to retry */
+ protected static $defaultErrorCodes = array(500, 503);
+
+ protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
+ {
+ if ($response) {
+ //Short circuit the rest of the checks if it was successful
+ if ($response->isSuccessful()) {
+ return false;
+ } else {
+ return isset($this->errorCodes[$response->getStatusCode()]) ? true : null;
+ }
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php
new file mode 100644
index 0000000..b35e8a4
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\HttpException;
+
+/**
+ * Implements a linear backoff retry strategy.
+ *
+ * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
+ */
+class LinearBackoffStrategy extends AbstractBackoffStrategy
+{
+ /** @var int Amount of time to progress each delay */
+ protected $step;
+
+ /**
+ * @param int $step Amount of time to increase the delay each additional backoff
+ */
+ public function __construct($step = 1)
+ {
+ $this->step = $step;
+ }
+
+ public function makesDecision()
+ {
+ return false;
+ }
+
+ protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
+ {
+ return $retries * $this->step;
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php
new file mode 100644
index 0000000..4fd73fe
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\HttpException;
+
+/**
+ * Strategy used to retry HTTP requests when the response's reason phrase matches one of the registered phrases.
+ */
+class ReasonPhraseBackoffStrategy extends AbstractErrorCodeBackoffStrategy
+{
+ public function makesDecision()
+ {
+ return true;
+ }
+
+ protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
+ {
+ if ($response) {
+ return isset($this->errorCodes[$response->getReasonPhrase()]) ? true : null;
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php
new file mode 100644
index 0000000..3608f35
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Guzzle\Plugin\Backoff;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\HttpException;
+
+/**
+ * Strategy that will not retry more than a certain number of times.
+ */
+class TruncatedBackoffStrategy extends AbstractBackoffStrategy
+{
+ /** @var int Maximum number of retries per request */
+ protected $max;
+
+ /**
+ * @param int $maxRetries Maximum number of retries per request
+ * @param BackoffStrategyInterface $next The optional next strategy
+ */
+ public function __construct($maxRetries, BackoffStrategyInterface $next = null)
+ {
+ $this->max = $maxRetries;
+ $this->next = $next;
+ }
+
+ public function makesDecision()
+ {
+ return true;
+ }
+
+ protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
+ {
+ return $retries < $this->max ? null : false;
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json
new file mode 100644
index 0000000..91c122c
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json
@@ -0,0 +1,28 @@
+{
+ "name": "guzzle/plugin-backoff",
+ "description": "Guzzle backoff retry plugins",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version",
+ "guzzle/log": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Backoff": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Backoff",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php
new file mode 100644
index 0000000..7790f88
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+\Guzzle\Common\Version::warn('Guzzle\Plugin\Cache\CacheKeyProviderInterface is no longer used');
+
+/**
+ * @deprecated This is no longer used
+ * @codeCoverageIgnore
+ */
+interface CacheKeyProviderInterface {}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php
new file mode 100644
index 0000000..ce4b317
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php
@@ -0,0 +1,353 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+use Guzzle\Cache\CacheAdapterFactory;
+use Guzzle\Cache\CacheAdapterInterface;
+use Guzzle\Common\Event;
+use Guzzle\Common\Exception\InvalidArgumentException;
+use Guzzle\Common\Version;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Cache\DoctrineCacheAdapter;
+use Guzzle\Http\Exception\CurlException;
+use Doctrine\Common\Cache\ArrayCache;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Plugin to enable the caching of GET and HEAD requests. Caching can be done on all requests passing through this
+ * plugin or only after retrieving resources with cacheable response headers.
+ *
+ * This is a simple implementation of RFC 2616 and should be considered a private transparent proxy cache, meaning
+ * authorization and private data can be cached.
+ *
+ * It also implements RFC 5861's `stale-if-error` Cache-Control extension, allowing stale cache responses to be used
+ * when an error is encountered (such as a `500 Internal Server Error` or DNS failure).
+ */
+class CachePlugin implements EventSubscriberInterface
+{
+ /** @var RevalidationInterface Cache revalidation strategy */
+ protected $revalidation;
+
+ /** @var CanCacheStrategyInterface Object used to determine if a request can be cached */
+ protected $canCache;
+
+ /** @var CacheStorageInterface $cache Object used to cache responses */
+ protected $storage;
+
+ /** @var bool */
+ protected $autoPurge;
+
+ /**
+ * @param array|CacheAdapterInterface|CacheStorageInterface $options Array of options for the cache plugin,
+ * cache adapter, or cache storage object.
+ * - CacheStorageInterface storage: Adapter used to cache responses
+ * - RevalidationInterface revalidation: Cache revalidation strategy
+ * - CanCacheInterface can_cache: Object used to determine if a request can be cached
+ * - bool auto_purge Set to true to automatically PURGE resources when non-idempotent
+ * requests are sent to a resource. Defaults to false.
+ * @throws InvalidArgumentException if no cache is provided and Doctrine cache is not installed
+ */
+ public function __construct($options = null)
+ {
+ if (!is_array($options)) {
+ if ($options instanceof CacheAdapterInterface) {
+ $options = array('storage' => new DefaultCacheStorage($options));
+ } elseif ($options instanceof CacheStorageInterface) {
+ $options = array('storage' => $options);
+ } elseif ($options) {
+ $options = array('storage' => new DefaultCacheStorage(CacheAdapterFactory::fromCache($options)));
+ } elseif (!class_exists('Doctrine\Common\Cache\ArrayCache')) {
+ // @codeCoverageIgnoreStart
+ throw new InvalidArgumentException('No cache was provided and Doctrine is not installed');
+ // @codeCoverageIgnoreEnd
+ }
+ }
+
+ $this->autoPurge = isset($options['auto_purge']) ? $options['auto_purge'] : false;
+
+ // Add a cache storage if a cache adapter was provided
+ $this->storage = isset($options['storage'])
+ ? $options['storage']
+ : new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache()));
+
+ if (!isset($options['can_cache'])) {
+ $this->canCache = new DefaultCanCacheStrategy();
+ } else {
+ $this->canCache = is_callable($options['can_cache'])
+ ? new CallbackCanCacheStrategy($options['can_cache'])
+ : $options['can_cache'];
+ }
+
+ // Use the provided revalidation strategy or the default
+ $this->revalidation = isset($options['revalidation'])
+ ? $options['revalidation']
+ : new DefaultRevalidation($this->storage, $this->canCache);
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ 'request.before_send' => array('onRequestBeforeSend', -255),
+ 'request.sent' => array('onRequestSent', 255),
+ 'request.error' => array('onRequestError', 0),
+ 'request.exception' => array('onRequestException', 0),
+ );
+ }
+
+ /**
+ * Check if a response in cache will satisfy the request before sending
+ *
+ * @param Event $event
+ */
+ public function onRequestBeforeSend(Event $event)
+ {
+ $request = $event['request'];
+ $request->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION));
+
+ if (!$this->canCache->canCacheRequest($request)) {
+ switch ($request->getMethod()) {
+ case 'PURGE':
+ $this->purge($request);
+ $request->setResponse(new Response(200, array(), 'purged'));
+ break;
+ case 'PUT':
+ case 'POST':
+ case 'DELETE':
+ case 'PATCH':
+ if ($this->autoPurge) {
+ $this->purge($request);
+ }
+ }
+ return;
+ }
+
+ if ($response = $this->storage->fetch($request)) {
+ $params = $request->getParams();
+ $params['cache.lookup'] = true;
+ $response->setHeader(
+ 'Age',
+ time() - strtotime($response->getDate() ? : $response->getLastModified() ?: 'now')
+ );
+ // Validate that the response satisfies the request
+ if ($this->canResponseSatisfyRequest($request, $response)) {
+ if (!isset($params['cache.hit'])) {
+ $params['cache.hit'] = true;
+ }
+ $request->setResponse($response);
+ }
+ }
+ }
+
+ /**
+ * If possible, store a response in cache after sending
+ *
+ * @param Event $event
+ */
+ public function onRequestSent(Event $event)
+ {
+ $request = $event['request'];
+ $response = $event['response'];
+
+ if ($request->getParams()->get('cache.hit') === null &&
+ $this->canCache->canCacheRequest($request) &&
+ $this->canCache->canCacheResponse($response)
+ ) {
+ $this->storage->cache($request, $response);
+ }
+
+ $this->addResponseHeaders($request, $response);
+ }
+
+ /**
+ * If possible, return a cache response on an error
+ *
+ * @param Event $event
+ */
+ public function onRequestError(Event $event)
+ {
+ $request = $event['request'];
+
+ if (!$this->canCache->canCacheRequest($request)) {
+ return;
+ }
+
+ if ($response = $this->storage->fetch($request)) {
+ $response->setHeader(
+ 'Age',
+ time() - strtotime($response->getLastModified() ? : $response->getDate() ?: 'now')
+ );
+
+ if ($this->canResponseSatisfyFailedRequest($request, $response)) {
+ $request->getParams()->set('cache.hit', 'error');
+ $this->addResponseHeaders($request, $response);
+ $event['response'] = $response;
+ $event->stopPropagation();
+ }
+ }
+ }
+
+ /**
+ * If possible, set a cache response on a cURL exception
+ *
+ * @param Event $event
+ *
+ * @return null
+ */
+ public function onRequestException(Event $event)
+ {
+ if (!$event['exception'] instanceof CurlException) {
+ return;
+ }
+
+ $request = $event['request'];
+ if (!$this->canCache->canCacheRequest($request)) {
+ return;
+ }
+
+ if ($response = $this->storage->fetch($request)) {
+ $response->setHeader('Age', time() - strtotime($response->getDate() ? : 'now'));
+ if (!$this->canResponseSatisfyFailedRequest($request, $response)) {
+ return;
+ }
+ $request->getParams()->set('cache.hit', 'error');
+ $request->setResponse($response);
+ $this->addResponseHeaders($request, $response);
+ $event->stopPropagation();
+ }
+ }
+
+ /**
+ * Check if a cache response satisfies a request's caching constraints
+ *
+ * @param RequestInterface $request Request to validate
+ * @param Response $response Response to validate
+ *
+ * @return bool
+ */
+ public function canResponseSatisfyRequest(RequestInterface $request, Response $response)
+ {
+ $responseAge = $response->calculateAge();
+ $reqc = $request->getHeader('Cache-Control');
+ $resc = $response->getHeader('Cache-Control');
+
+ // Check the request's max-age header against the age of the response
+ if ($reqc && $reqc->hasDirective('max-age') &&
+ $responseAge > $reqc->getDirective('max-age')) {
+ return false;
+ }
+
+ // Check the response's max-age header
+ if ($response->isFresh() === false) {
+ $maxStale = $reqc ? $reqc->getDirective('max-stale') : null;
+ if (null !== $maxStale) {
+ if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) {
+ return false;
+ }
+ } elseif ($resc && $resc->hasDirective('max-age')
+ && $responseAge > $resc->getDirective('max-age')
+ ) {
+ return false;
+ }
+ }
+
+ if ($this->revalidation->shouldRevalidate($request, $response)) {
+ try {
+ return $this->revalidation->revalidate($request, $response);
+ } catch (CurlException $e) {
+ $request->getParams()->set('cache.hit', 'error');
+ return $this->canResponseSatisfyFailedRequest($request, $response);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if a cache response satisfies a failed request's caching constraints
+ *
+ * @param RequestInterface $request Request to validate
+ * @param Response $response Response to validate
+ *
+ * @return bool
+ */
+ public function canResponseSatisfyFailedRequest(RequestInterface $request, Response $response)
+ {
+ $reqc = $request->getHeader('Cache-Control');
+ $resc = $response->getHeader('Cache-Control');
+ $requestStaleIfError = $reqc ? $reqc->getDirective('stale-if-error') : null;
+ $responseStaleIfError = $resc ? $resc->getDirective('stale-if-error') : null;
+
+ if (!$requestStaleIfError && !$responseStaleIfError) {
+ return false;
+ }
+
+ if (is_numeric($requestStaleIfError) && $response->getAge() - $response->getMaxAge() > $requestStaleIfError) {
+ return false;
+ }
+
+ if (is_numeric($responseStaleIfError) && $response->getAge() - $response->getMaxAge() > $responseStaleIfError) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Purge all cache entries for a given URL
+ *
+ * @param string $url URL to purge
+ */
+ public function purge($url)
+ {
+ // BC compatibility with previous version that accepted a Request object
+ $url = $url instanceof RequestInterface ? $url->getUrl() : $url;
+ $this->storage->purge($url);
+ }
+
+ /**
+ * Add the plugin's headers to a response
+ *
+ * @param RequestInterface $request Request
+ * @param Response $response Response to add headers to
+ */
+ protected function addResponseHeaders(RequestInterface $request, Response $response)
+ {
+ $params = $request->getParams();
+ $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION));
+
+ $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache';
+ if ($header = $response->getHeader('X-Cache-Lookup')) {
+ // Don't add duplicates
+ $values = $header->toArray();
+ $values[] = $lookup;
+ $response->setHeader('X-Cache-Lookup', array_unique($values));
+ } else {
+ $response->setHeader('X-Cache-Lookup', $lookup);
+ }
+
+ if ($params['cache.hit'] === true) {
+ $xcache = 'HIT from GuzzleCache';
+ } elseif ($params['cache.hit'] == 'error') {
+ $xcache = 'HIT_ERROR from GuzzleCache';
+ } else {
+ $xcache = 'MISS from GuzzleCache';
+ }
+
+ if ($header = $response->getHeader('X-Cache')) {
+ // Don't add duplicates
+ $values = $header->toArray();
+ $values[] = $xcache;
+ $response->setHeader('X-Cache', array_unique($values));
+ } else {
+ $response->setHeader('X-Cache', $xcache);
+ }
+
+ if ($response->isFresh() === false) {
+ $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION));
+ if ($params['cache.hit'] === 'error') {
+ $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION));
+ }
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php
new file mode 100644
index 0000000..f3d9154
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+
+/**
+ * Interface used to cache HTTP requests
+ */
+interface CacheStorageInterface
+{
+ /**
+ * Get a Response from the cache for a request
+ *
+ * @param RequestInterface $request
+ *
+ * @return null|Response
+ */
+ public function fetch(RequestInterface $request);
+
+ /**
+ * Cache an HTTP request
+ *
+ * @param RequestInterface $request Request being cached
+ * @param Response $response Response to cache
+ */
+ public function cache(RequestInterface $request, Response $response);
+
+ /**
+ * Deletes cache entries that match a request
+ *
+ * @param RequestInterface $request Request to delete from cache
+ */
+ public function delete(RequestInterface $request);
+
+ /**
+ * Purge all cache entries for a given URL
+ *
+ * @param string $url
+ */
+ public function purge($url);
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php
new file mode 100644
index 0000000..2d271e3
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+use Guzzle\Common\Exception\InvalidArgumentException;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+
+/**
+ * Determines if a request can be cached using a callback
+ */
+class CallbackCanCacheStrategy extends DefaultCanCacheStrategy
+{
+ /** @var callable Callback for request */
+ protected $requestCallback;
+
+ /** @var callable Callback for response */
+ protected $responseCallback;
+
+ /**
+ * @param \Closure|array|mixed $requestCallback Callable method to invoke for requests
+ * @param \Closure|array|mixed $responseCallback Callable method to invoke for responses
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct($requestCallback = null, $responseCallback = null)
+ {
+ if ($requestCallback && !is_callable($requestCallback)) {
+ throw new InvalidArgumentException('Method must be callable');
+ }
+
+ if ($responseCallback && !is_callable($responseCallback)) {
+ throw new InvalidArgumentException('Method must be callable');
+ }
+
+ $this->requestCallback = $requestCallback;
+ $this->responseCallback = $responseCallback;
+ }
+
+ public function canCacheRequest(RequestInterface $request)
+ {
+ return $this->requestCallback
+ ? call_user_func($this->requestCallback, $request)
+ : parent::canCacheRequest($request);
+ }
+
+ public function canCacheResponse(Response $response)
+ {
+ return $this->responseCallback
+ ? call_user_func($this->responseCallback, $response)
+ : parent::canCacheResponse($response);
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php
new file mode 100644
index 0000000..6e01a8e
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+
+/**
+ * Strategy used to determine if a request can be cached
+ */
+interface CanCacheStrategyInterface
+{
+ /**
+ * Determine if a request can be cached
+ *
+ * @param RequestInterface $request Request to determine
+ *
+ * @return bool
+ */
+ public function canCacheRequest(RequestInterface $request);
+
+ /**
+ * Determine if a response can be cached
+ *
+ * @param Response $response Response to determine
+ *
+ * @return bool
+ */
+ public function canCacheResponse(Response $response);
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php
new file mode 100644
index 0000000..ec0dc4e
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+use Guzzle\Http\Message\RequestInterface;
+
+\Guzzle\Common\Version::warn('Guzzle\Plugin\Cache\DefaultCacheKeyProvider is no longer used');
+
+/**
+ * @deprecated This class is no longer used
+ * @codeCoverageIgnore
+ */
+class DefaultCacheKeyProvider implements CacheKeyProviderInterface
+{
+ public function getCacheKey(RequestInterface $request)
+ {
+ // See if the key has already been calculated
+ $key = $request->getParams()->get(self::CACHE_KEY);
+
+ if (!$key) {
+
+ $cloned = clone $request;
+ $cloned->removeHeader('Cache-Control');
+
+ // Check to see how and if the key should be filtered
+ foreach (explode(';', $request->getParams()->get(self::CACHE_KEY_FILTER)) as $part) {
+ $pieces = array_map('trim', explode('=', $part));
+ if (isset($pieces[1])) {
+ foreach (array_map('trim', explode(',', $pieces[1])) as $remove) {
+ if ($pieces[0] == 'header') {
+ $cloned->removeHeader($remove);
+ } elseif ($pieces[0] == 'query') {
+ $cloned->getQuery()->remove($remove);
+ }
+ }
+ }
+ }
+
+ $raw = (string) $cloned;
+ $key = 'GZ' . md5($raw);
+ $request->getParams()->set(self::CACHE_KEY, $key)->set(self::CACHE_KEY_RAW, $raw);
+ }
+
+ return $key;
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php
new file mode 100644
index 0000000..26d7a8b
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php
@@ -0,0 +1,266 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+use Guzzle\Cache\CacheAdapterFactory;
+use Guzzle\Cache\CacheAdapterInterface;
+use Guzzle\Http\EntityBodyInterface;
+use Guzzle\Http\Message\MessageInterface;
+use Guzzle\Http\Message\Request;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+
+/**
+ * Default cache storage implementation
+ */
+class DefaultCacheStorage implements CacheStorageInterface
+{
+ /** @var string */
+ protected $keyPrefix;
+
+ /** @var CacheAdapterInterface Cache used to store cache data */
+ protected $cache;
+
+ /** @var int Default cache TTL */
+ protected $defaultTtl;
+
+ /**
+ * @param mixed $cache Cache used to store cache data
+ * @param string $keyPrefix Provide an optional key prefix to prefix on all cache keys
+ * @param int $defaultTtl Default cache TTL
+ */
+ public function __construct($cache, $keyPrefix = '', $defaultTtl = 3600)
+ {
+ $this->cache = CacheAdapterFactory::fromCache($cache);
+ $this->defaultTtl = $defaultTtl;
+ $this->keyPrefix = $keyPrefix;
+ }
+
+ public function cache(RequestInterface $request, Response $response)
+ {
+ $currentTime = time();
+
+ $overrideTtl = $request->getParams()->get('cache.override_ttl');
+ if ($overrideTtl) {
+ $ttl = $overrideTtl;
+ } else {
+ $maxAge = $response->getMaxAge();
+ if ($maxAge !== null) {
+ $ttl = $maxAge;
+ } else {
+ $ttl = $this->defaultTtl;
+ }
+ }
+
+ if ($cacheControl = $response->getHeader('Cache-Control')) {
+ $stale = $cacheControl->getDirective('stale-if-error');
+ if ($stale === true) {
+ $ttl += $ttl;
+ } else if (is_numeric($stale)) {
+ $ttl += $stale;
+ }
+ }
+
+ // Determine which manifest key should be used
+ $key = $this->getCacheKey($request);
+ $persistedRequest = $this->persistHeaders($request);
+ $entries = array();
+
+ if ($manifest = $this->cache->fetch($key)) {
+ // Determine which cache entries should still be in the cache
+ $vary = $response->getVary();
+ foreach (unserialize($manifest) as $entry) {
+ // Check if the entry is expired
+ if ($entry[4] < $currentTime) {
+ continue;
+ }
+ $entry[1]['vary'] = isset($entry[1]['vary']) ? $entry[1]['vary'] : '';
+ if ($vary != $entry[1]['vary'] || !$this->requestsMatch($vary, $entry[0], $persistedRequest)) {
+ $entries[] = $entry;
+ }
+ }
+ }
+
+ // Persist the response body if needed
+ $bodyDigest = null;
+ if ($response->getBody() && $response->getBody()->getContentLength() > 0) {
+ $bodyDigest = $this->getBodyKey($request->getUrl(), $response->getBody());
+ $this->cache->save($bodyDigest, (string) $response->getBody(), $ttl);
+ }
+
+ array_unshift($entries, array(
+ $persistedRequest,
+ $this->persistHeaders($response),
+ $response->getStatusCode(),
+ $bodyDigest,
+ $currentTime + $ttl
+ ));
+
+ $this->cache->save($key, serialize($entries));
+ }
+
+ public function delete(RequestInterface $request)
+ {
+ $key = $this->getCacheKey($request);
+ if ($entries = $this->cache->fetch($key)) {
+ // Delete each cached body
+ foreach (unserialize($entries) as $entry) {
+ if ($entry[3]) {
+ $this->cache->delete($entry[3]);
+ }
+ }
+ $this->cache->delete($key);
+ }
+ }
+
+ public function purge($url)
+ {
+ foreach (array('GET', 'HEAD', 'POST', 'PUT', 'DELETE') as $method) {
+ $this->delete(new Request($method, $url));
+ }
+ }
+
+ public function fetch(RequestInterface $request)
+ {
+ $key = $this->getCacheKey($request);
+ if (!($entries = $this->cache->fetch($key))) {
+ return null;
+ }
+
+ $match = null;
+ $headers = $this->persistHeaders($request);
+ $entries = unserialize($entries);
+ foreach ($entries as $index => $entry) {
+ if ($this->requestsMatch(isset($entry[1]['vary']) ? $entry[1]['vary'] : '', $headers, $entry[0])) {
+ $match = $entry;
+ break;
+ }
+ }
+
+ if (!$match) {
+ return null;
+ }
+
+ // Ensure that the response is not expired
+ $response = null;
+ if ($match[4] < time()) {
+ $response = -1;
+ } else {
+ $response = new Response($match[2], $match[1]);
+ if ($match[3]) {
+ if ($body = $this->cache->fetch($match[3])) {
+ $response->setBody($body);
+ } else {
+ // The response is not valid because the body was somehow deleted
+ $response = -1;
+ }
+ }
+ }
+
+ if ($response === -1) {
+ // Remove the entry from the metadata and update the cache
+ unset($entries[$index]);
+ if ($entries) {
+ $this->cache->save($key, serialize($entries));
+ } else {
+ $this->cache->delete($key);
+ }
+ return null;
+ }
+
+ return $response;
+ }
+
+ /**
+ * Hash a request URL into a string that returns cache metadata
+ *
+ * @param RequestInterface $request
+ *
+ * @return string
+ */
+ protected function getCacheKey(RequestInterface $request)
+ {
+ // Allow cache.key_filter to trim down the URL cache key by removing generate query string values (e.g. auth)
+ if ($filter = $request->getParams()->get('cache.key_filter')) {
+ $url = $request->getUrl(true);
+ foreach (explode(',', $filter) as $remove) {
+ $url->getQuery()->remove(trim($remove));
+ }
+ } else {
+ $url = $request->getUrl();
+ }
+
+ return $this->keyPrefix . md5($request->getMethod() . ' ' . $url);
+ }
+
+ /**
+ * Create a cache key for a response's body
+ *
+ * @param string $url URL of the entry
+ * @param EntityBodyInterface $body Response body
+ *
+ * @return string
+ */
+ protected function getBodyKey($url, EntityBodyInterface $body)
+ {
+ return $this->keyPrefix . md5($url) . $body->getContentMd5();
+ }
+
+ /**
+ * Determines whether two Request HTTP header sets are non-varying
+ *
+ * @param string $vary Response vary header
+ * @param array $r1 HTTP header array
+ * @param array $r2 HTTP header array
+ *
+ * @return bool
+ */
+ private function requestsMatch($vary, $r1, $r2)
+ {
+ if ($vary) {
+ foreach (explode(',', $vary) as $header) {
+ $key = trim(strtolower($header));
+ $v1 = isset($r1[$key]) ? $r1[$key] : null;
+ $v2 = isset($r2[$key]) ? $r2[$key] : null;
+ if ($v1 !== $v2) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Creates an array of cacheable and normalized message headers
+ *
+ * @param MessageInterface $message
+ *
+ * @return array
+ */
+ private function persistHeaders(MessageInterface $message)
+ {
+ // Headers are excluded from the caching (see RFC 2616:13.5.1)
+ static $noCache = array(
+ 'age' => true,
+ 'connection' => true,
+ 'keep-alive' => true,
+ 'proxy-authenticate' => true,
+ 'proxy-authorization' => true,
+ 'te' => true,
+ 'trailers' => true,
+ 'transfer-encoding' => true,
+ 'upgrade' => true,
+ 'set-cookie' => true,
+ 'set-cookie2' => true
+ );
+
+ // Clone the response to not destroy any necessary headers when caching
+ $headers = $message->getHeaders()->getAll();
+ $headers = array_diff_key($headers, $noCache);
+ // Cast the headers to a string
+ $headers = array_map(function ($h) { return (string) $h; }, $headers);
+
+ return $headers;
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php
new file mode 100644
index 0000000..3ca1fbf
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+
+/**
+ * Default strategy used to determine of an HTTP request can be cached
+ */
+class DefaultCanCacheStrategy implements CanCacheStrategyInterface
+{
+ public function canCacheRequest(RequestInterface $request)
+ {
+ // Only GET and HEAD requests can be cached
+ if ($request->getMethod() != RequestInterface::GET && $request->getMethod() != RequestInterface::HEAD) {
+ return false;
+ }
+
+ // Never cache requests when using no-store
+ if ($request->hasHeader('Cache-Control') && $request->getHeader('Cache-Control')->hasDirective('no-store')) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function canCacheResponse(Response $response)
+ {
+ return $response->isSuccessful() && $response->canCache();
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php
new file mode 100644
index 0000000..af33234
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php
@@ -0,0 +1,174 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Http\Exception\BadResponseException;
+
+/**
+ * Default revalidation strategy
+ */
+class DefaultRevalidation implements RevalidationInterface
+{
+ /** @var CacheStorageInterface Cache object storing cache data */
+ protected $storage;
+
+ /** @var CanCacheStrategyInterface */
+ protected $canCache;
+
+ /**
+ * @param CacheStorageInterface $cache Cache storage
+ * @param CanCacheStrategyInterface $canCache Determines if a message can be cached
+ */
+ public function __construct(CacheStorageInterface $cache, CanCacheStrategyInterface $canCache = null)
+ {
+ $this->storage = $cache;
+ $this->canCache = $canCache ?: new DefaultCanCacheStrategy();
+ }
+
+ public function revalidate(RequestInterface $request, Response $response)
+ {
+ try {
+ $revalidate = $this->createRevalidationRequest($request, $response);
+ $validateResponse = $revalidate->send();
+ if ($validateResponse->getStatusCode() == 200) {
+ return $this->handle200Response($request, $validateResponse);
+ } elseif ($validateResponse->getStatusCode() == 304) {
+ return $this->handle304Response($request, $validateResponse, $response);
+ }
+ } catch (BadResponseException $e) {
+ $this->handleBadResponse($e);
+ }
+
+ // Other exceptions encountered in the revalidation request are ignored
+ // in hopes that sending a request to the origin server will fix it
+ return false;
+ }
+
+ public function shouldRevalidate(RequestInterface $request, Response $response)
+ {
+ if ($request->getMethod() != RequestInterface::GET) {
+ return false;
+ }
+
+ $reqCache = $request->getHeader('Cache-Control');
+ $resCache = $response->getHeader('Cache-Control');
+
+ $revalidate = $request->getHeader('Pragma') == 'no-cache' ||
+ ($reqCache && ($reqCache->hasDirective('no-cache') || $reqCache->hasDirective('must-revalidate'))) ||
+ ($resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate')));
+
+ // Use the strong ETag validator if available and the response contains no Cache-Control directive
+ if (!$revalidate && !$resCache && $response->hasHeader('ETag')) {
+ $revalidate = true;
+ }
+
+ return $revalidate;
+ }
+
+ /**
+ * Handles a bad response when attempting to revalidate
+ *
+ * @param BadResponseException $e Exception encountered
+ *
+ * @throws BadResponseException
+ */
+ protected function handleBadResponse(BadResponseException $e)
+ {
+ // 404 errors mean the resource no longer exists, so remove from
+ // cache, and prevent an additional request by throwing the exception
+ if ($e->getResponse()->getStatusCode() == 404) {
+ $this->storage->delete($e->getRequest());
+ throw $e;
+ }
+ }
+
+ /**
+ * Creates a request to use for revalidation
+ *
+ * @param RequestInterface $request Request
+ * @param Response $response Response to revalidate
+ *
+ * @return RequestInterface returns a revalidation request
+ */
+ protected function createRevalidationRequest(RequestInterface $request, Response $response)
+ {
+ $revalidate = clone $request;
+ $revalidate->removeHeader('Pragma')->removeHeader('Cache-Control');
+
+ if ($response->getLastModified()) {
+ $revalidate->setHeader('If-Modified-Since', $response->getLastModified());
+ }
+
+ if ($response->getEtag()) {
+ $revalidate->setHeader('If-None-Match', $response->getEtag());
+ }
+
+ // Remove any cache plugins that might be on the request to prevent infinite recursive revalidations
+ $dispatcher = $revalidate->getEventDispatcher();
+ foreach ($dispatcher->getListeners() as $eventName => $listeners) {
+ foreach ($listeners as $listener) {
+ if (is_array($listener) && $listener[0] instanceof CachePlugin) {
+ $dispatcher->removeListener($eventName, $listener);
+ }
+ }
+ }
+
+ return $revalidate;
+ }
+
+ /**
+ * Handles a 200 response response from revalidating. The server does not support validation, so use this response.
+ *
+ * @param RequestInterface $request Request that was sent
+ * @param Response $validateResponse Response received
+ *
+ * @return bool Returns true if valid, false if invalid
+ */
+ protected function handle200Response(RequestInterface $request, Response $validateResponse)
+ {
+ $request->setResponse($validateResponse);
+ if ($this->canCache->canCacheResponse($validateResponse)) {
+ $this->storage->cache($request, $validateResponse);
+ }
+
+ return false;
+ }
+
+ /**
+ * Handle a 304 response and ensure that it is still valid
+ *
+ * @param RequestInterface $request Request that was sent
+ * @param Response $validateResponse Response received
+ * @param Response $response Original cached response
+ *
+ * @return bool Returns true if valid, false if invalid
+ */
+ protected function handle304Response(RequestInterface $request, Response $validateResponse, Response $response)
+ {
+ static $replaceHeaders = array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified');
+
+ // Make sure that this response has the same ETag
+ if ($validateResponse->getEtag() != $response->getEtag()) {
+ return false;
+ }
+
+ // Replace cached headers with any of these headers from the
+ // origin server that might be more up to date
+ $modified = false;
+ foreach ($replaceHeaders as $name) {
+ if ($validateResponse->hasHeader($name)) {
+ $modified = true;
+ $response->setHeader($name, $validateResponse->getHeader($name));
+ }
+ }
+
+ // Store the updated response in cache
+ if ($modified && $this->canCache->canCacheResponse($response)) {
+ $this->storage->cache($request, $response);
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php
new file mode 100644
index 0000000..88b86f3
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+
+/**
+ * Never performs cache revalidation and just assumes the request is invalid
+ */
+class DenyRevalidation extends DefaultRevalidation
+{
+ public function __construct() {}
+
+ public function revalidate(RequestInterface $request, Response $response)
+ {
+ return false;
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.php
new file mode 100644
index 0000000..52353d8
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+
+/**
+ * Cache revalidation interface
+ */
+interface RevalidationInterface
+{
+ /**
+ * Performs a cache revalidation
+ *
+ * @param RequestInterface $request Request to revalidate
+ * @param Response $response Response that was received
+ *
+ * @return bool Returns true if the request can be cached
+ */
+ public function revalidate(RequestInterface $request, Response $response);
+
+ /**
+ * Returns true if the response should be revalidated
+ *
+ * @param RequestInterface $request Request to check
+ * @param Response $response Response to check
+ *
+ * @return bool
+ */
+ public function shouldRevalidate(RequestInterface $request, Response $response);
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php
new file mode 100644
index 0000000..10b5c11
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Guzzle\Plugin\Cache;
+
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+
+/**
+ * Never performs cache revalidation and just assumes the request is still ok
+ */
+class SkipRevalidation extends DefaultRevalidation
+{
+ public function __construct() {}
+
+ public function revalidate(RequestInterface $request, Response $response)
+ {
+ return true;
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/composer.json
new file mode 100644
index 0000000..674d8c4
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/composer.json
@@ -0,0 +1,28 @@
+{
+ "name": "guzzle/plugin-cache",
+ "description": "Guzzle HTTP cache plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version",
+ "guzzle/cache": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Cache": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Cache",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php
new file mode 100644
index 0000000..5218e5f
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php
@@ -0,0 +1,538 @@
+<?php
+
+namespace Guzzle\Plugin\Cookie;
+
+use Guzzle\Common\ToArrayInterface;
+
+/**
+ * Set-Cookie object
+ */
+class Cookie implements ToArrayInterface
+{
+ /** @var array Cookie data */
+ protected $data;
+
+ /**
+ * @var string ASCII codes not valid for for use in a cookie name
+ *
+ * Cookie names are defined as 'token', according to RFC 2616, Section 2.2
+ * A valid token may contain any CHAR except CTLs (ASCII 0 - 31 or 127)
+ * or any of the following separators
+ */
+ protected static $invalidCharString;
+
+ /**
+ * Gets an array of invalid cookie characters
+ *
+ * @return array
+ */
+ protected static function getInvalidCharacters()
+ {
+ if (!self::$invalidCharString) {
+ self::$invalidCharString = implode('', array_map('chr', array_merge(
+ range(0, 32),
+ array(34, 40, 41, 44, 47),
+ array(58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 123, 125, 127)
+ )));
+ }
+
+ return self::$invalidCharString;
+ }
+
+ /**
+ * @param array $data Array of cookie data provided by a Cookie parser
+ */
+ public function __construct(array $data = array())
+ {
+ static $defaults = array(
+ 'name' => '',
+ 'value' => '',
+ 'domain' => '',
+ 'path' => '/',
+ 'expires' => null,
+ 'max_age' => 0,
+ 'comment' => null,
+ 'comment_url' => null,
+ 'port' => array(),
+ 'version' => null,
+ 'secure' => false,
+ 'discard' => false,
+ 'http_only' => false
+ );
+
+ $this->data = array_merge($defaults, $data);
+ // Extract the expires value and turn it into a UNIX timestamp if needed
+ if (!$this->getExpires() && $this->getMaxAge()) {
+ // Calculate the expires date
+ $this->setExpires(time() + (int) $this->getMaxAge());
+ } elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
+ $this->setExpires(strtotime($this->getExpires()));
+ }
+ }
+
+ /**
+ * Get the cookie as an array
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Get the cookie name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->data['name'];
+ }
+
+ /**
+ * Set the cookie name
+ *
+ * @param string $name Cookie name
+ *
+ * @return Cookie
+ */
+ public function setName($name)
+ {
+ return $this->setData('name', $name);
+ }
+
+ /**
+ * Get the cookie value
+ *
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->data['value'];
+ }
+
+ /**
+ * Set the cookie value
+ *
+ * @param string $value Cookie value
+ *
+ * @return Cookie
+ */
+ public function setValue($value)
+ {
+ return $this->setData('value', $value);
+ }
+
+ /**
+ * Get the domain
+ *
+ * @return string|null
+ */
+ public function getDomain()
+ {
+ return $this->data['domain'];
+ }
+
+ /**
+ * Set the domain of the cookie
+ *
+ * @param string $domain
+ *
+ * @return Cookie
+ */
+ public function setDomain($domain)
+ {
+ return $this->setData('domain', $domain);
+ }
+
+ /**
+ * Get the path
+ *
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->data['path'];
+ }
+
+ /**
+ * Set the path of the cookie
+ *
+ * @param string $path Path of the cookie
+ *
+ * @return Cookie
+ */
+ public function setPath($path)
+ {
+ return $this->setData('path', $path);
+ }
+
+ /**
+ * Maximum lifetime of the cookie in seconds
+ *
+ * @return int|null
+ */
+ public function getMaxAge()
+ {
+ return $this->data['max_age'];
+ }
+
+ /**
+ * Set the max-age of the cookie
+ *
+ * @param int $maxAge Max age of the cookie in seconds
+ *
+ * @return Cookie
+ */
+ public function setMaxAge($maxAge)
+ {
+ return $this->setData('max_age', $maxAge);
+ }
+
+ /**
+ * The UNIX timestamp when the cookie expires
+ *
+ * @return mixed
+ */
+ public function getExpires()
+ {
+ return $this->data['expires'];
+ }
+
+ /**
+ * Set the unix timestamp for which the cookie will expire
+ *
+ * @param int $timestamp Unix timestamp
+ *
+ * @return Cookie
+ */
+ public function setExpires($timestamp)
+ {
+ return $this->setData('expires', $timestamp);
+ }
+
+ /**
+ * Version of the cookie specification. RFC 2965 is 1
+ *
+ * @return mixed
+ */
+ public function getVersion()
+ {
+ return $this->data['version'];
+ }
+
+ /**
+ * Set the cookie version
+ *
+ * @param string|int $version Version to set
+ *
+ * @return Cookie
+ */
+ public function setVersion($version)
+ {
+ return $this->setData('version', $version);
+ }
+
+ /**
+ * Get whether or not this is a secure cookie
+ *
+ * @return null|bool
+ */
+ public function getSecure()
+ {
+ return $this->data['secure'];
+ }
+
+ /**
+ * Set whether or not the cookie is secure
+ *
+ * @param bool $secure Set to true or false if secure
+ *
+ * @return Cookie
+ */
+ public function setSecure($secure)
+ {
+ return $this->setData('secure', (bool) $secure);
+ }
+
+ /**
+ * Get whether or not this is a session cookie
+ *
+ * @return null|bool
+ */
+ public function getDiscard()
+ {
+ return $this->data['discard'];
+ }
+
+ /**
+ * Set whether or not this is a session cookie
+ *
+ * @param bool $discard Set to true or false if this is a session cookie
+ *
+ * @return Cookie
+ */
+ public function setDiscard($discard)
+ {
+ return $this->setData('discard', $discard);
+ }
+
+ /**
+ * Get the comment
+ *
+ * @return string|null
+ */
+ public function getComment()
+ {
+ return $this->data['comment'];
+ }
+
+ /**
+ * Set the comment of the cookie
+ *
+ * @param string $comment Cookie comment
+ *
+ * @return Cookie
+ */
+ public function setComment($comment)
+ {
+ return $this->setData('comment', $comment);
+ }
+
+ /**
+ * Get the comment URL of the cookie
+ *
+ * @return string|null
+ */
+ public function getCommentUrl()
+ {
+ return $this->data['comment_url'];
+ }
+
+ /**
+ * Set the comment URL of the cookie
+ *
+ * @param string $commentUrl Cookie comment URL for more information
+ *
+ * @return Cookie
+ */
+ public function setCommentUrl($commentUrl)
+ {
+ return $this->setData('comment_url', $commentUrl);
+ }
+
+ /**
+ * Get an array of acceptable ports this cookie can be used with
+ *
+ * @return array
+ */
+ public function getPorts()
+ {
+ return $this->data['port'];
+ }
+
+ /**
+ * Set a list of acceptable ports this cookie can be used with
+ *
+ * @param array $ports Array of acceptable ports
+ *
+ * @return Cookie
+ */
+ public function setPorts(array $ports)
+ {
+ return $this->setData('port', $ports);
+ }
+
+ /**
+ * Get whether or not this is an HTTP only cookie
+ *
+ * @return bool
+ */
+ public function getHttpOnly()
+ {
+ return $this->data['http_only'];
+ }
+
+ /**
+ * Set whether or not this is an HTTP only cookie
+ *
+ * @param bool $httpOnly Set to true or false if this is HTTP only
+ *
+ * @return Cookie
+ */
+ public function setHttpOnly($httpOnly)
+ {
+ return $this->setData('http_only', $httpOnly);
+ }
+
+ /**
+ * Get an array of extra cookie data
+ *
+ * @return array
+ */
+ public function getAttributes()
+ {
+ return $this->data['data'];
+ }
+
+ /**
+ * Get a specific data point from the extra cookie data
+ *
+ * @param string $name Name of the data point to retrieve
+ *
+ * @return null|string
+ */
+ public function getAttribute($name)
+ {
+ return array_key_exists($name, $this->data['data']) ? $this->data['data'][$name] : null;
+ }
+
+ /**
+ * Set a cookie data attribute
+ *
+ * @param string $name Name of the attribute to set
+ * @param string $value Value to set
+ *
+ * @return Cookie
+ */
+ public function setAttribute($name, $value)
+ {
+ $this->data['data'][$name] = $value;
+
+ return $this;
+ }
+
+ /**
+ * Check if the cookie matches a path value
+ *
+ * @param string $path Path to check against
+ *
+ * @return bool
+ */
+ public function matchesPath($path)
+ {
+ // RFC6265 http://tools.ietf.org/search/rfc6265#section-5.1.4
+ // A request-path path-matches a given cookie-path if at least one of
+ // the following conditions holds:
+
+ // o The cookie-path and the request-path are identical.
+ if ($path == $this->getPath()) {
+ return true;
+ }
+
+ $pos = stripos($path, $this->getPath());
+ if ($pos === 0) {
+ // o The cookie-path is a prefix of the request-path, and the last
+ // character of the cookie-path is %x2F ("/").
+ if (substr($this->getPath(), -1, 1) === "/") {
+ return true;
+ }
+
+ // o The cookie-path is a prefix of the request-path, and the first
+ // character of the request-path that is not included in the cookie-
+ // path is a %x2F ("/") character.
+ if (substr($path, strlen($this->getPath()), 1) === "/") {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if the cookie matches a domain value
+ *
+ * @param string $domain Domain to check against
+ *
+ * @return bool
+ */
+ public function matchesDomain($domain)
+ {
+ // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3
+ $cookieDomain = ltrim($this->getDomain(), '.');
+
+ // Domain not set or exact match.
+ if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
+ return true;
+ }
+
+ // Matching the subdomain according to RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.1.3
+ if (filter_var($domain, FILTER_VALIDATE_IP)) {
+ return false;
+ }
+
+ return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/i', $domain);
+ }
+
+ /**
+ * Check if the cookie is compatible with a specific port
+ *
+ * @param int $port Port to check
+ *
+ * @return bool
+ */
+ public function matchesPort($port)
+ {
+ return count($this->getPorts()) == 0 || in_array($port, $this->getPorts());
+ }
+
+ /**
+ * Check if the cookie is expired
+ *
+ * @return bool
+ */
+ public function isExpired()
+ {
+ return $this->getExpires() && time() > $this->getExpires();
+ }
+
+ /**
+ * Check if the cookie is valid according to RFC 6265
+ *
+ * @return bool|string Returns true if valid or an error message if invalid
+ */
+ public function validate()
+ {
+ // Names must not be empty, but can be 0
+ $name = $this->getName();
+ if (empty($name) && !is_numeric($name)) {
+ return 'The cookie name must not be empty';
+ }
+
+ // Check if any of the invalid characters are present in the cookie name
+ if (strpbrk($name, self::getInvalidCharacters()) !== false) {
+ return 'The cookie name must not contain invalid characters: ' . $name;
+ }
+
+ // Value must not be empty, but can be 0
+ $value = $this->getValue();
+ if (empty($value) && !is_numeric($value)) {
+ return 'The cookie value must not be empty';
+ }
+
+ // Domains must not be empty, but can be 0
+ // A "0" is not a valid internet domain, but may be used as server name in a private network
+ $domain = $this->getDomain();
+ if (empty($domain) && !is_numeric($domain)) {
+ return 'The cookie domain must not be empty';
+ }
+
+ return true;
+ }
+
+ /**
+ * Set a value and return the cookie object
+ *
+ * @param string $key Key to set
+ * @param string $value Value to set
+ *
+ * @return Cookie
+ */
+ private function setData($key, $value)
+ {
+ $this->data[$key] = $value;
+
+ return $this;
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php
new file mode 100644
index 0000000..6b67503
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php
@@ -0,0 +1,237 @@
+<?php
+
+namespace Guzzle\Plugin\Cookie\CookieJar;
+
+use Guzzle\Plugin\Cookie\Cookie;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Guzzle\Parser\ParserRegistry;
+use Guzzle\Plugin\Cookie\Exception\InvalidCookieException;
+
+/**
+ * Cookie cookieJar that stores cookies an an array
+ */
+class ArrayCookieJar implements CookieJarInterface, \Serializable
+{
+ /** @var array Loaded cookie data */
+ protected $cookies = array();
+
+ /** @var bool Whether or not strict mode is enabled. When enabled, exceptions will be thrown for invalid cookies */
+ protected $strictMode;
+
+ /**
+ * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added to the cookie jar
+ */
+ public function __construct($strictMode = false)
+ {
+ $this->strictMode = $strictMode;
+ }
+
+ /**
+ * Enable or disable strict mode on the cookie jar
+ *
+ * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added. False to ignore them.
+ *
+ * @return self
+ */
+ public function setStrictMode($strictMode)
+ {
+ $this->strictMode = $strictMode;
+ }
+
+ public function remove($domain = null, $path = null, $name = null)
+ {
+ $cookies = $this->all($domain, $path, $name, false, false);
+ $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($cookies) {
+ return !in_array($cookie, $cookies, true);
+ });
+
+ return $this;
+ }
+
+ public function removeTemporary()
+ {
+ $this->cookies = array_filter($this->cookies, function (Cookie $cookie) {
+ return !$cookie->getDiscard() && $cookie->getExpires();
+ });
+
+ return $this;
+ }
+
+ public function removeExpired()
+ {
+ $currentTime = time();
+ $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($currentTime) {
+ return !$cookie->getExpires() || $currentTime < $cookie->getExpires();
+ });
+
+ return $this;
+ }
+
+ public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true)
+ {
+ return array_values(array_filter($this->cookies, function (Cookie $cookie) use (
+ $domain,
+ $path,
+ $name,
+ $skipDiscardable,
+ $skipExpired
+ ) {
+ return false === (($name && $cookie->getName() != $name) ||
+ ($skipExpired && $cookie->isExpired()) ||
+ ($skipDiscardable && ($cookie->getDiscard() || !$cookie->getExpires())) ||
+ ($path && !$cookie->matchesPath($path)) ||
+ ($domain && !$cookie->matchesDomain($domain)));
+ }));
+ }
+
+ public function add(Cookie $cookie)
+ {
+ // Only allow cookies with set and valid domain, name, value
+ $result = $cookie->validate();
+ if ($result !== true) {
+ if ($this->strictMode) {
+ throw new InvalidCookieException($result);
+ } else {
+ $this->removeCookieIfEmpty($cookie);
+ return false;
+ }
+ }
+
+ // Resolve conflicts with previously set cookies
+ foreach ($this->cookies as $i => $c) {
+
+ // Two cookies are identical, when their path, domain, port and name are identical
+ if ($c->getPath() != $cookie->getPath() ||
+ $c->getDomain() != $cookie->getDomain() ||
+ $c->getPorts() != $cookie->getPorts() ||
+ $c->getName() != $cookie->getName()
+ ) {
+ continue;
+ }
+
+ // The previously set cookie is a discard cookie and this one is not so allow the new cookie to be set
+ if (!$cookie->getDiscard() && $c->getDiscard()) {
+ unset($this->cookies[$i]);
+ continue;
+ }
+
+ // If the new cookie's expiration is further into the future, then replace the old cookie
+ if ($cookie->getExpires() > $c->getExpires()) {
+ unset($this->cookies[$i]);
+ continue;
+ }
+
+ // If the value has changed, we better change it
+ if ($cookie->getValue() !== $c->getValue()) {
+ unset($this->cookies[$i]);
+ continue;
+ }
+
+ // The cookie exists, so no need to continue
+ return false;
+ }
+
+ $this->cookies[] = $cookie;
+
+ return true;
+ }
+
+ /**
+ * Serializes the cookie cookieJar
+ *
+ * @return string
+ */
+ public function serialize()
+ {
+ // Only serialize long term cookies and unexpired cookies
+ return json_encode(array_map(function (Cookie $cookie) {
+ return $cookie->toArray();
+ }, $this->all(null, null, null, true, true)));
+ }
+
+ /**
+ * Unserializes the cookie cookieJar
+ */
+ public function unserialize($data)
+ {
+ $data = json_decode($data, true);
+ if (empty($data)) {
+ $this->cookies = array();
+ } else {
+ $this->cookies = array_map(function (array $cookie) {
+ return new Cookie($cookie);
+ }, $data);
+ }
+ }
+
+ /**
+ * Returns the total number of stored cookies
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->cookies);
+ }
+
+ /**
+ * Returns an iterator
+ *
+ * @return \ArrayIterator
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->cookies);
+ }
+
+ public function addCookiesFromResponse(Response $response, RequestInterface $request = null)
+ {
+ if ($cookieHeader = $response->getHeader('Set-Cookie')) {
+ $parser = ParserRegistry::getInstance()->getParser('cookie');
+ foreach ($cookieHeader as $cookie) {
+ if ($parsed = $request
+ ? $parser->parseCookie($cookie, $request->getHost(), $request->getPath())
+ : $parser->parseCookie($cookie)
+ ) {
+ // Break up cookie v2 into multiple cookies
+ foreach ($parsed['cookies'] as $key => $value) {
+ $row = $parsed;
+ $row['name'] = $key;
+ $row['value'] = $value;
+ unset($row['cookies']);
+ $this->add(new Cookie($row));
+ }
+ }
+ }
+ }
+ }
+
+ public function getMatchingCookies(RequestInterface $request)
+ {
+ // Find cookies that match this request
+ $cookies = $this->all($request->getHost(), $request->getPath());
+ // Remove ineligible cookies
+ foreach ($cookies as $index => $cookie) {
+ if (!$cookie->matchesPort($request->getPort()) || ($cookie->getSecure() && $request->getScheme() != 'https')) {
+ unset($cookies[$index]);
+ }
+ };
+
+ return $cookies;
+ }
+
+ /**
+ * If a cookie already exists and the server asks to set it again with a null value, the
+ * cookie must be deleted.
+ *
+ * @param \Guzzle\Plugin\Cookie\Cookie $cookie
+ */
+ private function removeCookieIfEmpty(Cookie $cookie)
+ {
+ $cookieValue = $cookie->getValue();
+ if ($cookieValue === null || $cookieValue === '') {
+ $this->remove($cookie->getDomain(), $cookie->getPath(), $cookie->getName());
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php
new file mode 100644
index 0000000..7faa7d2
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Guzzle\Plugin\Cookie\CookieJar;
+
+use Guzzle\Plugin\Cookie\Cookie;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+
+/**
+ * Interface for persisting cookies
+ */
+interface CookieJarInterface extends \Countable, \IteratorAggregate
+{
+ /**
+ * Remove cookies currently held in the Cookie cookieJar.
+ *
+ * Invoking this method without arguments will empty the whole Cookie cookieJar. If given a $domain argument only
+ * cookies belonging to that domain will be removed. If given a $domain and $path argument, cookies belonging to
+ * the specified path within that domain are removed. If given all three arguments, then the cookie with the
+ * specified name, path and domain is removed.
+ *
+ * @param string $domain Set to clear only cookies matching a domain
+ * @param string $path Set to clear only cookies matching a domain and path
+ * @param string $name Set to clear only cookies matching a domain, path, and name
+ *
+ * @return CookieJarInterface
+ */
+ public function remove($domain = null, $path = null, $name = null);
+
+ /**
+ * Discard all temporary cookies.
+ *
+ * Scans for all cookies in the cookieJar with either no expire field or a true discard flag. To be called when the
+ * user agent shuts down according to RFC 2965.
+ *
+ * @return CookieJarInterface
+ */
+ public function removeTemporary();
+
+ /**
+ * Delete any expired cookies
+ *
+ * @return CookieJarInterface
+ */
+ public function removeExpired();
+
+ /**
+ * Add a cookie to the cookie cookieJar
+ *
+ * @param Cookie $cookie Cookie to add
+ *
+ * @return bool Returns true on success or false on failure
+ */
+ public function add(Cookie $cookie);
+
+ /**
+ * Add cookies from a {@see Guzzle\Http\Message\Response} object
+ *
+ * @param Response $response Response object
+ * @param RequestInterface $request Request that received the response
+ */
+ public function addCookiesFromResponse(Response $response, RequestInterface $request = null);
+
+ /**
+ * Get cookies matching a request object
+ *
+ * @param RequestInterface $request Request object to match
+ *
+ * @return array
+ */
+ public function getMatchingCookies(RequestInterface $request);
+
+ /**
+ * Get all of the matching cookies
+ *
+ * @param string $domain Domain of the cookie
+ * @param string $path Path of the cookie
+ * @param string $name Name of the cookie
+ * @param bool $skipDiscardable Set to TRUE to skip cookies with the Discard attribute.
+ * @param bool $skipExpired Set to FALSE to include expired
+ *
+ * @return array Returns an array of Cookie objects
+ */
+ public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true);
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php
new file mode 100644
index 0000000..99344cd
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Guzzle\Plugin\Cookie\CookieJar;
+
+use Guzzle\Common\Exception\RuntimeException;
+
+/**
+ * Persists non-session cookies using a JSON formatted file
+ */
+class FileCookieJar extends ArrayCookieJar
+{
+ /** @var string filename */
+ protected $filename;
+
+ /**
+ * Create a new FileCookieJar object
+ *
+ * @param string $cookieFile File to store the cookie data
+ *
+ * @throws RuntimeException if the file cannot be found or created
+ */
+ public function __construct($cookieFile)
+ {
+ $this->filename = $cookieFile;
+ $this->load();
+ }
+
+ /**
+ * Saves the file when shutting down
+ */
+ public function __destruct()
+ {
+ $this->persist();
+ }
+
+ /**
+ * Save the contents of the data array to the file
+ *
+ * @throws RuntimeException if the file cannot be found or created
+ */
+ protected function persist()
+ {
+ if (false === file_put_contents($this->filename, $this->serialize())) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException('Unable to open file ' . $this->filename);
+ // @codeCoverageIgnoreEnd
+ }
+ }
+
+ /**
+ * Load the contents of the json formatted file into the data array and discard any unsaved state
+ */
+ protected function load()
+ {
+ $json = file_get_contents($this->filename);
+ if (false === $json) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException('Unable to open file ' . $this->filename);
+ // @codeCoverageIgnoreEnd
+ }
+
+ $this->unserialize($json);
+ $this->cookies = $this->cookies ?: array();
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php
new file mode 100644
index 0000000..df3210e
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Guzzle\Plugin\Cookie;
+
+use Guzzle\Common\Event;
+use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar;
+use Guzzle\Plugin\Cookie\CookieJar\CookieJarInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Adds, extracts, and persists cookies between HTTP requests
+ */
+class CookiePlugin implements EventSubscriberInterface
+{
+ /** @var CookieJarInterface Cookie cookieJar used to hold cookies */
+ protected $cookieJar;
+
+ /**
+ * @param CookieJarInterface $cookieJar Cookie jar used to hold cookies. Creates an ArrayCookieJar by default.
+ */
+ public function __construct(CookieJarInterface $cookieJar = null)
+ {
+ $this->cookieJar = $cookieJar ?: new ArrayCookieJar();
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ 'request.before_send' => array('onRequestBeforeSend', 125),
+ 'request.sent' => array('onRequestSent', 125)
+ );
+ }
+
+ /**
+ * Get the cookie cookieJar
+ *
+ * @return CookieJarInterface
+ */
+ public function getCookieJar()
+ {
+ return $this->cookieJar;
+ }
+
+ /**
+ * Add cookies before a request is sent
+ *
+ * @param Event $event
+ */
+ public function onRequestBeforeSend(Event $event)
+ {
+ $request = $event['request'];
+ if (!$request->getParams()->get('cookies.disable')) {
+ $request->removeHeader('Cookie');
+ // Find cookies that match this request
+ foreach ($this->cookieJar->getMatchingCookies($request) as $cookie) {
+ $request->addCookie($cookie->getName(), $cookie->getValue());
+ }
+ }
+ }
+
+ /**
+ * Extract cookies from a sent request
+ *
+ * @param Event $event
+ */
+ public function onRequestSent(Event $event)
+ {
+ $this->cookieJar->addCookiesFromResponse($event['response'], $event['request']);
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php
new file mode 100644
index 0000000..b1fa6fd
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Guzzle\Plugin\Cookie\Exception;
+
+use Guzzle\Common\Exception\InvalidArgumentException;
+
+class InvalidCookieException extends InvalidArgumentException {}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/composer.json
new file mode 100644
index 0000000..f0b5230
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "guzzle/plugin-cookie",
+ "description": "Guzzle cookie plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Cookie": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Cookie",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php
new file mode 100644
index 0000000..610e60c
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Guzzle\Plugin\CurlAuth;
+
+use Guzzle\Common\Event;
+use Guzzle\Common\Version;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Adds specified curl auth to all requests sent from a client. Defaults to CURLAUTH_BASIC if none supplied.
+ * @deprecated Use $client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');
+ */
+class CurlAuthPlugin implements EventSubscriberInterface
+{
+ private $username;
+ private $password;
+ private $scheme;
+
+ /**
+ * @param string $username HTTP basic auth username
+ * @param string $password Password
+ * @param int $scheme Curl auth scheme
+ */
+ public function __construct($username, $password, $scheme=CURLAUTH_BASIC)
+ {
+ Version::warn(__CLASS__ . " is deprecated. Use \$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');");
+ $this->username = $username;
+ $this->password = $password;
+ $this->scheme = $scheme;
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array('client.create_request' => array('onRequestCreate', 255));
+ }
+
+ /**
+ * Add basic auth
+ *
+ * @param Event $event
+ */
+ public function onRequestCreate(Event $event)
+ {
+ $event['request']->setAuth($this->username, $this->password, $this->scheme);
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json
new file mode 100644
index 0000000..edc8b24
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "guzzle/plugin-curlauth",
+ "description": "Guzzle cURL authorization plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "curl", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\CurlAuth": "" }
+ },
+ "target-dir": "Guzzle/Plugin/CurlAuth",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php
new file mode 100644
index 0000000..5dce8bd
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Guzzle\Plugin\ErrorResponse;
+
+use Guzzle\Service\Command\CommandInterface;
+use Guzzle\Http\Message\Response;
+
+/**
+ * Interface used to create an exception from an error response
+ */
+interface ErrorResponseExceptionInterface
+{
+ /**
+ * Create an exception for a command based on a command and an error response definition
+ *
+ * @param CommandInterface $command Command that was sent
+ * @param Response $response The error response
+ *
+ * @return self
+ */
+ public static function fromCommand(CommandInterface $command, Response $response);
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php
new file mode 100644
index 0000000..588b9c3
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Guzzle\Plugin\ErrorResponse;
+
+use Guzzle\Common\Event;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Service\Command\CommandInterface;
+use Guzzle\Service\Description\Operation;
+use Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Converts generic Guzzle response exceptions into errorResponse exceptions
+ */
+class ErrorResponsePlugin implements EventSubscriberInterface
+{
+ public static function getSubscribedEvents()
+ {
+ return array('command.before_send' => array('onCommandBeforeSend', -1));
+ }
+
+ /**
+ * Adds a listener to requests before they sent from a command
+ *
+ * @param Event $event Event emitted
+ */
+ public function onCommandBeforeSend(Event $event)
+ {
+ $command = $event['command'];
+ if ($operation = $command->getOperation()) {
+ if ($operation->getErrorResponses()) {
+ $request = $command->getRequest();
+ $request->getEventDispatcher()
+ ->addListener('request.complete', $this->getErrorClosure($request, $command, $operation));
+ }
+ }
+ }
+
+ /**
+ * @param RequestInterface $request Request that received an error
+ * @param CommandInterface $command Command that created the request
+ * @param Operation $operation Operation that defines the request and errors
+ *
+ * @return \Closure Returns a closure
+ * @throws ErrorResponseException
+ */
+ protected function getErrorClosure(RequestInterface $request, CommandInterface $command, Operation $operation)
+ {
+ return function (Event $event) use ($request, $command, $operation) {
+ $response = $event['response'];
+ foreach ($operation->getErrorResponses() as $error) {
+ if (!isset($error['class'])) {
+ continue;
+ }
+ if (isset($error['code']) && $response->getStatusCode() != $error['code']) {
+ continue;
+ }
+ if (isset($error['reason']) && $response->getReasonPhrase() != $error['reason']) {
+ continue;
+ }
+ $className = $error['class'];
+ $errorClassInterface = __NAMESPACE__ . '\\ErrorResponseExceptionInterface';
+ if (!class_exists($className)) {
+ throw new ErrorResponseException("{$className} does not exist");
+ } elseif (!(in_array($errorClassInterface, class_implements($className)))) {
+ throw new ErrorResponseException("{$className} must implement {$errorClassInterface}");
+ }
+ throw $className::fromCommand($command, $response);
+ }
+ };
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php
new file mode 100644
index 0000000..1d89e40
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Guzzle\Plugin\ErrorResponse\Exception;
+
+use Guzzle\Common\Exception\RuntimeException;
+
+class ErrorResponseException extends RuntimeException {}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/composer.json
new file mode 100644
index 0000000..607e861
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "guzzle/plugin-error-response",
+ "description": "Guzzle errorResponse plugin for creating error exceptions based on a service description",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/service": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\ErrorResponse": "" }
+ },
+ "target-dir": "Guzzle/Plugin/ErrorResponse",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php
new file mode 100644
index 0000000..7375e89
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace Guzzle\Plugin\History;
+
+use Guzzle\Common\Event;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\Response;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Maintains a list of requests and responses sent using a request or client
+ */
+class HistoryPlugin implements EventSubscriberInterface, \IteratorAggregate, \Countable
+{
+ /** @var int The maximum number of requests to maintain in the history */
+ protected $limit = 10;
+
+ /** @var array Requests and responses that have passed through the plugin */
+ protected $transactions = array();
+
+ public static function getSubscribedEvents()
+ {
+ return array('request.sent' => array('onRequestSent', 9999));
+ }
+
+ /**
+ * Convert to a string that contains all request and response headers
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $lines = array();
+ foreach ($this->transactions as $entry) {
+ $response = isset($entry['response']) ? $entry['response'] : '';
+ $lines[] = '> ' . trim($entry['request']) . "\n\n< " . trim($response) . "\n";
+ }
+
+ return implode("\n", $lines);
+ }
+
+ /**
+ * Add a request to the history
+ *
+ * @param RequestInterface $request Request to add
+ * @param Response $response Response of the request
+ *
+ * @return HistoryPlugin
+ */
+ public function add(RequestInterface $request, Response $response = null)
+ {
+ if (!$response && $request->getResponse()) {
+ $response = $request->getResponse();
+ }
+
+ $this->transactions[] = array('request' => $request, 'response' => $response);
+ if (count($this->transactions) > $this->getlimit()) {
+ array_shift($this->transactions);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set the max number of requests to store
+ *
+ * @param int $limit Limit
+ *
+ * @return HistoryPlugin
+ */
+ public function setLimit($limit)
+ {
+ $this->limit = (int) $limit;
+
+ return $this;
+ }
+
+ /**
+ * Get the request limit
+ *
+ * @return int
+ */
+ public function getLimit()
+ {
+ return $this->limit;
+ }
+
+ /**
+ * Get all of the raw transactions in the form of an array of associative arrays containing
+ * 'request' and 'response' keys.
+ *
+ * @return array
+ */
+ public function getAll()
+ {
+ return $this->transactions;
+ }
+
+ /**
+ * Get the requests in the history
+ *
+ * @return \ArrayIterator
+ */
+ public function getIterator()
+ {
+ // Return an iterator just like the old iteration of the HistoryPlugin for BC compatibility (use getAll())
+ return new \ArrayIterator(array_map(function ($entry) {
+ $entry['request']->getParams()->set('actual_response', $entry['response']);
+ return $entry['request'];
+ }, $this->transactions));
+ }
+
+ /**
+ * Get the number of requests in the history
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->transactions);
+ }
+
+ /**
+ * Get the last request sent
+ *
+ * @return RequestInterface
+ */
+ public function getLastRequest()
+ {
+ $last = end($this->transactions);
+
+ return $last['request'];
+ }
+
+ /**
+ * Get the last response in the history
+ *
+ * @return Response|null
+ */
+ public function getLastResponse()
+ {
+ $last = end($this->transactions);
+
+ return isset($last['response']) ? $last['response'] : null;
+ }
+
+ /**
+ * Clears the history
+ *
+ * @return HistoryPlugin
+ */
+ public function clear()
+ {
+ $this->transactions = array();
+
+ return $this;
+ }
+
+ public function onRequestSent(Event $event)
+ {
+ $this->add($event['request'], $event['response']);
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json
new file mode 100644
index 0000000..ba0bf2c
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "guzzle/plugin-history",
+ "description": "Guzzle history plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\History": "" }
+ },
+ "target-dir": "Guzzle/Plugin/History",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php
new file mode 100644
index 0000000..cabdea8
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php
@@ -0,0 +1,161 @@
+<?php
+
+namespace Guzzle\Plugin\Log;
+
+use Guzzle\Common\Event;
+use Guzzle\Log\LogAdapterInterface;
+use Guzzle\Log\MessageFormatter;
+use Guzzle\Log\ClosureLogAdapter;
+use Guzzle\Http\EntityBody;
+use Guzzle\Http\Message\EntityEnclosingRequestInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Plugin class that will add request and response logging to an HTTP request.
+ *
+ * The log plugin uses a message formatter that allows custom messages via template variable substitution.
+ *
+ * @see MessageLogger for a list of available log template variable substitutions
+ */
+class LogPlugin implements EventSubscriberInterface
+{
+ /** @var LogAdapterInterface Adapter responsible for writing log data */
+ protected $logAdapter;
+
+ /** @var MessageFormatter Formatter used to format messages before logging */
+ protected $formatter;
+
+ /** @var bool Whether or not to wire request and response bodies */
+ protected $wireBodies;
+
+ /**
+ * @param LogAdapterInterface $logAdapter Adapter object used to log message
+ * @param string|MessageFormatter $formatter Formatter used to format log messages or the formatter template
+ * @param bool $wireBodies Set to true to track request and response bodies using a temporary
+ * buffer if the bodies are not repeatable.
+ */
+ public function __construct(
+ LogAdapterInterface $logAdapter,
+ $formatter = null,
+ $wireBodies = false
+ ) {
+ $this->logAdapter = $logAdapter;
+ $this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter);
+ $this->wireBodies = $wireBodies;
+ }
+
+ /**
+ * Get a log plugin that outputs full request, response, and curl error information to stderr
+ *
+ * @param bool $wireBodies Set to false to disable request/response body output when they use are not repeatable
+ * @param resource $stream Stream to write to when logging. Defaults to STDERR when it is available
+ *
+ * @return self
+ */
+ public static function getDebugPlugin($wireBodies = true, $stream = null)
+ {
+ if ($stream === null) {
+ if (defined('STDERR')) {
+ $stream = STDERR;
+ } else {
+ $stream = fopen('php://output', 'w');
+ }
+ }
+
+ return new self(new ClosureLogAdapter(function ($m) use ($stream) {
+ fwrite($stream, $m . PHP_EOL);
+ }), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies);
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ 'curl.callback.write' => array('onCurlWrite', 255),
+ 'curl.callback.read' => array('onCurlRead', 255),
+ 'request.before_send' => array('onRequestBeforeSend', 255),
+ 'request.sent' => array('onRequestSent', 255)
+ );
+ }
+
+ /**
+ * Event triggered when curl data is read from a request
+ *
+ * @param Event $event
+ */
+ public function onCurlRead(Event $event)
+ {
+ // Stream the request body to the log if the body is not repeatable
+ if ($wire = $event['request']->getParams()->get('request_wire')) {
+ $wire->write($event['read']);
+ }
+ }
+
+ /**
+ * Event triggered when curl data is written to a response
+ *
+ * @param Event $event
+ */
+ public function onCurlWrite(Event $event)
+ {
+ // Stream the response body to the log if the body is not repeatable
+ if ($wire = $event['request']->getParams()->get('response_wire')) {
+ $wire->write($event['write']);
+ }
+ }
+
+ /**
+ * Called before a request is sent
+ *
+ * @param Event $event
+ */
+ public function onRequestBeforeSend(Event $event)
+ {
+ if ($this->wireBodies) {
+ $request = $event['request'];
+ // Ensure that curl IO events are emitted
+ $request->getCurlOptions()->set('emit_io', true);
+ // We need to make special handling for content wiring and non-repeatable streams.
+ if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
+ && (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable())
+ ) {
+ // The body of the request cannot be recalled so logging the body will require us to buffer it
+ $request->getParams()->set('request_wire', EntityBody::factory());
+ }
+ if (!$request->getResponseBody()->isRepeatable()) {
+ // The body of the response cannot be recalled so logging the body will require us to buffer it
+ $request->getParams()->set('response_wire', EntityBody::factory());
+ }
+ }
+ }
+
+ /**
+ * Triggers the actual log write when a request completes
+ *
+ * @param Event $event
+ */
+ public function onRequestSent(Event $event)
+ {
+ $request = $event['request'];
+ $response = $event['response'];
+ $handle = $event['handle'];
+
+ if ($wire = $request->getParams()->get('request_wire')) {
+ $request = clone $request;
+ $request->setBody($wire);
+ }
+
+ if ($wire = $request->getParams()->get('response_wire')) {
+ $response = clone $response;
+ $response->setBody($wire);
+ }
+
+ // Send the log message to the adapter, adding a category and host
+ $priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG;
+ $message = $this->formatter->format($request, $response, $handle);
+ $this->logAdapter->log($message, $priority, array(
+ 'request' => $request,
+ 'response' => $response,
+ 'handle' => $handle
+ ));
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json
new file mode 100644
index 0000000..130e6da
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json
@@ -0,0 +1,28 @@
+{
+ "name": "guzzle/plugin-log",
+ "description": "Guzzle log plugin for over the wire logging",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "log", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version",
+ "guzzle/log": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Log": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Log",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php
new file mode 100644
index 0000000..8512424
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Guzzle\Plugin\Md5;
+
+use Guzzle\Common\Event;
+use Guzzle\Http\Message\EntityEnclosingRequestInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Listener used to add a ContentMD5 header to the body of a command and adds ContentMD5 validation if the
+ * ValidateMD5 option is not set to false on a command
+ */
+class CommandContentMd5Plugin implements EventSubscriberInterface
+{
+ /** @var string Parameter used to check if the ContentMD5 value is being added */
+ protected $contentMd5Param;
+
+ /** @var string Parameter used to check if validation should occur on the response */
+ protected $validateMd5Param;
+
+ /**
+ * @param string $contentMd5Param Parameter used to check if the ContentMD5 value is being added
+ * @param string $validateMd5Param Parameter used to check if validation should occur on the response
+ */
+ public function __construct($contentMd5Param = 'ContentMD5', $validateMd5Param = 'ValidateMD5')
+ {
+ $this->contentMd5Param = $contentMd5Param;
+ $this->validateMd5Param = $validateMd5Param;
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array('command.before_send' => array('onCommandBeforeSend', -255));
+ }
+
+ public function onCommandBeforeSend(Event $event)
+ {
+ $command = $event['command'];
+ $request = $command->getRequest();
+
+ // Only add an MD5 is there is a MD5 option on the operation and it has a payload
+ if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
+ && $command->getOperation()->hasParam($this->contentMd5Param)) {
+ // Check if an MD5 checksum value should be passed along to the request
+ if ($command[$this->contentMd5Param] === true) {
+ if (false !== ($md5 = $request->getBody()->getContentMd5(true, true))) {
+ $request->setHeader('Content-MD5', $md5);
+ }
+ }
+ }
+
+ // Check if MD5 validation should be used with the response
+ if ($command[$this->validateMd5Param] === true) {
+ $request->addSubscriber(new Md5ValidatorPlugin(true, false));
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php
new file mode 100644
index 0000000..5d7a378
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace Guzzle\Plugin\Md5;
+
+use Guzzle\Common\Event;
+use Guzzle\Common\Exception\UnexpectedValueException;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Ensures that an the MD5 hash of an entity body matches the Content-MD5
+ * header (if set) of an HTTP response. An exception is thrown if the
+ * calculated MD5 does not match the expected MD5.
+ */
+class Md5ValidatorPlugin implements EventSubscriberInterface
+{
+ /** @var int Maximum Content-Length in bytes to validate */
+ protected $contentLengthCutoff;
+
+ /** @var bool Whether or not to compare when a Content-Encoding is present */
+ protected $contentEncoded;
+
+ /**
+ * @param bool $contentEncoded Calculating the MD5 hash of an entity body where a Content-Encoding was
+ * applied is a more expensive comparison because the entity body will need to
+ * be compressed in order to get the correct hash. Set to FALSE to not
+ * validate the MD5 hash of an entity body with an applied Content-Encoding.
+ * @param bool|int $contentLengthCutoff Maximum Content-Length (bytes) in which a MD5 hash will be validated. Any
+ * response with a Content-Length greater than this value will not be validated
+ * because it will be deemed too memory intensive.
+ */
+ public function __construct($contentEncoded = true, $contentLengthCutoff = false)
+ {
+ $this->contentLengthCutoff = $contentLengthCutoff;
+ $this->contentEncoded = $contentEncoded;
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array('request.complete' => array('onRequestComplete', 255));
+ }
+
+ /**
+ * {@inheritdoc}
+ * @throws UnexpectedValueException
+ */
+ public function onRequestComplete(Event $event)
+ {
+ $response = $event['response'];
+
+ if (!$contentMd5 = $response->getContentMd5()) {
+ return;
+ }
+
+ $contentEncoding = $response->getContentEncoding();
+ if ($contentEncoding && !$this->contentEncoded) {
+ return false;
+ }
+
+ // Make sure that the size of the request is under the cutoff size
+ if ($this->contentLengthCutoff) {
+ $size = $response->getContentLength() ?: $response->getBody()->getSize();
+ if (!$size || $size > $this->contentLengthCutoff) {
+ return;
+ }
+ }
+
+ if (!$contentEncoding) {
+ $hash = $response->getBody()->getContentMd5();
+ } elseif ($contentEncoding == 'gzip') {
+ $response->getBody()->compress('zlib.deflate');
+ $hash = $response->getBody()->getContentMd5();
+ $response->getBody()->uncompress();
+ } elseif ($contentEncoding == 'compress') {
+ $response->getBody()->compress('bzip2.compress');
+ $hash = $response->getBody()->getContentMd5();
+ $response->getBody()->uncompress();
+ } else {
+ return;
+ }
+
+ if ($contentMd5 !== $hash) {
+ throw new UnexpectedValueException(
+ "The response entity body may have been modified over the wire. The Content-MD5 "
+ . "received ({$contentMd5}) did not match the calculated MD5 hash ({$hash})."
+ );
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json
new file mode 100644
index 0000000..0602d06
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "guzzle/plugin-md5",
+ "description": "Guzzle MD5 plugins",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Md5": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Md5",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php
new file mode 100644
index 0000000..2440578
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php
@@ -0,0 +1,245 @@
+<?php
+
+namespace Guzzle\Plugin\Mock;
+
+use Guzzle\Common\Event;
+use Guzzle\Common\Exception\InvalidArgumentException;
+use Guzzle\Common\AbstractHasDispatcher;
+use Guzzle\Http\Exception\CurlException;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\EntityEnclosingRequestInterface;
+use Guzzle\Http\Message\Response;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Queues mock responses or exceptions and delivers mock responses or exceptions in a fifo order.
+ */
+class MockPlugin extends AbstractHasDispatcher implements EventSubscriberInterface, \Countable
+{
+ /** @var array Array of mock responses / exceptions */
+ protected $queue = array();
+
+ /** @var bool Whether or not to remove the plugin when the queue is empty */
+ protected $temporary = false;
+
+ /** @var array Array of requests that were mocked */
+ protected $received = array();
+
+ /** @var bool Whether or not to consume an entity body when a mock response is served */
+ protected $readBodies;
+
+ /**
+ * @param array $items Array of responses or exceptions to queue
+ * @param bool $temporary Set to TRUE to remove the plugin when the queue is empty
+ * @param bool $readBodies Set to TRUE to consume the entity body when a mock is served
+ */
+ public function __construct(array $items = null, $temporary = false, $readBodies = false)
+ {
+ $this->readBodies = $readBodies;
+ $this->temporary = $temporary;
+ if ($items) {
+ foreach ($items as $item) {
+ if ($item instanceof \Exception) {
+ $this->addException($item);
+ } else {
+ $this->addResponse($item);
+ }
+ }
+ }
+ }
+
+ public static function getSubscribedEvents()
+ {
+ // Use a number lower than the CachePlugin
+ return array('request.before_send' => array('onRequestBeforeSend', -999));
+ }
+
+ public static function getAllEvents()
+ {
+ return array('mock.request');
+ }
+
+ /**
+ * Get a mock response from a file
+ *
+ * @param string $path File to retrieve a mock response from
+ *
+ * @return Response
+ * @throws InvalidArgumentException if the file is not found
+ */
+ public static function getMockFile($path)
+ {
+ if (!file_exists($path)) {
+ throw new InvalidArgumentException('Unable to open mock file: ' . $path);
+ }
+
+ return Response::fromMessage(file_get_contents($path));
+ }
+
+ /**
+ * Set whether or not to consume the entity body of a request when a mock
+ * response is used
+ *
+ * @param bool $readBodies Set to true to read and consume entity bodies
+ *
+ * @return self
+ */
+ public function readBodies($readBodies)
+ {
+ $this->readBodies = $readBodies;
+
+ return $this;
+ }
+
+ /**
+ * Returns the number of remaining mock responses
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->queue);
+ }
+
+ /**
+ * Add a response to the end of the queue
+ *
+ * @param string|Response $response Response object or path to response file
+ *
+ * @return MockPlugin
+ * @throws InvalidArgumentException if a string or Response is not passed
+ */
+ public function addResponse($response)
+ {
+ if (!($response instanceof Response)) {
+ if (!is_string($response)) {
+ throw new InvalidArgumentException('Invalid response');
+ }
+ $response = self::getMockFile($response);
+ }
+
+ $this->queue[] = $response;
+
+ return $this;
+ }
+
+ /**
+ * Add an exception to the end of the queue
+ *
+ * @param CurlException $e Exception to throw when the request is executed
+ *
+ * @return MockPlugin
+ */
+ public function addException(CurlException $e)
+ {
+ $this->queue[] = $e;
+
+ return $this;
+ }
+
+ /**
+ * Clear the queue
+ *
+ * @return MockPlugin
+ */
+ public function clearQueue()
+ {
+ $this->queue = array();
+
+ return $this;
+ }
+
+ /**
+ * Returns an array of mock responses remaining in the queue
+ *
+ * @return array
+ */
+ public function getQueue()
+ {
+ return $this->queue;
+ }
+
+ /**
+ * Check if this is a temporary plugin
+ *
+ * @return bool
+ */
+ public function isTemporary()
+ {
+ return $this->temporary;
+ }
+
+ /**
+ * Get a response from the front of the list and add it to a request
+ *
+ * @param RequestInterface $request Request to mock
+ *
+ * @return self
+ * @throws CurlException When request.send is called and an exception is queued
+ */
+ public function dequeue(RequestInterface $request)
+ {
+ $this->dispatch('mock.request', array('plugin' => $this, 'request' => $request));
+
+ $item = array_shift($this->queue);
+ if ($item instanceof Response) {
+ if ($this->readBodies && $request instanceof EntityEnclosingRequestInterface) {
+ $request->getEventDispatcher()->addListener('request.sent', $f = function (Event $event) use (&$f) {
+ while ($data = $event['request']->getBody()->read(8096));
+ // Remove the listener after one-time use
+ $event['request']->getEventDispatcher()->removeListener('request.sent', $f);
+ });
+ }
+ $request->setResponse($item);
+ } elseif ($item instanceof CurlException) {
+ // Emulates exceptions encountered while transferring requests
+ $item->setRequest($request);
+ $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $item));
+ // Only throw if the exception wasn't handled
+ if ($state == RequestInterface::STATE_ERROR) {
+ throw $item;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Clear the array of received requests
+ */
+ public function flush()
+ {
+ $this->received = array();
+ }
+
+ /**
+ * Get an array of requests that were mocked by this plugin
+ *
+ * @return array
+ */
+ public function getReceivedRequests()
+ {
+ return $this->received;
+ }
+
+ /**
+ * Called when a request is about to be sent
+ *
+ * @param Event $event
+ * @throws \OutOfBoundsException When queue is empty
+ */
+ public function onRequestBeforeSend(Event $event)
+ {
+ if (!$this->queue) {
+ throw new \OutOfBoundsException('Mock queue is empty');
+ }
+
+ $request = $event['request'];
+ $this->received[] = $request;
+ // Detach the filter from the client so it's a one-time use
+ if ($this->temporary && count($this->queue) == 1 && $request->getClient()) {
+ $request->getClient()->getEventDispatcher()->removeSubscriber($this);
+ }
+ $this->dequeue($request);
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json
new file mode 100644
index 0000000..f8201e3
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "guzzle/plugin-mock",
+ "description": "Guzzle Mock plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["mock", "plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Mock": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Mock",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php
new file mode 100644
index 0000000..95e0c3e
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php
@@ -0,0 +1,306 @@
+<?php
+
+namespace Guzzle\Plugin\Oauth;
+
+use Guzzle\Common\Event;
+use Guzzle\Common\Collection;
+use Guzzle\Http\Message\RequestInterface;
+use Guzzle\Http\Message\EntityEnclosingRequestInterface;
+use Guzzle\Http\QueryString;
+use Guzzle\Http\Url;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * OAuth signing plugin
+ * @link http://oauth.net/core/1.0/#rfc.section.9.1.1
+ */
+class OauthPlugin implements EventSubscriberInterface
+{
+ /**
+ * Consumer request method constants. See http://oauth.net/core/1.0/#consumer_req_param
+ */
+ const REQUEST_METHOD_HEADER = 'header';
+ const REQUEST_METHOD_QUERY = 'query';
+
+ /** @var Collection Configuration settings */
+ protected $config;
+
+ /**
+ * Create a new OAuth 1.0 plugin
+ *
+ * @param array $config Configuration array containing these parameters:
+ * - string 'request_method' Consumer request method. Use the class constants.
+ * - string 'callback' OAuth callback
+ * - string 'consumer_key' Consumer key
+ * - string 'consumer_secret' Consumer secret
+ * - string 'token' Token
+ * - string 'token_secret' Token secret
+ * - string 'verifier' OAuth verifier.
+ * - string 'version' OAuth version. Defaults to 1.0
+ * - string 'signature_method' Custom signature method
+ * - bool 'disable_post_params' Set to true to prevent POST parameters from being signed
+ * - array|Closure 'signature_callback' Custom signature callback that accepts a string to sign and a signing key
+ */
+ public function __construct($config)
+ {
+ $this->config = Collection::fromConfig($config, array(
+ 'version' => '1.0',
+ 'request_method' => self::REQUEST_METHOD_HEADER,
+ 'consumer_key' => 'anonymous',
+ 'consumer_secret' => 'anonymous',
+ 'signature_method' => 'HMAC-SHA1',
+ 'signature_callback' => function($stringToSign, $key) {
+ return hash_hmac('sha1', $stringToSign, $key, true);
+ }
+ ), array(
+ 'signature_method', 'signature_callback', 'version',
+ 'consumer_key', 'consumer_secret'
+ ));
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ 'request.before_send' => array('onRequestBeforeSend', -1000)
+ );
+ }
+
+ /**
+ * Request before-send event handler
+ *
+ * @param Event $event Event received
+ * @return array
+ * @throws \InvalidArgumentException
+ */
+ public function onRequestBeforeSend(Event $event)
+ {
+ $timestamp = $this->getTimestamp($event);
+ $request = $event['request'];
+ $nonce = $this->generateNonce($request);
+ $authorizationParams = $this->getOauthParams($timestamp, $nonce);
+ $authorizationParams['oauth_signature'] = $this->getSignature($request, $timestamp, $nonce);
+
+ switch ($this->config['request_method']) {
+ case self::REQUEST_METHOD_HEADER:
+ $request->setHeader(
+ 'Authorization',
+ $this->buildAuthorizationHeader($authorizationParams)
+ );
+ break;
+ case self::REQUEST_METHOD_QUERY:
+ foreach ($authorizationParams as $key => $value) {
+ $request->getQuery()->set($key, $value);
+ }
+ break;
+ default:
+ throw new \InvalidArgumentException(sprintf(
+ 'Invalid consumer method "%s"',
+ $this->config['request_method']
+ ));
+ }
+
+ return $authorizationParams;
+ }
+
+ /**
+ * Builds the Authorization header for a request
+ *
+ * @param array $authorizationParams Associative array of authorization parameters
+ *
+ * @return string
+ */
+ private function buildAuthorizationHeader($authorizationParams)
+ {
+ $authorizationString = 'OAuth ';
+ foreach ($authorizationParams as $key => $val) {
+ if ($val) {
+ $authorizationString .= $key . '="' . urlencode($val) . '", ';
+ }
+ }
+
+ return substr($authorizationString, 0, -2);
+ }
+
+ /**
+ * Calculate signature for request
+ *
+ * @param RequestInterface $request Request to generate a signature for
+ * @param integer $timestamp Timestamp to use for nonce
+ * @param string $nonce
+ *
+ * @return string
+ */
+ public function getSignature(RequestInterface $request, $timestamp, $nonce)
+ {
+ $string = $this->getStringToSign($request, $timestamp, $nonce);
+ $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']);
+
+ return base64_encode(call_user_func($this->config['signature_callback'], $string, $key));
+ }
+
+ /**
+ * Calculate string to sign
+ *
+ * @param RequestInterface $request Request to generate a signature for
+ * @param int $timestamp Timestamp to use for nonce
+ * @param string $nonce
+ *
+ * @return string
+ */
+ public function getStringToSign(RequestInterface $request, $timestamp, $nonce)
+ {
+ $params = $this->getParamsToSign($request, $timestamp, $nonce);
+
+ // Convert booleans to strings.
+ $params = $this->prepareParameters($params);
+
+ // Build signing string from combined params
+ $parameterString = clone $request->getQuery();
+ $parameterString->replace($params);
+
+ $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null);
+
+ return strtoupper($request->getMethod()) . '&'
+ . rawurlencode($url) . '&'
+ . rawurlencode((string) $parameterString);
+ }
+
+ /**
+ * Get the oauth parameters as named by the oauth spec
+ *
+ * @param $timestamp
+ * @param $nonce
+ * @return Collection
+ */
+ protected function getOauthParams($timestamp, $nonce)
+ {
+ $params = new Collection(array(
+ 'oauth_consumer_key' => $this->config['consumer_key'],
+ 'oauth_nonce' => $nonce,
+ 'oauth_signature_method' => $this->config['signature_method'],
+ 'oauth_timestamp' => $timestamp,
+ ));
+
+ // Optional parameters should not be set if they have not been set in the config as
+ // the parameter may be considered invalid by the Oauth service.
+ $optionalParams = array(
+ 'callback' => 'oauth_callback',
+ 'token' => 'oauth_token',
+ 'verifier' => 'oauth_verifier',
+ 'version' => 'oauth_version'
+ );
+
+ foreach ($optionalParams as $optionName => $oauthName) {
+ if (isset($this->config[$optionName]) == true) {
+ $params[$oauthName] = $this->config[$optionName];
+ }
+ }
+
+ return $params;
+ }
+
+ /**
+ * Get all of the parameters required to sign a request including:
+ * * The oauth params
+ * * The request GET params
+ * * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded)
+ *
+ * @param RequestInterface $request Request to generate a signature for
+ * @param integer $timestamp Timestamp to use for nonce
+ * @param string $nonce
+ *
+ * @return array
+ */
+ public function getParamsToSign(RequestInterface $request, $timestamp, $nonce)
+ {
+ $params = $this->getOauthParams($timestamp, $nonce);
+
+ // Add query string parameters
+ $params->merge($request->getQuery());
+
+ // Add POST fields to signing string if required
+ if ($this->shouldPostFieldsBeSigned($request))
+ {
+ $params->merge($request->getPostFields());
+ }
+
+ // Sort params
+ $params = $params->toArray();
+ uksort($params, 'strcmp');
+
+ return $params;
+ }
+
+ /**
+ * Decide whether the post fields should be added to the base string that Oauth signs.
+ * This implementation is correct. Non-conformant APIs may require that this method be
+ * overwritten e.g. the Flickr API incorrectly adds the post fields when the Content-Type
+ * is 'application/x-www-form-urlencoded'
+ *
+ * @param $request
+ * @return bool Whether the post fields should be signed or not
+ */
+ public function shouldPostFieldsBeSigned($request)
+ {
+ if (!$this->config->get('disable_post_params') &&
+ $request instanceof EntityEnclosingRequestInterface &&
+ false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded'))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a Nonce Based on the unique id and URL. This will allow for multiple requests in parallel with the same
+ * exact timestamp to use separate nonce's.
+ *
+ * @param RequestInterface $request Request to generate a nonce for
+ *
+ * @return string
+ */
+ public function generateNonce(RequestInterface $request)
+ {
+ return sha1(uniqid('', true) . $request->getUrl());
+ }
+
+ /**
+ * Gets timestamp from event or create new timestamp
+ *
+ * @param Event $event Event containing contextual information
+ *
+ * @return int
+ */
+ public function getTimestamp(Event $event)
+ {
+ return $event['timestamp'] ?: time();
+ }
+
+ /**
+ * Convert booleans to strings, removed unset parameters, and sorts the array
+ *
+ * @param array $data Data array
+ *
+ * @return array
+ */
+ protected function prepareParameters($data)
+ {
+ ksort($data);
+ foreach ($data as $key => &$value) {
+ switch (gettype($value)) {
+ case 'NULL':
+ unset($data[$key]);
+ break;
+ case 'array':
+ $data[$key] = self::prepareParameters($value);
+ break;
+ case 'boolean':
+ $data[$key] = $value ? 'true' : 'false';
+ break;
+ }
+ }
+
+ return $data;
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json
new file mode 100644
index 0000000..c9766ba
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "guzzle/plugin-oauth",
+ "description": "Guzzle OAuth plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["oauth", "plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Oauth": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Oauth",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
diff --git a/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json b/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json
new file mode 100644
index 0000000..2bbe64c
--- /dev/null
+++ b/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json
@@ -0,0 +1,44 @@
+{
+ "name": "guzzle/plugin",
+ "description": "Guzzle plugin component containing all Guzzle HTTP plugins",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["http", "client", "plugin", "extension", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "suggest": {
+ "guzzle/cache": "self.version",
+ "guzzle/log": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin": "" }
+ },
+ "target-dir": "Guzzle/Plugin",
+ "replace": {
+ "guzzle/plugin-async": "self.version",
+ "guzzle/plugin-backoff": "self.version",
+ "guzzle/plugin-cache": "self.version",
+ "guzzle/plugin-cookie": "self.version",
+ "guzzle/plugin-curlauth": "self.version",
+ "guzzle/plugin-error-response": "self.version",
+ "guzzle/plugin-history": "self.version",
+ "guzzle/plugin-log": "self.version",
+ "guzzle/plugin-md5": "self.version",
+ "guzzle/plugin-mock": "self.version",
+ "guzzle/plugin-oauth": "self.version"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}