mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
Update AWS sdk to 2.6.15
This commit is contained in:
parent
cb0da1178b
commit
dadb1fad2a
263 changed files with 5931 additions and 9805 deletions
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
namespace Aws\Common;
|
||||
|
||||
use Aws\Common\Facade\Facade;
|
||||
use Guzzle\Service\Builder\ServiceBuilder;
|
||||
use Guzzle\Service\Builder\ServiceBuilderLoader;
|
||||
|
||||
|
|
@ -27,7 +28,7 @@ class Aws extends ServiceBuilder
|
|||
/**
|
||||
* @var string Current version of the SDK
|
||||
*/
|
||||
const VERSION = '2.4.0';
|
||||
const VERSION = '2.6.15';
|
||||
|
||||
/**
|
||||
* Create a new service locator for the AWS SDK
|
||||
|
|
@ -97,10 +98,7 @@ class Aws extends ServiceBuilder
|
|||
*/
|
||||
public function enableFacades($namespace = null)
|
||||
{
|
||||
$facadeClass = 'Aws\\Common\\Facade\\Facade';
|
||||
if (class_exists($facadeClass)) {
|
||||
$facadeClass::mountFacades($this, $namespace);
|
||||
}
|
||||
Facade::mountFacades($this, $namespace);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ namespace Aws\Common\Client;
|
|||
use Aws\Common\Aws;
|
||||
use Aws\Common\Credentials\Credentials;
|
||||
use Aws\Common\Credentials\CredentialsInterface;
|
||||
use Aws\Common\Credentials\NullCredentials;
|
||||
use Aws\Common\Enum\ClientOptions as Options;
|
||||
use Aws\Common\Exception\InvalidArgumentException;
|
||||
use Aws\Common\Exception\TransferException;
|
||||
use Aws\Common\Signature\EndpointSignatureInterface;
|
||||
use Aws\Common\Signature\SignatureInterface;
|
||||
use Aws\Common\Signature\SignatureListener;
|
||||
|
|
@ -29,6 +31,7 @@ use Aws\Common\Waiter\CompositeWaiterFactory;
|
|||
use Aws\Common\Waiter\WaiterFactoryInterface;
|
||||
use Aws\Common\Waiter\WaiterConfigFactory;
|
||||
use Guzzle\Common\Collection;
|
||||
use Guzzle\Http\Exception\CurlException;
|
||||
use Guzzle\Service\Client;
|
||||
use Guzzle\Service\Description\ServiceDescriptionInterface;
|
||||
|
||||
|
|
@ -82,7 +85,9 @@ abstract class AbstractClient extends Client implements AwsClientInterface
|
|||
|
||||
// Add the event listener so that requests are signed before they are sent
|
||||
$dispatcher = $this->getEventDispatcher();
|
||||
$dispatcher->addSubscriber(new SignatureListener($credentials, $signature));
|
||||
if (!$credentials instanceof NullCredentials) {
|
||||
$dispatcher->addSubscriber(new SignatureListener($credentials, $signature));
|
||||
}
|
||||
|
||||
if ($backoff = $config->get(Options::BACKOFF)) {
|
||||
$dispatcher->addSubscriber($backoff, -255);
|
||||
|
|
@ -133,17 +138,11 @@ abstract class AbstractClient extends Client implements AwsClientInterface
|
|||
return $scheme . '://' . $regions[$region]['hostname'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCredentials()
|
||||
{
|
||||
return $this->credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCredentials(CredentialsInterface $credentials)
|
||||
{
|
||||
$formerCredentials = $this->credentials;
|
||||
|
|
@ -158,33 +157,21 @@ abstract class AbstractClient extends Client implements AwsClientInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSignature()
|
||||
{
|
||||
return $this->signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRegions()
|
||||
{
|
||||
return $this->serviceDescription->getData('regions');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRegion()
|
||||
{
|
||||
return $this->getConfig(Options::REGION);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRegion($region)
|
||||
{
|
||||
$config = $this->getConfig();
|
||||
|
|
@ -214,9 +201,6 @@ abstract class AbstractClient extends Client implements AwsClientInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function waitUntil($waiter, array $input = array())
|
||||
{
|
||||
$this->getWaiter($waiter, $input)->wait();
|
||||
|
|
@ -224,9 +208,6 @@ abstract class AbstractClient extends Client implements AwsClientInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWaiter($waiter, array $input = array())
|
||||
{
|
||||
return $this->getWaiterFactory()->build($waiter)
|
||||
|
|
@ -234,9 +215,6 @@ abstract class AbstractClient extends Client implements AwsClientInterface
|
|||
->setConfig($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setWaiterFactory(WaiterFactoryInterface $waiterFactory)
|
||||
{
|
||||
$this->waiterFactory = $waiterFactory;
|
||||
|
|
@ -244,9 +222,6 @@ abstract class AbstractClient extends Client implements AwsClientInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWaiterFactory()
|
||||
{
|
||||
if (!$this->waiterFactory) {
|
||||
|
|
@ -256,18 +231,34 @@ abstract class AbstractClient extends Client implements AwsClientInterface
|
|||
new WaiterClassFactory(substr($clientClass, 0, strrpos($clientClass, '\\')) . '\\Waiter')
|
||||
));
|
||||
if ($this->getDescription()) {
|
||||
$this->waiterFactory->addFactory(new WaiterConfigFactory($this->getDescription()->getData('waiters')));
|
||||
$waiterConfig = $this->getDescription()->getData('waiters') ?: array();
|
||||
$this->waiterFactory->addFactory(new WaiterConfigFactory($waiterConfig));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->waiterFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getApiVersion()
|
||||
{
|
||||
return $this->serviceDescription->getApiVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws \Aws\Common\Exception\TransferException
|
||||
*/
|
||||
public function send($requests)
|
||||
{
|
||||
try {
|
||||
return parent::send($requests);
|
||||
} catch (CurlException $e) {
|
||||
$wrapped = new TransferException($e->getMessage(), null, $e);
|
||||
$wrapped->setCurlHandle($e->getCurlHandle())
|
||||
->setCurlInfo($e->getCurlInfo())
|
||||
->setError($e->getError(), $e->getErrorNo())
|
||||
->setRequest($e->getRequest());
|
||||
throw $wrapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
namespace Aws\Common\Client;
|
||||
|
||||
use Aws\Common\Credentials\Credentials;
|
||||
use Aws\Common\Credentials\CredentialsInterface;
|
||||
use Aws\Common\Credentials\NullCredentials;
|
||||
use Aws\Common\Enum\ClientOptions as Options;
|
||||
use Aws\Common\Enum\Region;
|
||||
use Aws\Common\Exception\ExceptionListener;
|
||||
|
|
@ -28,7 +30,6 @@ use Aws\Common\Iterator\AwsResourceIteratorFactory;
|
|||
use Aws\Common\Signature\EndpointSignatureInterface;
|
||||
use Aws\Common\Signature\SignatureInterface;
|
||||
use Aws\Common\Signature\SignatureV2;
|
||||
use Aws\Common\Signature\SignatureV3;
|
||||
use Aws\Common\Signature\SignatureV3Https;
|
||||
use Aws\Common\Signature\SignatureV4;
|
||||
use Guzzle\Common\Collection;
|
||||
|
|
@ -199,14 +200,10 @@ class ClientBuilder
|
|||
(self::$commonConfigRequirements + $this->configRequirements)
|
||||
);
|
||||
|
||||
// Resolve endpoint and signature from the config and service description
|
||||
// Resolve the endpoint, signature, and credentials
|
||||
$description = $this->updateConfigFromDescription($config);
|
||||
$signature = $this->getSignature($description, $config);
|
||||
|
||||
// Resolve credentials
|
||||
if (!$credentials = $config->get('credentials')) {
|
||||
$credentials = Credentials::factory($config);
|
||||
}
|
||||
$credentials = $this->getCredentials($config);
|
||||
|
||||
// Resolve exception parser
|
||||
if (!$this->exceptionParser) {
|
||||
|
|
@ -221,10 +218,10 @@ class ClientBuilder
|
|||
new TruncatedBackoffStrategy(3,
|
||||
// Retry failed requests with 400-level responses due to throttling
|
||||
new ThrottlingErrorChecker($this->exceptionParser,
|
||||
// Retry failed requests with 500-level responses
|
||||
new HttpBackoffStrategy(array(500, 503, 509),
|
||||
// Retry failed requests due to transient network or cURL problems
|
||||
new CurlBackoffStrategy(null,
|
||||
// Retry failed requests due to transient network or cURL problems
|
||||
new CurlBackoffStrategy(null,
|
||||
// Retry failed requests with 500-level responses
|
||||
new HttpBackoffStrategy(array(500, 503, 509),
|
||||
// Retry requests that failed due to expired credentials
|
||||
new ExpiredCredentialsChecker($this->exceptionParser,
|
||||
new ExponentialBackoffStrategy()
|
||||
|
|
@ -402,7 +399,10 @@ class ClientBuilder
|
|||
}
|
||||
|
||||
/**
|
||||
* Return an appropriate signature object for a a client based on a description
|
||||
* Return an appropriate signature object for a a client based on the
|
||||
* "signature" configuration setting, or the default signature specified in
|
||||
* a service description. The signature can be set to a valid signature
|
||||
* version identifier string or an instance of Aws\Common\Signature\SignatureInterface.
|
||||
*
|
||||
* @param ServiceDescription $description Description that holds a signature option
|
||||
* @param Collection $config Configuration options
|
||||
|
|
@ -412,43 +412,50 @@ class ClientBuilder
|
|||
*/
|
||||
protected function getSignature(ServiceDescription $description, Collection $config)
|
||||
{
|
||||
if (!$signature = $config->get(Options::SIGNATURE)) {
|
||||
switch ($description->getData('signatureVersion')) {
|
||||
case 'v2':
|
||||
$signature = new SignatureV2();
|
||||
break;
|
||||
case 'v3':
|
||||
$signature = new SignatureV3();
|
||||
break;
|
||||
case 'v3https':
|
||||
$signature = new SignatureV3Https();
|
||||
break;
|
||||
case 'v4':
|
||||
$signature = new SignatureV4();
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException('Service description does not specify a valid signatureVersion');
|
||||
// If a custom signature has not been provided, then use the default
|
||||
// signature setting specified in the service description.
|
||||
$signature = $config->get(Options::SIGNATURE) ?: $description->getData('signatureVersion');
|
||||
|
||||
if (is_string($signature)) {
|
||||
if ($signature == 'v4') {
|
||||
$signature = new SignatureV4();
|
||||
} elseif ($signature == 'v2') {
|
||||
$signature = new SignatureV2();
|
||||
} elseif ($signature == 'v3https') {
|
||||
$signature = new SignatureV3Https();
|
||||
} else {
|
||||
throw new InvalidArgumentException("Invalid signature type: {$signature}");
|
||||
}
|
||||
} elseif (!($signature instanceof SignatureInterface)) {
|
||||
throw new InvalidArgumentException('The provided signature is not '
|
||||
. 'a signature version string or an instance of '
|
||||
. 'Aws\\Common\\Signature\\SignatureInterface');
|
||||
}
|
||||
|
||||
// Allow a custom service name or region value to be provided
|
||||
if ($signature instanceof EndpointSignatureInterface) {
|
||||
|
||||
// Determine the service name to use when signing
|
||||
if (!$service = $config->get(Options::SIGNATURE_SERVICE)) {
|
||||
if (!$service = $description->getData('signingName')) {
|
||||
$service = $description->getData('endpointPrefix');
|
||||
}
|
||||
}
|
||||
$signature->setServiceName($service);
|
||||
$signature->setServiceName($config->get(Options::SIGNATURE_SERVICE)
|
||||
?: $description->getData('signingName')
|
||||
?: $description->getData('endpointPrefix'));
|
||||
|
||||
// Determine the region to use when signing requests
|
||||
if (!$region = $config->get(Options::SIGNATURE_REGION)) {
|
||||
$region = $config->get(Options::REGION);
|
||||
}
|
||||
$signature->setRegionName($region);
|
||||
$signature->setRegionName($config->get(Options::SIGNATURE_REGION) ?: $config->get(Options::REGION));
|
||||
}
|
||||
|
||||
return $signature;
|
||||
}
|
||||
|
||||
protected function getCredentials(Collection $config)
|
||||
{
|
||||
$credentials = $config->get(Options::CREDENTIALS);
|
||||
if ($credentials === false) {
|
||||
$credentials = new NullCredentials();
|
||||
} elseif (!$credentials instanceof CredentialsInterface) {
|
||||
$credentials = Credentials::factory($config);
|
||||
}
|
||||
|
||||
return $credentials;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,38 +29,29 @@ class DefaultClient extends AbstractClient
|
|||
*
|
||||
* The following array keys and values are available options:
|
||||
*
|
||||
* - Credential options (`key`, `secret`, and optional `token` OR `credentials` is required)
|
||||
* - key: AWS Access Key ID
|
||||
* - secret: AWS secret access key
|
||||
* - credentials: You can optionally provide a custom `Aws\Common\Credentials\CredentialsInterface` object
|
||||
* - token: Custom AWS security token to use with request authentication
|
||||
* - token.ttd: UNIX timestamp for when the custom credentials expire
|
||||
* - credentials.cache: Used to cache credentials when using providers that require HTTP requests. Set the true
|
||||
* to use the default APC cache or provide a `Guzzle\Cache\CacheAdapterInterface` object.
|
||||
* - credentials.cache.key: Optional custom cache key to use with the credentials
|
||||
* - credentials.client: Pass this option to specify a custom `Guzzle\Http\ClientInterface` to use if your
|
||||
* credentials require a HTTP request (e.g. RefreshableInstanceProfileCredentials)
|
||||
* - Region and Endpoint options (a `region` and optional `scheme` OR a `base_url` is required)
|
||||
* - region: Region name (e.g. 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1', etc...)
|
||||
* - scheme: URI Scheme of the base URL (e.g. 'https', 'http').
|
||||
* - service: Specify the name of the service
|
||||
* - base_url: Instead of using a `region` and `scheme`, you can specify a custom base URL for the client
|
||||
* - Signature options
|
||||
* - signature: You can optionally provide a custom signature implementation used to sign requests
|
||||
* - signature.service: Set to explicitly override the service name used in signatures
|
||||
* - signature.region: Set to explicitly override the region name used in signatures
|
||||
* - Exponential backoff options
|
||||
* - client.backoff.logger: `Guzzle\Log\LogAdapterInterface` object used to log backoff retries. Use
|
||||
* 'debug' to emit PHP warnings when a retry is issued.
|
||||
* - client.backoff.logger.template: Optional template to use for exponential backoff log messages. See
|
||||
* `Guzzle\Plugin\Backoff\BackoffLogger` for formatting information.
|
||||
* - Generic client options
|
||||
* - ssl.certificate_authority: Set to true to use the bundled CA cert (default), system to use the certificate
|
||||
* bundled with your system, or pass the full path to an SSL certificate bundle. This option should be used
|
||||
* when you encounter curl error code 60.
|
||||
* - curl.CURLOPT_VERBOSE: Set to true to output curl debug information during transfers
|
||||
* - curl.*: Prefix any available cURL option with `curl.` to add cURL options to each request.
|
||||
* See: http://www.php.net/manual/en/function.curl-setopt.php
|
||||
* Credential options ((`key`, `secret`, and optional `token`) OR `credentials` is required):
|
||||
*
|
||||
* - key: AWS Access Key ID
|
||||
* - secret: AWS secret access key
|
||||
* - credentials: You can optionally provide a custom `Aws\Common\Credentials\CredentialsInterface` object
|
||||
* - token: Custom AWS security token to use with request authentication. Please note that not all services accept temporary credentials. See http://docs.aws.amazon.com/STS/latest/UsingSTS/UsingTokens.html
|
||||
* - token.ttd: UNIX timestamp for when the custom credentials expire
|
||||
* - credentials.cache.key: Optional custom cache key to use with the credentials
|
||||
* - credentials.client: Pass this option to specify a custom `Guzzle\Http\ClientInterface` to use if your credentials require a HTTP request (e.g. RefreshableInstanceProfileCredentials)
|
||||
*
|
||||
* Region and endpoint options (Some services do not require a region while others do. Check the service specific user guide documentation for details):
|
||||
*
|
||||
* - region: Region name (e.g. 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1', etc...)
|
||||
* - scheme: URI Scheme of the base URL (e.g. 'https', 'http') used when base_url is not supplied
|
||||
* - base_url: Allows you to specify a custom endpoint instead of building one from the region and scheme
|
||||
*
|
||||
* Generic client options:
|
||||
*
|
||||
* - signature: Overrides the signature used by the client. Clients will always choose an appropriate default signature. However, it can be useful to override this with a custom setting. This can be set to "v4", "v3https", "v2" or an instance of Aws\Common\Signature\SignatureInterface.
|
||||
* - ssl.certificate_authority: Set to true to use the bundled CA cert or pass the full path to an SSL certificate bundle
|
||||
* - curl.options: Associative of CURLOPT_* cURL options to add to each request
|
||||
* - client.backoff.logger: `Guzzle\Log\LogAdapterInterface` object used to log backoff retries. Use 'debug' to emit PHP warnings when a retry is issued.
|
||||
* - client.backoff.logger.template: Optional template to use for exponential backoff log messages. See `Guzzle\Plugin\Backoff\BackoffLogger` for formatting information.
|
||||
*
|
||||
* @param array|Collection $config Client configuration data
|
||||
*
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
namespace Aws\Common\Client;
|
||||
|
||||
use Aws\Common\Exception\InvalidArgumentException;
|
||||
use Guzzle\Common\Event;
|
||||
use Guzzle\Http\EntityBody;
|
||||
use Guzzle\Service\Command\AbstractCommand as Command;
|
||||
|
|
@ -66,6 +67,7 @@ class UploadBodyListener implements EventSubscriberInterface
|
|||
* Converts filenames and file handles into EntityBody objects before the command is validated
|
||||
*
|
||||
* @param Event $event Event emitted
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function onCommandBeforePrepare(Event $event)
|
||||
{
|
||||
|
|
@ -81,13 +83,13 @@ class UploadBodyListener implements EventSubscriberInterface
|
|||
$body = fopen($source, 'r');
|
||||
}
|
||||
|
||||
if (null !== $body) {
|
||||
$body = EntityBody::factory($body);
|
||||
}
|
||||
|
||||
// Prepare the body parameter and remove the source file parameter
|
||||
$command->remove($this->sourceParameter);
|
||||
$command->set($this->bodyParameter, $body);
|
||||
if (null !== $body) {
|
||||
$command->remove($this->sourceParameter);
|
||||
$command->set($this->bodyParameter, EntityBody::factory($body));
|
||||
} else {
|
||||
throw new InvalidArgumentException("You must specify a non-null value for the {$this->bodyParameter} or {$this->sourceParameter} parameters.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ use Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor;
|
|||
*/
|
||||
class AwsQueryVisitor extends AbstractRequestVisitor
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
private $fqname;
|
||||
|
||||
public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
|
||||
{
|
||||
$this->fqname = $command->getName();
|
||||
$query = array();
|
||||
$this->customResolver($value, $param, $query, $param->getWireName());
|
||||
$request->addPostFields($query);
|
||||
|
|
@ -66,8 +66,11 @@ class AwsQueryVisitor extends AbstractRequestVisitor
|
|||
} elseif ($hasAdditionalProperties) {
|
||||
// Handle map cases like &Attribute.1.Name=<name>&Attribute.1.Value=<value>
|
||||
$additionalPropertyCount++;
|
||||
$query["{$prefix}.{$additionalPropertyCount}.Name"] = $name;
|
||||
$newPrefix = "{$prefix}.{$additionalPropertyCount}.Value";
|
||||
$data = $param->getData();
|
||||
$keyName = isset($data['keyName']) ? $data['keyName'] : 'key';
|
||||
$valueName = isset($data['valueName']) ? $data['valueName'] : 'value';
|
||||
$query["{$prefix}.{$additionalPropertyCount}.{$keyName}"] = $name;
|
||||
$newPrefix = "{$prefix}.{$additionalPropertyCount}.{$valueName}";
|
||||
if (is_array($v)) {
|
||||
$this->customResolver($v, $param->getAdditionalProperties(), $query, $newPrefix);
|
||||
} else {
|
||||
|
|
@ -87,6 +90,20 @@ class AwsQueryVisitor extends AbstractRequestVisitor
|
|||
*/
|
||||
protected function resolveArray(Parameter $param, array $value, $prefix, array &$query)
|
||||
{
|
||||
static $serializeEmpty = array(
|
||||
'SetLoadBalancerPoliciesForBackendServer' => 1,
|
||||
'SetLoadBalancerPoliciesOfListener' => 1,
|
||||
'UpdateStack' => 1
|
||||
);
|
||||
|
||||
// For BC, serialize empty lists for specific operations
|
||||
if (!$value) {
|
||||
if (isset($serializeEmpty[$this->fqname])) {
|
||||
$query[$prefix] = '';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$offset = $param->getData('offset') ?: 1;
|
||||
foreach ($value as $index => $v) {
|
||||
$index += $offset;
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ use Aws\Common\Enum\ClientOptions as Options;
|
|||
use Aws\Common\Exception\InvalidArgumentException;
|
||||
use Aws\Common\Exception\RequiredExtensionNotLoadedException;
|
||||
use Aws\Common\Exception\RuntimeException;
|
||||
use Guzzle\Http\ClientInterface;
|
||||
use Guzzle\Common\FromConfigInterface;
|
||||
use Guzzle\Cache\CacheAdapterInterface;
|
||||
use Guzzle\Cache\DoctrineCacheAdapter;
|
||||
use Guzzle\Common\Collection;
|
||||
|
||||
/**
|
||||
* Basic implementation of the AWSCredentials interface that allows callers to
|
||||
|
|
@ -33,25 +33,19 @@ class Credentials implements CredentialsInterface, FromConfigInterface
|
|||
{
|
||||
const ENV_KEY = 'AWS_ACCESS_KEY_ID';
|
||||
const ENV_SECRET = 'AWS_SECRET_KEY';
|
||||
const ENV_SECRET_ACCESS_KEY = 'AWS_SECRET_ACCESS_KEY';
|
||||
const ENV_PROFILE = 'AWS_PROFILE';
|
||||
|
||||
/**
|
||||
* @var string AWS Access key ID
|
||||
*/
|
||||
/** @var string AWS Access Key ID */
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* @var string AWS Secret access key
|
||||
*/
|
||||
/** @var string AWS Secret Access Key */
|
||||
protected $secret;
|
||||
|
||||
/**
|
||||
* @var string Security token
|
||||
*/
|
||||
/** @var string AWS Security Token */
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* @var int Time to die of token
|
||||
*/
|
||||
/** @var int Time to die of token */
|
||||
protected $ttd;
|
||||
|
||||
/**
|
||||
|
|
@ -66,6 +60,7 @@ class Credentials implements CredentialsInterface, FromConfigInterface
|
|||
Options::SECRET => null,
|
||||
Options::TOKEN => null,
|
||||
Options::TOKEN_TTD => null,
|
||||
Options::PROFILE => null,
|
||||
Options::CREDENTIALS_CACHE => null,
|
||||
Options::CREDENTIALS_CACHE_KEY => null,
|
||||
Options::CREDENTIALS_CLIENT => null
|
||||
|
|
@ -97,19 +92,7 @@ class Credentials implements CredentialsInterface, FromConfigInterface
|
|||
|
||||
// Create the credentials object
|
||||
if (!$config[Options::KEY] || !$config[Options::SECRET]) {
|
||||
// No keys were provided, so attempt to retrieve some from the environment
|
||||
$envKey = isset($_SERVER[self::ENV_KEY]) ? $_SERVER[self::ENV_KEY] : getenv(self::ENV_KEY);
|
||||
$envSecret = isset($_SERVER[self::ENV_SECRET]) ? $_SERVER[self::ENV_SECRET] : getenv(self::ENV_SECRET);
|
||||
if ($envKey && $envSecret) {
|
||||
// Use credentials set in the environment variables
|
||||
$credentials = new static($envKey, $envSecret);
|
||||
} else {
|
||||
// Use instance profile credentials (available on EC2 instances)
|
||||
$credentials = new RefreshableInstanceProfileCredentials(
|
||||
new static('', '', '', 1),
|
||||
$config[Options::CREDENTIALS_CLIENT]
|
||||
);
|
||||
}
|
||||
$credentials = self::createFromEnvironment($config);
|
||||
// If no cache key was set, use the crc32 hostname of the server
|
||||
$cacheKey = $cacheKey ?: 'credentials_' . crc32(gethostname());
|
||||
} else {
|
||||
|
|
@ -127,31 +110,56 @@ class Credentials implements CredentialsInterface, FromConfigInterface
|
|||
// Check if the credentials are refreshable, and if so, configure caching
|
||||
$cache = $config[Options::CREDENTIALS_CACHE];
|
||||
if ($cacheKey && $cache) {
|
||||
if ($cache === 'true' || $cache === true) {
|
||||
// If no cache adapter was provided, then create one for the user
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!extension_loaded('apc')) {
|
||||
throw new RequiredExtensionNotLoadedException('PHP has not been compiled with APC. Unable to cache '
|
||||
. 'the credentials.');
|
||||
} elseif (!class_exists('Doctrine\Common\Cache\ApcCache')) {
|
||||
throw new RuntimeException(
|
||||
'Cannot set ' . Options::CREDENTIALS_CACHE . ' to true because the Doctrine cache component is '
|
||||
. 'not installed. Either install doctrine/cache or pass in an instantiated '
|
||||
. 'Guzzle\Cache\CacheAdapterInterface object'
|
||||
);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
$cache = new DoctrineCacheAdapter(new \Doctrine\Common\Cache\ApcCache());
|
||||
} elseif (!($cache instanceof CacheAdapterInterface)) {
|
||||
throw new InvalidArgumentException('Unable to utilize caching with the specified options');
|
||||
}
|
||||
// Decorate the credentials with a cache
|
||||
$credentials = new CacheableCredentials($credentials, $cache, $cacheKey);
|
||||
$credentials = self::createCache($credentials, $cache, $cacheKey);
|
||||
}
|
||||
|
||||
return $credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create credentials from the credentials ini file in the HOME directory.
|
||||
*
|
||||
* @param string|null $profile Pass a specific profile to use. If no
|
||||
* profile is specified we will attempt to use
|
||||
* the value specified in the AWS_PROFILE
|
||||
* environment variable. If AWS_PROFILE is not
|
||||
* set, the "default" profile is used.
|
||||
* @param string|null $filename Pass a string to specify the location of the
|
||||
* credentials files. If null is passed, the
|
||||
* SDK will attempt to find the configuration
|
||||
* file at in your HOME directory at
|
||||
* ~/.aws/credentials.
|
||||
* @return CredentialsInterface
|
||||
* @throws \RuntimeException if the file cannot be found, if the file is
|
||||
* invalid, or if the profile is invalid.
|
||||
*/
|
||||
public static function fromIni($profile = null, $filename = null)
|
||||
{
|
||||
if (!$filename) {
|
||||
$filename = self::getHomeDir() . '/.aws/credentials';
|
||||
}
|
||||
|
||||
if (!$profile) {
|
||||
$profile = self::getEnvVar(self::ENV_PROFILE) ?: 'default';
|
||||
}
|
||||
|
||||
if (!file_exists($filename) || !($data = parse_ini_file($filename, true))) {
|
||||
throw new \RuntimeException("Invalid AWS credentials file: {$filename}.");
|
||||
}
|
||||
|
||||
if (empty($data[$profile])) {
|
||||
throw new \RuntimeException("Invalid AWS credentials profile {$profile} in {$filename}.");
|
||||
}
|
||||
|
||||
return new self(
|
||||
$data[$profile]['aws_access_key_id'],
|
||||
$data[$profile]['aws_secret_access_key'],
|
||||
isset($data[$profile]['aws_security_token'])
|
||||
? $data[$profile]['aws_security_token']
|
||||
: null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new BasicAWSCredentials object, with the specified AWS
|
||||
* access key and AWS secret key
|
||||
|
|
@ -169,9 +177,6 @@ class Credentials implements CredentialsInterface, FromConfigInterface
|
|||
$this->ttd = $expiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return json_encode(array(
|
||||
|
|
@ -182,9 +187,6 @@ class Credentials implements CredentialsInterface, FromConfigInterface
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$data = json_decode($serialized, true);
|
||||
|
|
@ -194,49 +196,31 @@ class Credentials implements CredentialsInterface, FromConfigInterface
|
|||
$this->ttd = $data[Options::TOKEN_TTD];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAccessKeyId()
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSecretKey()
|
||||
{
|
||||
return $this->secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSecurityToken()
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExpiration()
|
||||
{
|
||||
return $this->ttd;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isExpired()
|
||||
{
|
||||
return $this->ttd !== null && time() >= $this->ttd;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setAccessKeyId($key)
|
||||
{
|
||||
$this->key = $key;
|
||||
|
|
@ -244,9 +228,6 @@ class Credentials implements CredentialsInterface, FromConfigInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSecretKey($secret)
|
||||
{
|
||||
$this->secret = $secret;
|
||||
|
|
@ -254,9 +235,6 @@ class Credentials implements CredentialsInterface, FromConfigInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSecurityToken($token)
|
||||
{
|
||||
$this->token = $token;
|
||||
|
|
@ -264,13 +242,96 @@ class Credentials implements CredentialsInterface, FromConfigInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setExpiration($timestamp)
|
||||
{
|
||||
$this->ttd = $timestamp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* When no keys are provided, attempt to create them based on the
|
||||
* environment or instance profile credentials.
|
||||
*
|
||||
* @param array|Collection $config
|
||||
*
|
||||
* @return CredentialsInterface
|
||||
*/
|
||||
private static function createFromEnvironment($config)
|
||||
{
|
||||
// Get key and secret from ENV variables
|
||||
$envKey = self::getEnvVar(self::ENV_KEY);
|
||||
if (!($envSecret = self::getEnvVar(self::ENV_SECRET))) {
|
||||
// Use AWS_SECRET_ACCESS_KEY if AWS_SECRET_KEY was not set.
|
||||
$envSecret = self::getEnvVar(self::ENV_SECRET_ACCESS_KEY);
|
||||
}
|
||||
|
||||
// Use credentials from the environment variables if available
|
||||
if ($envKey && $envSecret) {
|
||||
return new static($envKey, $envSecret);
|
||||
}
|
||||
|
||||
// Use credentials from the ini file in HOME directory if available
|
||||
$home = self::getHomeDir();
|
||||
if ($home && file_exists("{$home}/.aws/credentials")) {
|
||||
return self::fromIni($config[Options::PROFILE], "{$home}/.aws/credentials");
|
||||
}
|
||||
|
||||
// Use instance profile credentials (available on EC2 instances)
|
||||
return new RefreshableInstanceProfileCredentials(
|
||||
new static('', '', '', 1),
|
||||
$config[Options::CREDENTIALS_CLIENT]
|
||||
);
|
||||
}
|
||||
|
||||
private static function createCache(CredentialsInterface $credentials, $cache, $cacheKey)
|
||||
{
|
||||
if ($cache === 'true' || $cache === true) {
|
||||
// If no cache adapter was provided, then create one for the user
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!extension_loaded('apc')) {
|
||||
throw new RequiredExtensionNotLoadedException('PHP has not been compiled with APC. Unable to cache '
|
||||
. 'the credentials.');
|
||||
} elseif (!class_exists('Doctrine\Common\Cache\ApcCache')) {
|
||||
throw new RuntimeException(
|
||||
'Cannot set ' . Options::CREDENTIALS_CACHE . ' to true because the Doctrine cache component is '
|
||||
. 'not installed. Either install doctrine/cache or pass in an instantiated '
|
||||
. 'Guzzle\Cache\CacheAdapterInterface object'
|
||||
);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
$cache = new DoctrineCacheAdapter(new \Doctrine\Common\Cache\ApcCache());
|
||||
} elseif (!($cache instanceof CacheAdapterInterface)) {
|
||||
throw new InvalidArgumentException('Unable to utilize caching with the specified options');
|
||||
}
|
||||
|
||||
// Decorate the credentials with a cache
|
||||
return new CacheableCredentials($credentials, $cache, $cacheKey);
|
||||
}
|
||||
|
||||
private static function getHomeDir()
|
||||
{
|
||||
// On Linux/Unix-like systems, use the HOME environment variable
|
||||
if ($homeDir = self::getEnvVar('HOME')) {
|
||||
return $homeDir;
|
||||
}
|
||||
|
||||
// Get the HOMEDRIVE and HOMEPATH values for Windows hosts
|
||||
$homeDrive = self::getEnvVar('HOMEDRIVE');
|
||||
$homePath = self::getEnvVar('HOMEPATH');
|
||||
|
||||
return ($homeDrive && $homePath) ? $homeDrive . $homePath : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the value of an environment variable by checking $_SERVER and getenv().
|
||||
*
|
||||
* @param string $var Name of the environment variable
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
private static function getEnvVar($var)
|
||||
{
|
||||
return isset($_SERVER[$var]) ? $_SERVER[$var] : getenv($var);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
68
apps/files_external/3rdparty/aws-sdk-php/Aws/Common/Credentials/NullCredentials.php
vendored
Normal file
68
apps/files_external/3rdparty/aws-sdk-php/Aws/Common/Credentials/NullCredentials.php
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
namespace Aws\Common\Credentials;
|
||||
|
||||
/**
|
||||
* A blank set of credentials. AWS clients must be provided credentials, but
|
||||
* there are some types of requests that do not need authentication. This class
|
||||
* can be used to pivot on that scenario, and also serve as a mock credentials
|
||||
* object when testing
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class NullCredentials implements CredentialsInterface
|
||||
{
|
||||
public function getAccessKeyId()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getSecretKey()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getSecurityToken()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getExpiration()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isExpired()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return 'N;';
|
||||
}
|
||||
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
public function setAccessKeyId($key)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
public function setSecretKey($secret)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
public function setSecurityToken($token)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
public function setExpiration($timestamp)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,11 @@ class ClientOptions extends Enum
|
|||
*/
|
||||
const CREDENTIALS = 'credentials';
|
||||
|
||||
/**
|
||||
* @var string Name of a credential profile to read from your ~/.aws/credentials file
|
||||
*/
|
||||
const PROFILE = 'profile';
|
||||
|
||||
/**
|
||||
* @var string Custom AWS security token to use with request authentication
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use Aws\Common\Enum;
|
|||
* Contains enumerable region code values. These should be useful in most cases,
|
||||
* with Amazon S3 being the most notable exception
|
||||
*
|
||||
* @link http://docs.amazonwebservices.com/general/latest/gr/rande.html AWS Regions and Endpoints
|
||||
* @link http://docs.aws.amazon.com/general/latest/gr/rande.html AWS Regions and Endpoints
|
||||
*/
|
||||
class Region extends Enum
|
||||
{
|
||||
|
|
@ -52,6 +52,9 @@ class Region extends Enum
|
|||
const SA_EAST_1 = 'sa-east-1';
|
||||
const SAO_PAULO = 'sa-east-1';
|
||||
|
||||
const CN_NORTH_1 = 'cn-north-1';
|
||||
const BEIJING = 'cn-north-1';
|
||||
|
||||
const US_GOV_WEST_1 = 'us-gov-west-1';
|
||||
const GOV_CLOUD_US = 'us-gov-west-1';
|
||||
}
|
||||
|
|
|
|||
24
apps/files_external/3rdparty/aws-sdk-php/Aws/Common/Exception/TransferException.php
vendored
Normal file
24
apps/files_external/3rdparty/aws-sdk-php/Aws/Common/Exception/TransferException.php
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://aws.amazon.com/apache2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Aws\Common\Exception;
|
||||
|
||||
use Guzzle\Http\Exception\CurlException;
|
||||
|
||||
/**
|
||||
* Transfer request exception
|
||||
*/
|
||||
class TransferException extends CurlException implements AwsExceptionInterface {}
|
||||
|
|
@ -41,7 +41,7 @@ abstract class Facade implements FacadeInterface
|
|||
if (isset($service['alias'], $service['class'])) {
|
||||
$facadeClass = __NAMESPACE__ . '\\' . $service['alias'];
|
||||
$facadeAlias = ltrim($targetNamespace . '\\' . $service['alias'], '\\');
|
||||
if (!class_exists($facadeAlias)) {
|
||||
if (!class_exists($facadeAlias) && class_exists($facadeClass)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
class_alias($facadeClass, $facadeAlias);
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
|||
|
|
@ -56,6 +56,14 @@ class CloudSearch extends Facade
|
|||
}
|
||||
}
|
||||
|
||||
class CloudTrail extends Facade
|
||||
{
|
||||
public static function getServiceBuilderKey()
|
||||
{
|
||||
return 'cloudtrail';
|
||||
}
|
||||
}
|
||||
|
||||
class CloudWatch extends Facade
|
||||
{
|
||||
public static function getServiceBuilderKey()
|
||||
|
|
@ -160,6 +168,14 @@ class ImportExport extends Facade
|
|||
}
|
||||
}
|
||||
|
||||
class Kinesis extends Facade
|
||||
{
|
||||
public static function getServiceBuilderKey()
|
||||
{
|
||||
return 'kinesis';
|
||||
}
|
||||
}
|
||||
|
||||
class OpsWorks extends Facade
|
||||
{
|
||||
public static function getServiceBuilderKey()
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class HostNameUtils
|
|||
* @param Url $url HTTP URL
|
||||
*
|
||||
* @return string
|
||||
* @link http://docs.amazonwebservices.com/general/latest/gr/rande.html
|
||||
* @link http://docs.aws.amazon.com/general/latest/gr/rande.html
|
||||
*/
|
||||
public static function parseRegionName(Url $url)
|
||||
{
|
||||
|
|
@ -68,7 +68,7 @@ class HostNameUtils
|
|||
* @param Url $url HTTP URL
|
||||
*
|
||||
* @return string Returns a service name (or empty string)
|
||||
* @link http://docs.amazonwebservices.com/general/latest/gr/rande.html
|
||||
* @link http://docs.aws.amazon.com/general/latest/gr/rande.html
|
||||
*/
|
||||
public static function parseServiceName(Url $url)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ class InstanceMetadataClient extends AbstractClient
|
|||
$config = Collection::fromConfig($config, array(
|
||||
Options::BASE_URL => 'http://169.254.169.254/{version}/',
|
||||
'version' => 'latest',
|
||||
'request.options' => array(
|
||||
'connect_timeout' => 5,
|
||||
'timeout' => 10
|
||||
)
|
||||
), array('base_url', 'version'));
|
||||
|
||||
return new self($config);
|
||||
|
|
@ -71,15 +75,14 @@ class InstanceMetadataClient extends AbstractClient
|
|||
{
|
||||
try {
|
||||
$request = $this->get('meta-data/iam/security-credentials/');
|
||||
$request->getCurlOptions()->set(CURLOPT_TIMEOUT, 1)->set(CURLOPT_CONNECTTIMEOUT, 1);
|
||||
$credentials = trim($request->send()->getBody(true));
|
||||
$result = $this->get("meta-data/iam/security-credentials/{$credentials}")->send()->json();
|
||||
} catch (\Exception $e) {
|
||||
$message = 'Error retrieving credentials from the instance profile metadata server. When you are not'
|
||||
. ' running inside of Amazon EC2, you must provide your AWS access key ID and secret access key in'
|
||||
$message = sprintf('Error retrieving credentials from the instance profile metadata server. When you are'
|
||||
. ' not running inside of Amazon EC2, you must provide your AWS access key ID and secret access key in'
|
||||
. ' the "key" and "secret" options when creating a client or provide an instantiated'
|
||||
. ' Aws\\Common\\Credentials\\CredentialsInterface object.';
|
||||
throw new InstanceProfileCredentialsException($message, $e->getCode(), $e);
|
||||
. ' Aws\\Common\\Credentials\\CredentialsInterface object. (%s)', $e->getMessage());
|
||||
throw new InstanceProfileCredentialsException($message, $e->getCode());
|
||||
}
|
||||
|
||||
// Ensure that the status code was successful
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ class AwsResourceIterator extends ResourceIterator
|
|||
protected $lastResult = null;
|
||||
|
||||
/**
|
||||
* Provides access to the most recent result obtained by the iterator.
|
||||
* Provides access to the most recent result obtained by the iterator. This makes it easier to extract any
|
||||
* additional information from the result which you do not have access to from the values emitted by the iterator
|
||||
*
|
||||
* @return Model|null
|
||||
*/
|
||||
|
|
@ -71,45 +72,37 @@ class AwsResourceIterator extends ResourceIterator
|
|||
return $resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareRequest()
|
||||
{
|
||||
// Get the limit parameter key to set
|
||||
$param = $this->get('limit_param');
|
||||
if ($param && ($limit = $this->command->get($param))) {
|
||||
$limitKey = $this->get('limit_key');
|
||||
if ($limitKey && ($limit = $this->command->get($limitKey))) {
|
||||
$pageSize = $this->calculatePageSize();
|
||||
|
||||
// If the limit of the command is different than the pageSize of the iterator, use the smaller value
|
||||
if ($limit && $pageSize) {
|
||||
$this->command->set('limit', min($limit, $pageSize));
|
||||
$realLimit = min($limit, $pageSize);
|
||||
$this->command->set($limitKey, $realLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function handleResults(Model $result)
|
||||
{
|
||||
$results = array();
|
||||
|
||||
// Get the result key that contains the results
|
||||
if ($resultKey = $this->get('result_key')) {
|
||||
$results = $result->getPath($resultKey) ?: array();
|
||||
$results = $this->getValueFromResult($result, $resultKey) ?: array();
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyNextToken()
|
||||
{
|
||||
// Get the token parameter key to set
|
||||
if ($tokenParam = $this->get('token_param')) {
|
||||
if ($tokenParam = $this->get('input_token')) {
|
||||
// Set the next token. Works with multi-value tokens
|
||||
if (is_array($tokenParam)) {
|
||||
if (is_array($this->nextToken) && count($tokenParam) === count($this->nextToken)) {
|
||||
|
|
@ -126,24 +119,51 @@ class AwsResourceIterator extends ResourceIterator
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function determineNextToken(Model $result)
|
||||
{
|
||||
$this->nextToken = null;
|
||||
|
||||
// If the value of "more key" is true or there is no "more key" to check, then try to get the next token
|
||||
$moreKey = $this->get('more_key');
|
||||
if ($moreKey === null || $result->getPath($moreKey)) {
|
||||
// If the value of "more_results" is true or there is no "more_results" to check, then try to get the next token
|
||||
$moreKey = $this->get('more_results');
|
||||
if ($moreKey === null || $this->getValueFromResult($result, $moreKey)) {
|
||||
// Get the token key to check
|
||||
if ($tokenKey = $this->get('token_key')) {
|
||||
if ($tokenKey = $this->get('output_token')) {
|
||||
// Get the next token's value. Works with multi-value tokens
|
||||
$getToken = function ($key) use ($result) {
|
||||
return $result->getPath((string) $key);
|
||||
};
|
||||
$this->nextToken = is_array($tokenKey) ? array_map($getToken, $tokenKey) : $getToken($tokenKey);
|
||||
if (is_array($tokenKey)) {
|
||||
$this->nextToken = array();
|
||||
foreach ($tokenKey as $key) {
|
||||
$this->nextToken[] = $this->getValueFromResult($result, $key);
|
||||
}
|
||||
} else {
|
||||
$this->nextToken = $this->getValueFromResult($result, $tokenKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the value from the result using Collection::getPath. Also adds some additional logic for keys that need
|
||||
* to access n-1 indexes (e.g., ImportExport, Kinesis). The n-1 logic only works for the known cases. We will switch
|
||||
* to a jmespath implementation in the future to cover all cases
|
||||
*
|
||||
* @param Model $result
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected function getValueFromResult(Model $result, $key)
|
||||
{
|
||||
// Special handling for keys that need to access n-1 indexes
|
||||
if (strpos($key, '#') !== false) {
|
||||
$keyParts = explode('#', $key, 2);
|
||||
$items = $result->getPath(trim($keyParts[0], '/'));
|
||||
if ($items && is_array($items)) {
|
||||
$index = count($items) - 1;
|
||||
$key = strtr($key, array('#' => $index));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the value
|
||||
return $result->getPath($key);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,24 +16,28 @@ class AwsResourceIteratorFactory implements ResourceIteratorFactoryInterface
|
|||
/**
|
||||
* @var array Default configuration values for iterators
|
||||
*/
|
||||
protected static $defaultConfig = array(
|
||||
'limit_key' => null,
|
||||
'limit_param' => null,
|
||||
'more_key' => null,
|
||||
'token_key' => null,
|
||||
'token_param' => null,
|
||||
'operations' => array(),
|
||||
protected static $defaultIteratorConfig = array(
|
||||
'input_token' => null,
|
||||
'output_token' => null,
|
||||
'limit_key' => null,
|
||||
'result_key' => null,
|
||||
'more_results' => null,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var Collection The configuration for the iterators
|
||||
* @var array Legacy configuration options mapped to their new names
|
||||
*/
|
||||
protected $config;
|
||||
private static $legacyConfigOptions = array(
|
||||
'token_param' => 'input_token',
|
||||
'token_key' => 'output_token',
|
||||
'limit_param' => 'limit_key',
|
||||
'more_key' => 'more_results',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var Collection Additional configurations for specific iterators
|
||||
* @var array Iterator configuration for each iterable operation
|
||||
*/
|
||||
protected $operations;
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var ResourceIteratorFactoryInterface Another factory that will be used first to instantiate the iterator
|
||||
|
|
@ -43,59 +47,60 @@ class AwsResourceIteratorFactory implements ResourceIteratorFactoryInterface
|
|||
/**
|
||||
* @param array $config An array of configuration values for the factory
|
||||
* @param ResourceIteratorFactoryInterface $primaryIteratorFactory Another factory to use for chain of command
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $config, ResourceIteratorFactoryInterface $primaryIteratorFactory = null)
|
||||
{
|
||||
$this->primaryIteratorFactory = $primaryIteratorFactory;
|
||||
// Set up the config with default values
|
||||
$this->config = Collection::fromConfig($config, self::$defaultConfig);
|
||||
|
||||
// Pull out the operation-specific configurations
|
||||
$this->operations = new Collection();
|
||||
$potentialOperations = $this->config->get('operations') ?: array();
|
||||
$this->config->remove('operations');
|
||||
foreach ($potentialOperations as $key => $value) {
|
||||
if (is_int($key) && is_string($value)) {
|
||||
$this->operations->set($value, array());
|
||||
} elseif (is_string($key) && is_array($value)) {
|
||||
$this->operations->set($key, $value);
|
||||
} else {
|
||||
throw new InvalidArgumentException('The iterator factory configuration was invalid.');
|
||||
}
|
||||
$this->config = array();
|
||||
foreach ($config as $name => $operation) {
|
||||
$this->config[$name] = $operation + self::$defaultIteratorConfig;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(CommandInterface $command, array $options = array())
|
||||
{
|
||||
// Get the configuration data for the command
|
||||
$commandName = $command->getName();
|
||||
$iteratorConfig = $this->operations->get($commandName) ?: array();
|
||||
$options = array_replace($this->config->getAll(), $iteratorConfig, $options);
|
||||
$commandSupported = isset($this->config[$commandName]);
|
||||
$options = $this->translateLegacyConfigOptions($options);
|
||||
$options += $commandSupported ? $this->config[$commandName] : array();
|
||||
|
||||
// Instantiate the iterator using the primary factory (if there is one)
|
||||
// Instantiate the iterator using the primary factory (if one was provided)
|
||||
if ($this->primaryIteratorFactory && $this->primaryIteratorFactory->canBuild($command)) {
|
||||
$iterator = $this->primaryIteratorFactory->build($command, $options);
|
||||
} elseif (!$this->operations->hasKey($commandName)) {
|
||||
} elseif (!$commandSupported) {
|
||||
throw new InvalidArgumentException("Iterator was not found for {$commandName}.");
|
||||
} else {
|
||||
// Fallback to this factory for creating the iterator if the primary factory did not work
|
||||
// Instantiate a generic AWS resource iterator
|
||||
$iterator = new AwsResourceIterator($command, $options);
|
||||
}
|
||||
|
||||
return $iterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canBuild(CommandInterface $command)
|
||||
{
|
||||
return ($this->primaryIteratorFactory && $this->primaryIteratorFactory->canBuild($command))
|
||||
|| $this->operations->hasKey($command->getName());
|
||||
if ($this->primaryIteratorFactory) {
|
||||
return $this->primaryIteratorFactory->canBuild($command);
|
||||
} else {
|
||||
return isset($this->config[$command->getName()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $config The config for a single operation
|
||||
*
|
||||
* @return array The modified config with legacy options translated
|
||||
*/
|
||||
private function translateLegacyConfigOptions($config)
|
||||
{
|
||||
foreach (self::$legacyConfigOptions as $legacyOption => $newOption) {
|
||||
if (isset($config[$legacyOption])) {
|
||||
$config[$newOption] = $config[$legacyOption];
|
||||
unset($config[$legacyOption]);
|
||||
}
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,12 +53,53 @@ return array(
|
|||
'class' => 'Aws\CloudSearch\CloudSearchClient'
|
||||
),
|
||||
|
||||
'cloudsearch_20110201' => array(
|
||||
'extends' => 'cloudsearch',
|
||||
'params' => array(
|
||||
'version' => '2011-02-01'
|
||||
)
|
||||
),
|
||||
|
||||
'cloudsearchdomain' => array(
|
||||
'alias' => 'CloudSearchDomain',
|
||||
'extends' => 'default_settings',
|
||||
'class' => 'Aws\CloudSearchDomain\CloudSearchDomainClient'
|
||||
),
|
||||
|
||||
'cloudtrail' => array(
|
||||
'alias' => 'CloudTrail',
|
||||
'extends' => 'default_settings',
|
||||
'class' => 'Aws\CloudTrail\CloudTrailClient'
|
||||
),
|
||||
|
||||
'cloudwatch' => array(
|
||||
'alias' => 'CloudWatch',
|
||||
'extends' => 'default_settings',
|
||||
'class' => 'Aws\CloudWatch\CloudWatchClient'
|
||||
),
|
||||
|
||||
'cognito-identity' => array(
|
||||
'alias' => 'CognitoIdentity',
|
||||
'extends' => 'default_settings',
|
||||
'class' => 'Aws\CognitoIdentity\CognitoIdentityClient'
|
||||
),
|
||||
|
||||
'cognitoidentity' => array('extends' => 'cognito-identity'),
|
||||
|
||||
'cognito-sync' => array(
|
||||
'alias' => 'CognitoSync',
|
||||
'extends' => 'default_settings',
|
||||
'class' => 'Aws\CognitoSync\CognitoSyncClient'
|
||||
),
|
||||
|
||||
'cognitosync' => array('extends' => 'cognito-sync'),
|
||||
|
||||
'cloudwatchlogs' => array(
|
||||
'alias' => 'CloudWatchLogs',
|
||||
'extends' => 'default_settings',
|
||||
'class' => 'Aws\CloudWatchLogs\CloudWatchLogsClient'
|
||||
),
|
||||
|
||||
'datapipeline' => array(
|
||||
'alias' => 'DataPipeline',
|
||||
'extends' => 'default_settings',
|
||||
|
|
@ -126,6 +167,12 @@ return array(
|
|||
'class' => 'Aws\Glacier\GlacierClient'
|
||||
),
|
||||
|
||||
'kinesis' => array(
|
||||
'alias' => 'Kinesis',
|
||||
'extends' => 'default_settings',
|
||||
'class' => 'Aws\Kinesis\KinesisClient'
|
||||
),
|
||||
|
||||
'iam' => array(
|
||||
'alias' => 'Iam',
|
||||
'extends' => 'default_settings',
|
||||
|
|
@ -162,6 +209,12 @@ return array(
|
|||
'class' => 'Aws\Route53\Route53Client'
|
||||
),
|
||||
|
||||
'route53domains' => array(
|
||||
'alias' => 'Route53Domains',
|
||||
'extends' => 'default_settings',
|
||||
'class' => 'Aws\Route53Domains\Route53DomainsClient'
|
||||
),
|
||||
|
||||
's3' => array(
|
||||
'alias' => 'S3',
|
||||
'extends' => 'default_settings',
|
||||
|
|
|
|||
|
|
@ -16,75 +16,29 @@
|
|||
|
||||
namespace Aws\Common\Signature;
|
||||
|
||||
use Aws\Common\Credentials\CredentialsInterface;
|
||||
use Guzzle\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Abstract signature class that can be used when implementing new concrete
|
||||
* AWS signature protocol strategies
|
||||
*/
|
||||
abstract class AbstractSignature implements SignatureInterface
|
||||
{
|
||||
/**
|
||||
* @var int Timestamp
|
||||
*/
|
||||
private $timestamp;
|
||||
|
||||
/**
|
||||
* Get the canonicalized query string for a request
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @return string
|
||||
*/
|
||||
protected function getCanonicalizedQueryString(RequestInterface $request)
|
||||
{
|
||||
$queryParams = $request->getQuery()->getAll();
|
||||
unset($queryParams['X-Amz-Signature']);
|
||||
if (empty($queryParams)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$qs = '';
|
||||
ksort($queryParams);
|
||||
foreach ($queryParams as $key => $values) {
|
||||
if (is_array($values)) {
|
||||
sort($values);
|
||||
} elseif (!$values) {
|
||||
$values = array('');
|
||||
}
|
||||
|
||||
foreach ((array) $values as $value) {
|
||||
$qs .= rawurlencode($key) . '=' . rawurlencode($value) . '&';
|
||||
}
|
||||
}
|
||||
|
||||
return substr($qs, 0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the timestamp used for the class
|
||||
*
|
||||
* @param bool $refresh Set to TRUE to refresh the cached timestamp
|
||||
* Provides the timestamp used for the class (used for mocking PHP's time() function)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getTimestamp($refresh = false)
|
||||
protected function getTimestamp()
|
||||
{
|
||||
if (!$this->timestamp || $refresh) {
|
||||
$this->timestamp = time();
|
||||
}
|
||||
|
||||
return $this->timestamp;
|
||||
return time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a date for one of the parts of the requests
|
||||
*
|
||||
* @param string $format Date format
|
||||
*
|
||||
* @return string
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
protected function getDateTime($format)
|
||||
{
|
||||
return gmdate($format, $this->getTimestamp());
|
||||
public function createPresignedUrl(
|
||||
RequestInterface $request,
|
||||
CredentialsInterface $credentials,
|
||||
$expires
|
||||
) {
|
||||
throw new \BadMethodCallException(__METHOD__ . ' not implemented');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,4 +34,19 @@ interface SignatureInterface
|
|||
* @param CredentialsInterface $credentials Signing credentials
|
||||
*/
|
||||
public function signRequest(RequestInterface $request, CredentialsInterface $credentials);
|
||||
|
||||
/**
|
||||
* Create a pre-signed URL
|
||||
*
|
||||
* @param RequestInterface $request Request to sign
|
||||
* @param CredentialsInterface $credentials Credentials used to sign
|
||||
* @param int|string|\DateTime $expires The time at which the URL should expire. This can be a Unix timestamp, a
|
||||
* PHP DateTime object, or a string that can be evaluated by strtotime
|
||||
* @return string
|
||||
*/
|
||||
public function createPresignedUrl(
|
||||
RequestInterface $request,
|
||||
CredentialsInterface $credentials,
|
||||
$expires
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,16 +25,13 @@ use Guzzle\Http\Message\RequestInterface;
|
|||
*/
|
||||
class SignatureV2 extends AbstractSignature
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
|
||||
{
|
||||
// refresh the cached timestamp
|
||||
$this->getTimestamp(true);
|
||||
$timestamp = $this->getTimestamp(true);
|
||||
|
||||
// set values we need in CanonicalizedParameterString
|
||||
$this->addParameter($request, 'Timestamp', $this->getDateTime('c'));
|
||||
$this->addParameter($request, 'Timestamp', gmdate('c', $timestamp));
|
||||
$this->addParameter($request, 'SignatureVersion', '2');
|
||||
$this->addParameter($request, 'SignatureMethod', 'HmacSHA256');
|
||||
$this->addParameter($request, 'AWSAccessKeyId', $credentials->getAccessKeyId());
|
||||
|
|
@ -90,7 +87,7 @@ class SignatureV2 extends AbstractSignature
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCanonicalizedParameterString(RequestInterface $request)
|
||||
private function getCanonicalizedParameterString(RequestInterface $request)
|
||||
{
|
||||
if ($request->getMethod() == 'POST') {
|
||||
$params = $request->getPostFields()->toArray();
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://aws.amazon.com/apache2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Aws\Common\Signature;
|
||||
|
||||
use Aws\Common\Credentials\CredentialsInterface;
|
||||
use Aws\Common\Enum\DateFormat;
|
||||
use Guzzle\Http\Message\RequestInterface;
|
||||
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
|
||||
|
||||
/**
|
||||
* Implementation of Signature Version 3
|
||||
* @link http://docs.amazonwebservices.com/amazonswf/latest/developerguide/HMACAuth-swf.html
|
||||
*/
|
||||
class SignatureV3 extends AbstractSignature
|
||||
{
|
||||
/**
|
||||
* Get an array of headers to be signed
|
||||
*
|
||||
* @param RequestInterface $request Request to get headers from
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getHeadersToSign(RequestInterface $request)
|
||||
{
|
||||
$headers = array();
|
||||
foreach ($request->getHeaders()->toArray() as $k => $v) {
|
||||
$k = strtolower($k);
|
||||
if ($k == 'host' || strpos($k, 'x-amz-') !== false) {
|
||||
$headers[$k] = implode(',', $v);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the headers alphabetically and add them to the string to sign
|
||||
ksort($headers);
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
|
||||
{
|
||||
// Refresh the cached timestamp
|
||||
$this->getTimestamp(true);
|
||||
|
||||
// Add default headers
|
||||
$request->setHeader('x-amz-date', $this->getDateTime(DateFormat::RFC1123));
|
||||
|
||||
// Add the security token if one is present
|
||||
if ($credentials->getSecurityToken()) {
|
||||
$request->setHeader('x-amz-security-token', $credentials->getSecurityToken());
|
||||
}
|
||||
|
||||
// Grab the path and ensure that it is absolute
|
||||
$path = '/' . ltrim($request->getUrl(true)->normalizePath()->getPath(), '/');
|
||||
|
||||
// Begin building the string to sign
|
||||
$sign = $request->getMethod() . "\n"
|
||||
. "{$path}\n"
|
||||
. $this->getCanonicalizedQueryString($request) . "\n";
|
||||
|
||||
// Get all of the headers that must be signed (host and x-amz-*)
|
||||
$headers = $this->getHeadersToSign($request);
|
||||
foreach ($headers as $key => $value) {
|
||||
$sign .= $key . ':' . $value . "\n";
|
||||
}
|
||||
|
||||
$sign .= "\n";
|
||||
|
||||
// Add the body of the request if a body is present
|
||||
if ($request instanceof EntityEnclosingRequestInterface) {
|
||||
$sign .= (string) $request->getBody();
|
||||
}
|
||||
|
||||
// Add the string to sign to the request for debugging purposes
|
||||
$request->getParams()->set('aws.string_to_sign', $sign);
|
||||
|
||||
$signature = base64_encode(hash_hmac('sha256',
|
||||
hash('sha256', $sign, true), $credentials->getSecretKey(), true));
|
||||
|
||||
// Add the authorization header to the request
|
||||
$request->setHeader('x-amzn-authorization', sprintf('AWS3 AWSAccessKeyId=%s,Algorithm=HmacSHA256,SignedHeaders=%s,Signature=%s',
|
||||
$credentials->getAccessKeyId(),
|
||||
implode(';', array_keys($headers)),
|
||||
$signature));
|
||||
}
|
||||
}
|
||||
|
|
@ -22,18 +22,15 @@ use Guzzle\Http\Message\RequestInterface;
|
|||
|
||||
/**
|
||||
* Implementation of Signature Version 3 HTTPS
|
||||
* @link http://docs.amazonwebservices.com/Route53/latest/DeveloperGuide/RESTAuthentication.html
|
||||
* @link http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/RESTAuthentication.html
|
||||
*/
|
||||
class SignatureV3Https extends AbstractSignature
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
|
||||
{
|
||||
// Add a date header if one is not set
|
||||
if (!$request->hasHeader('date') && !$request->hasHeader('x-amz-date')) {
|
||||
$request->setHeader('Date', $this->getDateTime(DateFormat::RFC1123));
|
||||
$request->setHeader('Date', gmdate(DateFormat::RFC1123, $this->getTimestamp()));
|
||||
}
|
||||
|
||||
// Add the security token if one is present
|
||||
|
|
@ -42,7 +39,7 @@ class SignatureV3Https extends AbstractSignature
|
|||
}
|
||||
|
||||
// Determine the string to sign
|
||||
$stringToSign = $request->getHeader('Date', true) ?: $request->getHeader('x-amz-date', true);
|
||||
$stringToSign = (string) ($request->getHeader('Date') ?: $request->getHeader('x-amz-date'));
|
||||
$request->getParams()->set('aws.string_to_sign', $stringToSign);
|
||||
|
||||
// Calculate the signature
|
||||
|
|
|
|||
|
|
@ -19,46 +19,47 @@ namespace Aws\Common\Signature;
|
|||
use Aws\Common\Credentials\CredentialsInterface;
|
||||
use Aws\Common\Enum\DateFormat;
|
||||
use Aws\Common\HostNameUtils;
|
||||
use Guzzle\Http\Message\EntityEnclosingRequest;
|
||||
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
|
||||
use Guzzle\Http\Message\RequestFactory;
|
||||
use Guzzle\Http\Message\RequestInterface;
|
||||
use Guzzle\Http\QueryString;
|
||||
use Guzzle\Http\Url;
|
||||
|
||||
/**
|
||||
* Signature Version 4
|
||||
* @link http://docs.amazonwebservices.com/general/latest/gr/signature-version-4.html
|
||||
* @link http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
|
||||
*/
|
||||
class SignatureV4 extends AbstractSignature implements EndpointSignatureInterface
|
||||
{
|
||||
/**
|
||||
* @var string Cache of the default empty entity-body payload
|
||||
*/
|
||||
/** @var string Cache of the default empty entity-body payload */
|
||||
const DEFAULT_PAYLOAD = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
|
||||
|
||||
/**
|
||||
* @var string Explicitly set service name
|
||||
*/
|
||||
/** @var string Explicitly set service name */
|
||||
protected $serviceName;
|
||||
|
||||
/**
|
||||
* @var string Explicitly set region name
|
||||
*/
|
||||
/** @var string Explicitly set region name */
|
||||
protected $regionName;
|
||||
|
||||
/**
|
||||
* @var int Maximum number of hashes to cache
|
||||
*/
|
||||
/** @var int Maximum number of hashes to cache */
|
||||
protected $maxCacheSize = 50;
|
||||
|
||||
/**
|
||||
* @var array Cache of previously signed values
|
||||
*/
|
||||
/** @var array Cache of previously signed values */
|
||||
protected $hashCache = array();
|
||||
|
||||
/**
|
||||
* @var int Size of the hash cache
|
||||
*/
|
||||
/** @var int Size of the hash cache */
|
||||
protected $cacheSize = 0;
|
||||
|
||||
/**
|
||||
* @param string $serviceName Bind the signing to a particular service name
|
||||
* @param string $regionName Bind the signing to a particular region name
|
||||
*/
|
||||
public function __construct($serviceName = null, $regionName = null)
|
||||
{
|
||||
$this->serviceName = $serviceName;
|
||||
$this->regionName = $regionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the service name instead of inferring it from a request URL
|
||||
*
|
||||
|
|
@ -101,26 +102,20 @@ class SignatureV4 extends AbstractSignature implements EndpointSignatureInterfac
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
|
||||
{
|
||||
// Refresh the cached timestamp
|
||||
$this->getTimestamp(true);
|
||||
$timestamp = $this->getTimestamp();
|
||||
$longDate = gmdate(DateFormat::ISO8601, $timestamp);
|
||||
$shortDate = substr($longDate, 0, 8);
|
||||
|
||||
$longDate = $this->getDateTime(DateFormat::ISO8601);
|
||||
$shortDate = $this->getDateTime(DateFormat::SHORT);
|
||||
|
||||
// Remove any previously set Authorization headers so that
|
||||
// exponential backoff works correctly
|
||||
// Remove any previously set Authorization headers so that retries work
|
||||
$request->removeHeader('Authorization');
|
||||
|
||||
// Requires a x-amz-date header or Date
|
||||
if ($request->hasHeader('x-amz-date') || !$request->hasHeader('Date')) {
|
||||
$request->setHeader('x-amz-date', $longDate);
|
||||
} else {
|
||||
$request->setHeader('Date', $this->getDateTime(DateFormat::RFC1123));
|
||||
$request->setHeader('Date', gmdate(DateFormat::RFC1123, $timestamp));
|
||||
}
|
||||
|
||||
// Add the security token if one is present
|
||||
|
|
@ -129,22 +124,22 @@ class SignatureV4 extends AbstractSignature implements EndpointSignatureInterfac
|
|||
}
|
||||
|
||||
// Parse the service and region or use one that is explicitly set
|
||||
$url = null;
|
||||
if (!$this->regionName || !$this->serviceName) {
|
||||
$region = $this->regionName;
|
||||
$service = $this->serviceName;
|
||||
if (!$region || !$service) {
|
||||
$url = Url::factory($request->getUrl());
|
||||
}
|
||||
if (!$region = $this->regionName) {
|
||||
$region = HostNameUtils::parseRegionName($url);
|
||||
}
|
||||
if (!$service = $this->serviceName) {
|
||||
$service = HostNameUtils::parseServiceName($url);
|
||||
$region = $region ?: HostNameUtils::parseRegionName($url);
|
||||
$service = $service ?: HostNameUtils::parseServiceName($url);
|
||||
}
|
||||
|
||||
$credentialScope = "{$shortDate}/{$region}/{$service}/aws4_request";
|
||||
|
||||
$signingContext = $this->createCanonicalRequest($request);
|
||||
$signingContext['string_to_sign'] = "AWS4-HMAC-SHA256\n{$longDate}\n{$credentialScope}\n"
|
||||
. hash('sha256', $signingContext['canonical_request']);
|
||||
$credentialScope = $this->createScope($shortDate, $region, $service);
|
||||
$payload = $this->getPayload($request);
|
||||
$signingContext = $this->createSigningContext($request, $payload);
|
||||
$signingContext['string_to_sign'] = $this->createStringToSign(
|
||||
$longDate,
|
||||
$credentialScope,
|
||||
$signingContext['canonical_request']
|
||||
);
|
||||
|
||||
// Calculate the signing key using a series of derived keys
|
||||
$signingKey = $this->getSigningKey($shortDate, $region, $service, $credentials->getSecretKey());
|
||||
|
|
@ -158,32 +153,175 @@ class SignatureV4 extends AbstractSignature implements EndpointSignatureInterfac
|
|||
$request->getParams()->set('aws.signature', $signingContext);
|
||||
}
|
||||
|
||||
public function createPresignedUrl(
|
||||
RequestInterface $request,
|
||||
CredentialsInterface $credentials,
|
||||
$expires
|
||||
) {
|
||||
$request = $this->createPresignedRequest($request, $credentials);
|
||||
$query = $request->getQuery();
|
||||
$httpDate = gmdate(DateFormat::ISO8601, $this->getTimestamp());
|
||||
$shortDate = substr($httpDate, 0, 8);
|
||||
$scope = $this->createScope(
|
||||
$shortDate,
|
||||
$this->regionName,
|
||||
$this->serviceName
|
||||
);
|
||||
$this->addQueryValues($scope, $request, $credentials, $expires);
|
||||
$payload = $this->getPresignedPayload($request);
|
||||
$context = $this->createSigningContext($request, $payload);
|
||||
$stringToSign = $this->createStringToSign(
|
||||
$httpDate,
|
||||
$scope,
|
||||
$context['canonical_request']
|
||||
);
|
||||
$key = $this->getSigningKey(
|
||||
$shortDate,
|
||||
$this->regionName,
|
||||
$this->serviceName,
|
||||
$credentials->getSecretKey()
|
||||
);
|
||||
$query['X-Amz-Signature'] = hash_hmac('sha256', $stringToSign, $key);
|
||||
|
||||
return $request->getUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a POST request to a GET request by moving POST fields into the
|
||||
* query string.
|
||||
*
|
||||
* Useful for pre-signing query protocol requests.
|
||||
*
|
||||
* @param EntityEnclosingRequestInterface $request Request to clone
|
||||
*
|
||||
* @return RequestInterface
|
||||
* @throws \InvalidArgumentException if the method is not POST
|
||||
*/
|
||||
public static function convertPostToGet(EntityEnclosingRequestInterface $request)
|
||||
{
|
||||
if ($request->getMethod() !== 'POST') {
|
||||
throw new \InvalidArgumentException('Expected a POST request but '
|
||||
. 'received a ' . $request->getMethod() . ' request.');
|
||||
}
|
||||
|
||||
$cloned = RequestFactory::getInstance()
|
||||
->cloneRequestWithMethod($request, 'GET');
|
||||
|
||||
// Move POST fields to the query if they are present
|
||||
foreach ($request->getPostFields() as $name => $value) {
|
||||
$cloned->getQuery()->set($name, $value);
|
||||
}
|
||||
|
||||
return $cloned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the payload part of a signature from a request.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getPayload(RequestInterface $request)
|
||||
{
|
||||
// Calculate the request signature payload
|
||||
if ($request->hasHeader('x-amz-content-sha256')) {
|
||||
// Handle streaming operations (e.g. Glacier.UploadArchive)
|
||||
return (string) $request->getHeader('x-amz-content-sha256');
|
||||
}
|
||||
|
||||
if ($request instanceof EntityEnclosingRequestInterface) {
|
||||
return hash(
|
||||
'sha256',
|
||||
$request->getMethod() == 'POST' && count($request->getPostFields())
|
||||
? (string) $request->getPostFields()
|
||||
: (string) $request->getBody()
|
||||
);
|
||||
}
|
||||
|
||||
return self::DEFAULT_PAYLOAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the payload of a request for use with pre-signed URLs.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getPresignedPayload(RequestInterface $request)
|
||||
{
|
||||
return $this->getPayload($request);
|
||||
}
|
||||
|
||||
protected function createCanonicalizedPath(RequestInterface $request)
|
||||
{
|
||||
$doubleEncoded = rawurlencode(ltrim($request->getPath(), '/'));
|
||||
|
||||
return '/' . str_replace('%2F', '/', $doubleEncoded);
|
||||
}
|
||||
|
||||
private function createStringToSign($longDate, $credentialScope, $creq)
|
||||
{
|
||||
return "AWS4-HMAC-SHA256\n{$longDate}\n{$credentialScope}\n"
|
||||
. hash('sha256', $creq);
|
||||
}
|
||||
|
||||
private function createPresignedRequest(
|
||||
RequestInterface $request,
|
||||
CredentialsInterface $credentials
|
||||
) {
|
||||
$sr = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET');
|
||||
|
||||
// Move POST fields to the query if they are present
|
||||
if ($request instanceof EntityEnclosingRequestInterface) {
|
||||
foreach ($request->getPostFields() as $name => $value) {
|
||||
$sr->getQuery()->set($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure to handle temporary credentials
|
||||
if ($token = $credentials->getSecurityToken()) {
|
||||
$sr->setHeader('X-Amz-Security-Token', $token);
|
||||
$sr->getQuery()->set('X-Amz-Security-Token', $token);
|
||||
}
|
||||
|
||||
$this->moveHeadersToQuery($sr);
|
||||
|
||||
return $sr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the canonical representation of a request
|
||||
*
|
||||
* @param RequestInterface $request Request to canonicalize
|
||||
* @param string $payload Request payload (typically the value
|
||||
* of the x-amz-content-sha256 header.
|
||||
*
|
||||
* @return array Returns an array of context information
|
||||
* @return array Returns an array of context information including:
|
||||
* - canonical_request
|
||||
* - signed_headers
|
||||
*/
|
||||
private function createCanonicalRequest(RequestInterface $request)
|
||||
private function createSigningContext(RequestInterface $request, $payload)
|
||||
{
|
||||
// Normalize the path as required by SigV4 and ensure it's absolute
|
||||
$method = $request->getMethod();
|
||||
$canon = $method . "\n"
|
||||
. '/' . ltrim($request->getUrl(true)->normalizePath()->getPath(), '/') . "\n"
|
||||
$canon = $request->getMethod() . "\n"
|
||||
. $this->createCanonicalizedPath($request) . "\n"
|
||||
. $this->getCanonicalizedQueryString($request) . "\n";
|
||||
|
||||
// Create the canonical headers
|
||||
$headers = array();
|
||||
foreach ($request->getHeaders()->getAll() as $key => $values) {
|
||||
if ($key != 'User-Agent') {
|
||||
$key = strtolower($key);
|
||||
if (!isset($headers[$key])) {
|
||||
$headers[$key] = array();
|
||||
}
|
||||
$key = strtolower($key);
|
||||
if ($key != 'user-agent') {
|
||||
$headers[$key] = array();
|
||||
foreach ($values as $value) {
|
||||
$headers[$key][] = preg_replace('/\s+/', ' ', trim($value));
|
||||
}
|
||||
// Sort the value if there is more than one
|
||||
if (count($values) > 1) {
|
||||
sort($headers[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -192,30 +330,13 @@ class SignatureV4 extends AbstractSignature implements EndpointSignatureInterfac
|
|||
|
||||
// Continue to build the canonical request by adding headers
|
||||
foreach ($headers as $key => $values) {
|
||||
// Combine multi-value headers into a sorted comma separated list
|
||||
if (count($values) > 1) {
|
||||
sort($values);
|
||||
}
|
||||
// Combine multi-value headers into a comma separated list
|
||||
$canon .= $key . ':' . implode(',', $values) . "\n";
|
||||
}
|
||||
|
||||
// Create the signed headers
|
||||
$signedHeaders = implode(';', array_keys($headers));
|
||||
$canon .= "\n{$signedHeaders}\n";
|
||||
|
||||
// Create the payload if this request has an entity body
|
||||
if ($request->hasHeader('x-amz-content-sha256')) {
|
||||
// Handle streaming operations (e.g. Glacier.UploadArchive)
|
||||
$canon .= $request->getHeader('x-amz-content-sha256');
|
||||
} elseif ($request instanceof EntityEnclosingRequestInterface) {
|
||||
$canon .= hash(
|
||||
'sha256',
|
||||
$method == 'POST' && count($request->getPostFields())
|
||||
? (string) $request->getPostFields() : (string) $request->getBody()
|
||||
);
|
||||
} else {
|
||||
$canon .= self::DEFAULT_PAYLOAD;
|
||||
}
|
||||
$canon .= "\n{$signedHeaders}\n{$payload}";
|
||||
|
||||
return array(
|
||||
'canonical_request' => $canon,
|
||||
|
|
@ -253,4 +374,97 @@ class SignatureV4 extends AbstractSignature implements EndpointSignatureInterfac
|
|||
|
||||
return $this->hashCache[$cacheKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the canonicalized query string for a request
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @return string
|
||||
*/
|
||||
private function getCanonicalizedQueryString(RequestInterface $request)
|
||||
{
|
||||
$queryParams = $request->getQuery()->getAll();
|
||||
unset($queryParams['X-Amz-Signature']);
|
||||
if (empty($queryParams)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$qs = '';
|
||||
ksort($queryParams);
|
||||
foreach ($queryParams as $key => $values) {
|
||||
if (is_array($values)) {
|
||||
sort($values);
|
||||
} elseif (!$values) {
|
||||
$values = array('');
|
||||
}
|
||||
|
||||
foreach ((array) $values as $value) {
|
||||
if ($value === QueryString::BLANK) {
|
||||
$value = '';
|
||||
}
|
||||
$qs .= rawurlencode($key) . '=' . rawurlencode($value) . '&';
|
||||
}
|
||||
}
|
||||
|
||||
return substr($qs, 0, -1);
|
||||
}
|
||||
|
||||
private function convertExpires($expires)
|
||||
{
|
||||
if ($expires instanceof \DateTime) {
|
||||
$expires = $expires->getTimestamp();
|
||||
} elseif (!is_numeric($expires)) {
|
||||
$expires = strtotime($expires);
|
||||
}
|
||||
|
||||
$duration = $expires - time();
|
||||
|
||||
// Ensure that the duration of the signature is not longer than a week
|
||||
if ($duration > 604800) {
|
||||
throw new \InvalidArgumentException('The expiration date of a '
|
||||
. 'signature version 4 presigned URL must be less than one '
|
||||
. 'week');
|
||||
}
|
||||
|
||||
return $duration;
|
||||
}
|
||||
|
||||
private function createScope($shortDate, $region, $service)
|
||||
{
|
||||
return $shortDate
|
||||
. '/' . $region
|
||||
. '/' . $service
|
||||
. '/aws4_request';
|
||||
}
|
||||
|
||||
private function addQueryValues(
|
||||
$scope,
|
||||
RequestInterface $request,
|
||||
CredentialsInterface $credentials,
|
||||
$expires
|
||||
) {
|
||||
$credential = $credentials->getAccessKeyId() . '/' . $scope;
|
||||
|
||||
// Set query params required for pre-signed URLs
|
||||
$request->getQuery()
|
||||
->set('X-Amz-Algorithm', 'AWS4-HMAC-SHA256')
|
||||
->set('X-Amz-Credential', $credential)
|
||||
->set('X-Amz-Date', gmdate('Ymd\THis\Z', $this->getTimestamp()))
|
||||
->set('X-Amz-SignedHeaders', 'Host')
|
||||
->set('X-Amz-Expires', $this->convertExpires($expires));
|
||||
}
|
||||
|
||||
private function moveHeadersToQuery(RequestInterface $request)
|
||||
{
|
||||
$query = $request->getQuery();
|
||||
|
||||
foreach ($request->getHeaders() as $name => $header) {
|
||||
if (substr($name, 0, 5) == 'x-amz') {
|
||||
$query[$header->getName()] = (string) $header;
|
||||
}
|
||||
if ($name !== 'host') {
|
||||
$request->removeHeader($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,16 @@ abstract class AbstractWaiter extends AbstractHasDispatcher implements WaiterInt
|
|||
*/
|
||||
public function setConfig(array $config)
|
||||
{
|
||||
if (isset($config['waiter.before_attempt'])) {
|
||||
$this->getEventDispatcher()->addListener('waiter.before_attempt', $config['waiter.before_attempt']);
|
||||
unset($config['waiter.before_attempt']);
|
||||
}
|
||||
|
||||
if (isset($config['waiter.before_wait'])) {
|
||||
$this->getEventDispatcher()->addListener('waiter.before_wait', $config['waiter.before_wait']);
|
||||
unset($config['waiter.before_wait']);
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
|
||||
return $this;
|
||||
|
|
|
|||
|
|
@ -1,141 +0,0 @@
|
|||
# Apache License
|
||||
Version 2.0, January 2004
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
## 1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1
|
||||
through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the
|
||||
License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled
|
||||
by, or are under common control with that entity. For the purposes of this definition, "control" means
|
||||
(i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract
|
||||
or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
|
||||
ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software
|
||||
source code, documentation source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form,
|
||||
including but not limited to compiled object code, generated documentation, and conversions to other media
|
||||
types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License,
|
||||
as indicated by a copyright notice that is included in or attached to the work (an example is provided in the
|
||||
Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from)
|
||||
the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent,
|
||||
as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not
|
||||
include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work
|
||||
and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any
|
||||
modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to
|
||||
Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to
|
||||
submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of
|
||||
electronic, verbal, or written communication sent to the Licensor or its representatives, including but not
|
||||
limited to communication on electronic mailing lists, source code control systems, and issue tracking systems
|
||||
that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been
|
||||
received by Licensor and subsequently incorporated within the Work.
|
||||
|
||||
## 2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
|
||||
Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
## 3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent
|
||||
license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such
|
||||
license applies only to those patent claims licensable by such Contributor that are necessarily infringed by
|
||||
their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such
|
||||
Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim
|
||||
or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work
|
||||
constitutes direct or contributory patent infringement, then any patent licenses granted to You under this
|
||||
License for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
## 4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You meet the following conditions:
|
||||
|
||||
1. You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
||||
|
||||
2. You must cause any modified files to carry prominent notices stating that You changed the files; and
|
||||
|
||||
3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent,
|
||||
trademark, and attribution notices from the Source form of the Work, excluding those notices that do
|
||||
not pertain to any part of the Derivative Works; and
|
||||
|
||||
4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that
|
||||
You distribute must include a readable copy of the attribution notices contained within such NOTICE
|
||||
file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed as part of the Derivative Works; within
|
||||
the Source form or documentation, if provided along with the Derivative Works; or, within a display
|
||||
generated by the Derivative Works, if and wherever such third-party notices normally appear. The
|
||||
contents of the NOTICE file are for informational purposes only and do not modify the License. You may
|
||||
add Your own attribution notices within Derivative Works that You distribute, alongside or as an
|
||||
addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be
|
||||
construed as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide additional or different license
|
||||
terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative
|
||||
Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the
|
||||
conditions stated in this License.
|
||||
|
||||
## 5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by
|
||||
You to the Licensor shall be under the terms and conditions of this License, without any additional terms or
|
||||
conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate
|
||||
license agreement you may have executed with Licensor regarding such Contributions.
|
||||
|
||||
## 6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks, service marks, or product names of
|
||||
the Licensor, except as required for reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
## 7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor
|
||||
provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
|
||||
MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
## 8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless
|
||||
required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any
|
||||
Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential
|
||||
damages of any character arising as a result of this License or out of the use or inability to use the Work
|
||||
(including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has been advised of the possibility
|
||||
of such damages.
|
||||
|
||||
## 9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for,
|
||||
acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole
|
||||
responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold
|
||||
each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
# AWS SDK for PHP
|
||||
|
||||
<http://aws.amazon.com/php>
|
||||
|
||||
Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License").
|
||||
You may not use this file except in compliance with the License.
|
||||
A copy of the License is located at
|
||||
|
||||
<http://aws.amazon.com/apache2.0>
|
||||
|
||||
or in the "license" file accompanying this file. This file is distributed
|
||||
on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
express or implied. See the License for the specific language governing
|
||||
permissions and limitations under the License.
|
||||
|
||||
# Guzzle
|
||||
|
||||
<https://github.com/guzzle/guzzle>
|
||||
|
||||
Copyright (c) 2011 Michael Dowling, https://github.com/mtdowling
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
# Symfony
|
||||
|
||||
<https://github.com/symfony/symfony>
|
||||
|
||||
Copyright (c) 2004-2012 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
# Doctrine Common
|
||||
|
||||
<https://github.com/doctrine/common>
|
||||
|
||||
Copyright (c) 2006-2012 Doctrine Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
# Monolog
|
||||
|
||||
<https://github.com/Seldaek/monolog>
|
||||
|
||||
Copyright (c) Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
namespace Aws\S3\Command;
|
||||
|
||||
use Aws\S3\Exception\RedirectException;
|
||||
use Guzzle\Service\Command\OperationCommand;
|
||||
use Guzzle\Service\Resource\Model;
|
||||
use Guzzle\Common\Event;
|
||||
|
|
|
|||
27
apps/files_external/3rdparty/aws-sdk-php/Aws/S3/Enum/EncodingType.php
vendored
Normal file
27
apps/files_external/3rdparty/aws-sdk-php/Aws/S3/Enum/EncodingType.php
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://aws.amazon.com/apache2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Aws\S3\Enum;
|
||||
|
||||
use Aws\Common\Enum;
|
||||
|
||||
/**
|
||||
* Contains enumerable EncodingType values
|
||||
*/
|
||||
class EncodingType extends Enum
|
||||
{
|
||||
const URL = 'url';
|
||||
}
|
||||
|
|
@ -24,5 +24,5 @@ use Aws\Common\Enum;
|
|||
class Status extends Enum
|
||||
{
|
||||
const ENABLED = 'Enabled';
|
||||
const DISABLED = 'Disabled';
|
||||
const SUSPENDED = 'Suspended';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ use Guzzle\Service\Resource\Model;
|
|||
* Iterator for the S3 ListBuckets command
|
||||
*
|
||||
* This iterator includes the following additional options:
|
||||
* @option bool names_only Set to true to receive only the object/prefix names
|
||||
*
|
||||
* - names_only: Set to true to receive only the object/prefix names
|
||||
*/
|
||||
class ListBucketsIterator extends AwsResourceIterator
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ use Aws\Common\Iterator\AwsResourceIterator;
|
|||
* Iterator for the S3 ListMultipartUploads command
|
||||
*
|
||||
* This iterator includes the following additional options:
|
||||
* @option bool return_prefixes Set to true to return both prefixes and uploads
|
||||
*
|
||||
* - return_prefixes: Set to true to return both prefixes and uploads
|
||||
*/
|
||||
class ListMultipartUploadsIterator extends AwsResourceIterator
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ use Guzzle\Service\Resource\Model;
|
|||
* Iterator for an S3 ListObjectVersions command
|
||||
*
|
||||
* This iterator includes the following additional options:
|
||||
* @option bool return_prefixes Set to true to receive both prefixes and versions in results
|
||||
*
|
||||
* - return_prefixes: Set to true to receive both prefixes and versions in results
|
||||
*/
|
||||
class ListObjectVersionsIterator extends AwsResourceIterator
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,23 +23,21 @@ use Guzzle\Service\Resource\Model;
|
|||
* Iterator for an S3 ListObjects command
|
||||
*
|
||||
* This iterator includes the following additional options:
|
||||
* @option bool return_prefixes Set to true to receive both prefixes and objects in results
|
||||
* @option bool sort_results Set to true to sort mixed (object/prefix) results
|
||||
* @option bool names_only Set to true to receive only the object/prefix names
|
||||
*
|
||||
* - return_prefixes: Set to true to receive both prefixes and objects in results
|
||||
* - sort_results: Set to true to sort mixed (object/prefix) results
|
||||
* - names_only: Set to true to receive only the object/prefix names
|
||||
*/
|
||||
class ListObjectsIterator extends AwsResourceIterator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function handleResults(Model $result)
|
||||
{
|
||||
// Get the list of objects and record the last key
|
||||
$objects = $result->get('Contents') ?: array();
|
||||
$numObjects = count($objects);
|
||||
$lastKey = $numObjects ? $objects[$numObjects - 1]['Key'] : false;
|
||||
if ($lastKey && !$result->hasKey($this->get('token_key'))) {
|
||||
$result->set($this->get('token_key'), $lastKey);
|
||||
if ($lastKey && !$result->hasKey($this->get('output_token'))) {
|
||||
$result->set($this->get('output_token'), $lastKey);
|
||||
}
|
||||
|
||||
// Closure for getting the name of an object or prefix
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
namespace Aws\S3\Model;
|
||||
|
||||
use Aws\Common\Client\AwsClientInterface;
|
||||
use Aws\Common\Iterator\AwsResourceIterator;
|
||||
use Guzzle\Common\AbstractHasDispatcher;
|
||||
use Guzzle\Batch\FlushingBatch;
|
||||
use Guzzle\Batch\ExceptionBufferingBatch;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ use Aws\Common\Enum\UaString as Ua;
|
|||
use Aws\Common\Exception\InvalidArgumentException;
|
||||
use Aws\Common\Model\MultipartUpload\AbstractUploadBuilder;
|
||||
use Aws\S3\Model\Acp;
|
||||
use Guzzle\Common\Collection;
|
||||
|
||||
/**
|
||||
* Easily create a multipart uploader used to quickly and reliably upload a
|
||||
|
|
@ -266,8 +265,10 @@ class UploadBuilder extends AbstractUploadBuilder
|
|||
protected function initiateMultipartUpload()
|
||||
{
|
||||
// Determine Content-Type
|
||||
if ($mimeType = $this->source->getContentType()) {
|
||||
$this->commandOptions['ContentType'] = $mimeType;
|
||||
if (!isset($this->commandOptions['ContentType'])) {
|
||||
if ($mimeType = $this->source->getContentType()) {
|
||||
$this->commandOptions['ContentType'] = $mimeType;
|
||||
}
|
||||
}
|
||||
|
||||
$params = array_replace(array(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
namespace Aws\S3\Model;
|
||||
|
||||
use Aws\Common\Exception\InvalidArgumentException;
|
||||
use Aws\Common\Enum\DateFormat;
|
||||
use Aws\S3\S3Client;
|
||||
use Guzzle\Common\Collection;
|
||||
|
|
@ -68,12 +67,19 @@ class PostObject extends Collection
|
|||
* - key: The location where the file should be uploaded to. The default value is
|
||||
* `^${filename}` which will use the name of the uploaded file
|
||||
* - policy: A raw policy in JSON format. By default, the PostObject creates one for you
|
||||
* - policy_callback: A callback used to modify the policy before encoding and signing it. The
|
||||
* method signature for the callback should accept an array of the policy data as
|
||||
* the 1st argument, (optionally) the PostObject as the 2nd argument, and return
|
||||
* the policy data with the desired modifications.
|
||||
* - success_action_redirect: The URI for Amazon S3 to redirect to upon successful upload
|
||||
* - success_action_status: The status code for Amazon S3 to return upon successful upload
|
||||
* - ttd: The expiration time for the generated upload form data
|
||||
* - x-amz-meta-*: Any custom meta tag that should be set to the object
|
||||
* - x-amz-server-side-encryption: The server-side encryption mechanism to use
|
||||
* - x-amz-storage-class: The storage setting to apply to the object
|
||||
* - x-amz-meta-*: Any custom meta tag that should be set to the object
|
||||
* - x-amz-server-side-encryption-customer-algorithm: The SSE-C algorithm
|
||||
* - x-amz-server-side-encryption-customer-key: The SSE-C customer secret key
|
||||
* - x-amz-server-side-encryption-customer-key-MD5: The MD5 hash of the SSE-C customer secret key
|
||||
*
|
||||
* For the Cache-Control, Content-Disposition, Content-Encoding,
|
||||
* Content-Type, Expires, and key options, to use a "starts-with" comparison
|
||||
|
|
@ -110,9 +116,10 @@ class PostObject extends Collection
|
|||
$ttd = is_numeric($ttd) ? (int) $ttd : strtotime($ttd);
|
||||
unset($options['ttd']);
|
||||
|
||||
// Save policy if passed in
|
||||
$rawPolicy = $options['policy'];
|
||||
unset($options['policy']);
|
||||
// If a policy or policy callback were provided, extract those from the options
|
||||
$rawJsonPolicy = $options['policy'];
|
||||
$policyCallback = $options['policy_callback'];
|
||||
unset($options['policy'], $options['policy_callback']);
|
||||
|
||||
// Setup policy document
|
||||
$policy = array(
|
||||
|
|
@ -122,7 +129,13 @@ class PostObject extends Collection
|
|||
|
||||
// Configure the endpoint/action
|
||||
$url = Url::factory($this->client->getBaseUrl());
|
||||
$url->setHost($this->bucket . '.' . $url->getHost());
|
||||
if ($url->getScheme() === 'https' && strpos($this->bucket, '.') !== false) {
|
||||
// Use path-style URLs
|
||||
$url->setPath($this->bucket);
|
||||
} else {
|
||||
// Use virtual-style URLs
|
||||
$url->setHost($this->bucket . '.' . $url->getHost());
|
||||
}
|
||||
|
||||
// Setup basic form
|
||||
$this->formAttributes = array(
|
||||
|
|
@ -141,7 +154,7 @@ class PostObject extends Collection
|
|||
$policy['conditions'][] = array(
|
||||
'success_action_status' => (string) $status
|
||||
);
|
||||
$options->remove('success_action_status');
|
||||
unset($options['success_action_status']);
|
||||
}
|
||||
|
||||
// Add other options
|
||||
|
|
@ -158,18 +171,10 @@ class PostObject extends Collection
|
|||
}
|
||||
}
|
||||
|
||||
// Add policy
|
||||
$this->jsonPolicy = $rawPolicy ?: json_encode($policy);
|
||||
$jsonPolicy64 = base64_encode($this->jsonPolicy);
|
||||
$this->formInputs['policy'] = $jsonPolicy64;
|
||||
|
||||
// Add signature
|
||||
$this->formInputs['signature'] = base64_encode(hash_hmac(
|
||||
'sha1',
|
||||
$jsonPolicy64,
|
||||
$this->client->getCredentials()->getSecretKey(),
|
||||
true
|
||||
));
|
||||
// Handle the policy
|
||||
$policy = is_callable($policyCallback) ? $policyCallback($policy, $this) : $policy;
|
||||
$this->jsonPolicy = $rawJsonPolicy ?: json_encode($policy);
|
||||
$this->applyPolicy();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -251,4 +256,20 @@ class PostObject extends Collection
|
|||
{
|
||||
return $this->jsonPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the encoding, singing, and injecting of the policy
|
||||
*/
|
||||
protected function applyPolicy()
|
||||
{
|
||||
$jsonPolicy64 = base64_encode($this->jsonPolicy);
|
||||
$this->formInputs['policy'] = $jsonPolicy64;
|
||||
|
||||
$this->formInputs['signature'] = base64_encode(hash_hmac(
|
||||
'sha1',
|
||||
$jsonPolicy64,
|
||||
$this->client->getCredentials()->getSecretKey(),
|
||||
true
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -23,6 +23,8 @@ use Aws\Common\Client\UploadBodyListener;
|
|||
use Aws\Common\Enum\ClientOptions as Options;
|
||||
use Aws\Common\Exception\RuntimeException;
|
||||
use Aws\Common\Exception\InvalidArgumentException;
|
||||
use Aws\Common\Signature\SignatureV4;
|
||||
use Aws\Common\Signature\SignatureInterface;
|
||||
use Aws\Common\Model\MultipartUpload\AbstractTransfer;
|
||||
use Aws\S3\Exception\AccessDeniedException;
|
||||
use Aws\S3\Exception\Parser\S3ExceptionParser;
|
||||
|
|
@ -30,10 +32,8 @@ use Aws\S3\Exception\S3Exception;
|
|||
use Aws\S3\Model\ClearBucket;
|
||||
use Aws\S3\Model\MultipartUpload\AbstractTransfer as AbstractMulti;
|
||||
use Aws\S3\Model\MultipartUpload\UploadBuilder;
|
||||
use Aws\S3\S3Signature;
|
||||
use Aws\S3\Sync\DownloadSyncBuilder;
|
||||
use Aws\S3\Sync\UploadSyncBuilder;
|
||||
use Aws\S3\Sync\AbstractSync;
|
||||
use Guzzle\Common\Collection;
|
||||
use Guzzle\Http\EntityBody;
|
||||
use Guzzle\Http\Message\RequestInterface;
|
||||
|
|
@ -43,7 +43,6 @@ use Guzzle\Plugin\Backoff\CurlBackoffStrategy;
|
|||
use Guzzle\Plugin\Backoff\ExponentialBackoffStrategy;
|
||||
use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
|
||||
use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy;
|
||||
use Guzzle\Plugin\Md5\CommandContentMd5Plugin;
|
||||
use Guzzle\Service\Command\CommandInterface;
|
||||
use Guzzle\Service\Command\Factory\AliasFactory;
|
||||
use Guzzle\Service\Command\Factory\CompositeFactory;
|
||||
|
|
@ -53,6 +52,7 @@ use Guzzle\Service\Resource\ResourceIteratorInterface;
|
|||
/**
|
||||
* Client to interact with Amazon Simple Storage Service
|
||||
*
|
||||
* @method S3SignatureInterface getSignature() Returns the signature implementation used with the client
|
||||
* @method Model abortMultipartUpload(array $args = array()) {@command S3 AbortMultipartUpload}
|
||||
* @method Model completeMultipartUpload(array $args = array()) {@command S3 CompleteMultipartUpload}
|
||||
* @method Model copyObject(array $args = array()) {@command S3 CopyObject}
|
||||
|
|
@ -102,17 +102,17 @@ use Guzzle\Service\Resource\ResourceIteratorInterface;
|
|||
* @method Model restoreObject(array $args = array()) {@command S3 RestoreObject}
|
||||
* @method Model uploadPart(array $args = array()) {@command S3 UploadPart}
|
||||
* @method Model uploadPartCopy(array $args = array()) {@command S3 UploadPartCopy}
|
||||
* @method waitUntilBucketExists(array $input) Wait until a bucket exists. The input array uses the parameters of the HeadBucket operation and waiter specific settings
|
||||
* @method waitUntilBucketNotExists(array $input) Wait until a bucket does not exist. The input array uses the parameters of the HeadBucket operation and waiter specific settings
|
||||
* @method waitUntilObjectExists(array $input) Wait until an object exists. The input array uses the parameters of the HeadObject operation and waiter specific settings
|
||||
* @method waitUntilBucketExists(array $input) The input array uses the parameters of the HeadBucket operation and waiter specific settings
|
||||
* @method waitUntilBucketNotExists(array $input) The input array uses the parameters of the HeadBucket operation and waiter specific settings
|
||||
* @method waitUntilObjectExists(array $input) The input array uses the parameters of the HeadObject operation and waiter specific settings
|
||||
* @method ResourceIteratorInterface getListBucketsIterator(array $args = array()) The input array uses the parameters of the ListBuckets operation
|
||||
* @method ResourceIteratorInterface getListMultipartUploadsIterator(array $args = array()) The input array uses the parameters of the ListMultipartUploads operation
|
||||
* @method ResourceIteratorInterface getListObjectsIterator(array $args = array()) The input array uses the parameters of the ListObjects operation
|
||||
* @method ResourceIteratorInterface getListObjectVersionsIterator(array $args = array()) The input array uses the parameters of the ListObjectVersions operation
|
||||
* @method ResourceIteratorInterface getListObjectsIterator(array $args = array()) The input array uses the parameters of the ListObjects operation
|
||||
* @method ResourceIteratorInterface getListPartsIterator(array $args = array()) The input array uses the parameters of the ListParts operation
|
||||
*
|
||||
* @link http://docs.aws.amazon.com/aws-sdk-php-2/guide/latest/service-s3.html User guide
|
||||
* @link http://docs.aws.amazon.com/aws-sdk-php-2/latest/class-Aws.S3.S3Client.html API docs
|
||||
* @link http://docs.aws.amazon.com/aws-sdk-php/guide/latest/service-s3.html User guide
|
||||
* @link http://docs.aws.amazon.com/aws-sdk-php/latest/class-Aws.S3.S3Client.html API docs
|
||||
*/
|
||||
class S3Client extends AbstractClient
|
||||
{
|
||||
|
|
@ -149,51 +149,15 @@ class S3Client extends AbstractClient
|
|||
'DeleteObjectExpirationConfig' => 'DeleteBucketLifecycle',
|
||||
);
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected $directory = __DIR__;
|
||||
|
||||
/**
|
||||
* Factory method to create a new Amazon S3 client using an array of configuration options.
|
||||
*
|
||||
* The following array keys and values are available options:
|
||||
*
|
||||
* Credential options (key, secret, and optional token OR credentials is required)
|
||||
*
|
||||
* - key - AWS Access Key ID
|
||||
* - secret - AWS secret access key
|
||||
* - credentials - You can optionally provide a custom `Aws\Common\Credentials\CredentialsInterface` object
|
||||
* - token - Custom AWS security token to use with request authentication
|
||||
* - token.ttd - UNIX timestamp for when the custom credentials expire
|
||||
* - credentials.cache - Used to cache credentials when using providers that require HTTP requests. Set the true
|
||||
* to use the default APC cache or provide a `Guzzle\Cache\CacheAdapterInterface` object.
|
||||
* - credentials.cache.key - Optional custom cache key to use with the credentials
|
||||
* - credentials.client - Pass this option to specify a custom `Guzzle\Http\ClientInterface` to use if your
|
||||
* credentials require a HTTP request (e.g. RefreshableInstanceProfileCredentials)
|
||||
*
|
||||
* Region and Endpoint options (a `region` and optional `scheme` OR a `base_url` is required)
|
||||
*
|
||||
* - region - Region name (e.g. 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1', etc...)
|
||||
* - scheme - URI Scheme of the base URL (e.g. 'https', 'http').
|
||||
* - base_url - Instead of using a `region` and `scheme`, you can specify a custom base URL for the client
|
||||
*
|
||||
* Generic client options
|
||||
*
|
||||
* - ssl.certificate_authority: Set to true to use the bundled CA cert (default), system to use the certificate
|
||||
* bundled with your system, or pass the full path to an SSL certificate bundle. This option should be used when
|
||||
* you encounter curl error code 60.
|
||||
* - curl.options - Array of cURL options to apply to every request.
|
||||
* See http://www.php.net/manual/en/function.curl-setopt.php for a list of available options
|
||||
* - signature - You can optionally provide a custom signature implementation used to sign requests
|
||||
* - client.backoff.logger - `Guzzle\Log\LogAdapterInterface` object used to log backoff retries. Use
|
||||
* 'debug' to emit PHP warnings when a retry is issued.
|
||||
* - client.backoff.logger.template - Optional template to use for exponential backoff log messages. See
|
||||
* `Guzzle\Plugin\Backoff\BackoffLogger` for formatting information.
|
||||
*
|
||||
* @param array|Collection $config Client configuration data
|
||||
*
|
||||
* @return self
|
||||
* @link http://docs.aws.amazon.com/aws-sdk-php/guide/latest/configuration.html#client-configuration-options
|
||||
*/
|
||||
public static function factory($config = array())
|
||||
{
|
||||
|
|
@ -201,25 +165,14 @@ class S3Client extends AbstractClient
|
|||
|
||||
// Configure the custom exponential backoff plugin for retrying S3 specific errors
|
||||
if (!isset($config[Options::BACKOFF])) {
|
||||
$config[Options::BACKOFF] = new BackoffPlugin(
|
||||
new TruncatedBackoffStrategy(3,
|
||||
new HttpBackoffStrategy(null,
|
||||
new SocketTimeoutChecker(
|
||||
new CurlBackoffStrategy(null,
|
||||
new ExpiredCredentialsChecker($exceptionParser,
|
||||
new ExponentialBackoffStrategy()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$config[Options::BACKOFF] = self::createBackoffPlugin($exceptionParser);
|
||||
}
|
||||
|
||||
$config[Options::SIGNATURE] = $signature = self::createSignature($config);
|
||||
|
||||
$client = ClientBuilder::factory(__NAMESPACE__)
|
||||
->setConfig($config)
|
||||
->setConfigDefaults(array(
|
||||
Options::SIGNATURE => new S3Signature(),
|
||||
Options::VERSION => self::LATEST_API_VERSION,
|
||||
Options::SERVICE_DESCRIPTION => __DIR__ . '/Resources/s3-%s.php'
|
||||
))
|
||||
|
|
@ -255,16 +208,17 @@ class S3Client extends AbstractClient
|
|||
|
||||
// Use virtual hosted buckets when possible
|
||||
$client->addSubscriber(new BucketStyleListener());
|
||||
|
||||
// Ensure that ACP headers are applied when needed
|
||||
$client->addSubscriber(new AcpListener());
|
||||
|
||||
// Validate and add Content-MD5 hashes
|
||||
$client->addSubscriber(new CommandContentMd5Plugin());
|
||||
// Validate and add required Content-MD5 hashes (e.g. DeleteObjects)
|
||||
$client->addSubscriber(new S3Md5Listener($signature));
|
||||
|
||||
// Allow for specifying bodies with file paths and file handles
|
||||
$client->addSubscriber(new UploadBodyListener(array('PutObject', 'UploadPart')));
|
||||
|
||||
// Ensures that if a SSE-CPK key is provided, the key and md5 are formatted correctly
|
||||
$client->addSubscriber(new SseCpkListener);
|
||||
|
||||
// Add aliases for some S3 operations
|
||||
$default = CompositeFactory::getDefaultChain($client);
|
||||
$default->add(
|
||||
|
|
@ -277,7 +231,66 @@ class S3Client extends AbstractClient
|
|||
}
|
||||
|
||||
/**
|
||||
* Find out if a string is a valid name for an Amazon S3 bucket.
|
||||
* Create an Amazon S3 specific backoff plugin
|
||||
*
|
||||
* @param S3ExceptionParser $exceptionParser
|
||||
*
|
||||
* @return BackoffPlugin
|
||||
*/
|
||||
private static function createBackoffPlugin(S3ExceptionParser $exceptionParser)
|
||||
{
|
||||
return new BackoffPlugin(
|
||||
new TruncatedBackoffStrategy(3,
|
||||
new CurlBackoffStrategy(null,
|
||||
new HttpBackoffStrategy(null,
|
||||
new SocketTimeoutChecker(
|
||||
new ExpiredCredentialsChecker($exceptionParser,
|
||||
new ExponentialBackoffStrategy()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an appropriate signature based on the configuration settings
|
||||
*
|
||||
* @param $config
|
||||
*
|
||||
* @return \Aws\Common\Signature\SignatureInterface
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private static function createSignature($config)
|
||||
{
|
||||
$currentValue = isset($config[Options::SIGNATURE]) ? $config[Options::SIGNATURE] : null;
|
||||
|
||||
// Use the Amazon S3 signature V4 when the value is set to "v4" or when
|
||||
// the value is not set and the region starts with "cn-".
|
||||
if ($currentValue == 'v4' ||
|
||||
(!$currentValue && isset($config['region']) && substr($config['region'], 0, 3) == 'cn-')
|
||||
) {
|
||||
// Force SignatureV4 for specific regions or if specified in the config
|
||||
$currentValue = new S3SignatureV4('s3');
|
||||
} elseif (!$currentValue || $currentValue == 's3') {
|
||||
// Use the Amazon S3 signature by default
|
||||
$currentValue = new S3Signature();
|
||||
}
|
||||
|
||||
// A region is require with v4
|
||||
if ($currentValue instanceof SignatureV4 && !isset($config['region'])) {
|
||||
throw new InvalidArgumentException('A region must be specified '
|
||||
. 'when using signature version 4');
|
||||
}
|
||||
|
||||
return $currentValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a string is a valid name for a DNS compatible Amazon S3
|
||||
* bucket, meaning the bucket can be used as a subdomain in a URL (e.g.,
|
||||
* "<bucket>.s3.amazonaws.com").
|
||||
*
|
||||
* @param string $bucket The name of the bucket to check.
|
||||
*
|
||||
|
|
@ -286,14 +299,12 @@ class S3Client extends AbstractClient
|
|||
public static function isValidBucketName($bucket)
|
||||
{
|
||||
$bucketLen = strlen($bucket);
|
||||
if (!$bucket || $bucketLen < 3 || $bucketLen > 63
|
||||
// Cannot start or end with a '.'
|
||||
|| $bucket[0] == '.'
|
||||
|| $bucket[$bucketLen - 1] == '.'
|
||||
if ($bucketLen < 3 || $bucketLen > 63 ||
|
||||
// Cannot look like an IP address
|
||||
|| preg_match('/^\d+\.\d+\.\d+\.\d+$/', $bucket)
|
||||
preg_match('/(\d+\.){3}\d+$/', $bucket) ||
|
||||
// Cannot include special characters, must start and end with lower alnum
|
||||
|| !preg_match('/^[a-z0-9][a-z0-9\-.]*[a-z0-9]?$/', $bucket)) {
|
||||
!preg_match('/^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$/', $bucket)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -319,36 +330,7 @@ class S3Client extends AbstractClient
|
|||
. 'request object');
|
||||
}
|
||||
|
||||
if ($expires instanceof \DateTime) {
|
||||
$expires = $expires->getTimestamp();
|
||||
} elseif (!is_numeric($expires)) {
|
||||
$expires = strtotime($expires);
|
||||
}
|
||||
|
||||
// Operate on a clone of the request, so the original is not altered
|
||||
$request = clone $request;
|
||||
|
||||
// URL encoding already occurs in the URI template expansion. Undo that and encode using the same encoding as
|
||||
// GET object, PUT object, etc.
|
||||
$path = $this->encodeKey(rawurldecode($request->getPath()));
|
||||
$request->setPath($path);
|
||||
|
||||
// Make sure to handle temporary credentials
|
||||
if ($token = $this->credentials->getSecurityToken()) {
|
||||
$request->setHeader('x-amz-security-token', $token);
|
||||
$request->getQuery()->set('x-amz-security-token', $token);
|
||||
}
|
||||
|
||||
// Set query params required for pre-signed URLs
|
||||
$request->getQuery()
|
||||
->set('AWSAccessKeyId', $this->credentials->getAccessKeyId())
|
||||
->set('Expires', $expires)
|
||||
->set('Signature', $this->signature->signString(
|
||||
$this->signature->createCanonicalizedString($request, $expires),
|
||||
$this->credentials
|
||||
));
|
||||
|
||||
return $request->getUrl();
|
||||
return $this->signature->createPresignedUrl($request, $this->credentials, $expires);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -565,7 +547,13 @@ class S3Client extends AbstractClient
|
|||
*/
|
||||
public function uploadDirectory($directory, $bucket, $keyPrefix = null, array $options = array())
|
||||
{
|
||||
$options = Collection::fromConfig($options, array('base_dir' => $directory));
|
||||
$options = Collection::fromConfig(
|
||||
$options,
|
||||
array(
|
||||
'base_dir' => realpath($directory) ?: $directory
|
||||
)
|
||||
);
|
||||
|
||||
$builder = $options['builder'] ?: UploadSyncBuilder::getInstance();
|
||||
$builder->uploadFromDirectory($directory)
|
||||
->setClient($this)
|
||||
|
|
|
|||
73
apps/files_external/3rdparty/aws-sdk-php/Aws/S3/S3Md5Listener.php
vendored
Normal file
73
apps/files_external/3rdparty/aws-sdk-php/Aws/S3/S3Md5Listener.php
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://aws.amazon.com/apache2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Aws\S3;
|
||||
|
||||
use Aws\Common\Signature\SignatureV4;
|
||||
use Aws\Common\Signature\SignatureInterface;
|
||||
use Guzzle\Common\Event;
|
||||
use Guzzle\Service\Command\CommandInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Adds required and optional Content-MD5 headers
|
||||
*/
|
||||
class S3Md5Listener implements EventSubscriberInterface
|
||||
{
|
||||
/** @var S3SignatureInterface */
|
||||
private $signature;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array('command.after_prepare' => 'onCommandAfterPrepare');
|
||||
}
|
||||
|
||||
public function __construct(SignatureInterface $signature)
|
||||
{
|
||||
$this->signature = $signature;
|
||||
}
|
||||
|
||||
public function onCommandAfterPrepare(Event $event)
|
||||
{
|
||||
$command = $event['command'];
|
||||
$operation = $command->getOperation();
|
||||
|
||||
if ($operation->getData('contentMd5')) {
|
||||
// Add the MD5 if it is required for all signers
|
||||
$this->addMd5($command);
|
||||
} elseif ($operation->hasParam('ContentMD5')) {
|
||||
$value = $command['ContentMD5'];
|
||||
// Add a computed MD5 if the parameter is set to true or if
|
||||
// not using Signature V4 and the value is not set (null).
|
||||
if ($value === true ||
|
||||
($value === null && !($this->signature instanceof SignatureV4))
|
||||
) {
|
||||
$this->addMd5($command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function addMd5(CommandInterface $command)
|
||||
{
|
||||
$request = $command->getRequest();
|
||||
$body = $request->getBody();
|
||||
if ($body && $body->getSize() > 0) {
|
||||
if (false !== ($md5 = $body->getContentMd5(true, true))) {
|
||||
$request->setHeader('Content-MD5', $md5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,39 +17,55 @@
|
|||
namespace Aws\S3;
|
||||
|
||||
use Aws\Common\Credentials\CredentialsInterface;
|
||||
use Aws\Common\Enum\DateFormat;
|
||||
use Guzzle\Http\Message\RequestInterface;
|
||||
use Guzzle\Http\QueryString;
|
||||
use Guzzle\Http\Url;
|
||||
|
||||
/**
|
||||
* Default Amazon S3 signature implementation
|
||||
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html
|
||||
* @link http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
|
||||
*/
|
||||
class S3Signature implements S3SignatureInterface
|
||||
{
|
||||
/**
|
||||
* @var array Query string values that must be signed
|
||||
*/
|
||||
protected $signableQueryString = array(
|
||||
'acl', 'delete', 'lifecycle', 'location', 'logging', 'notification',
|
||||
'partNumber', 'policy', 'requestPayment', 'torrent', 'uploadId',
|
||||
'uploads', 'versionId', 'versioning', 'versions', 'website',
|
||||
'response-cache-control', 'response-content-disposition',
|
||||
'response-content-encoding', 'response-content-language',
|
||||
'response-content-type', 'response-expires', 'restore', 'tagging', 'cors'
|
||||
protected $signableQueryString = array (
|
||||
'acl',
|
||||
'cors',
|
||||
'delete',
|
||||
'lifecycle',
|
||||
'location',
|
||||
'logging',
|
||||
'notification',
|
||||
'partNumber',
|
||||
'policy',
|
||||
'requestPayment',
|
||||
'response-cache-control',
|
||||
'response-content-disposition',
|
||||
'response-content-encoding',
|
||||
'response-content-language',
|
||||
'response-content-type',
|
||||
'response-expires',
|
||||
'restore',
|
||||
'tagging',
|
||||
'torrent',
|
||||
'uploadId',
|
||||
'uploads',
|
||||
'versionId',
|
||||
'versioning',
|
||||
'versions',
|
||||
'website',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array Sorted headers that must be signed
|
||||
*/
|
||||
protected $signableHeaders = array('Content-MD5', 'Content-Type');
|
||||
/** @var array Sorted headers that must be signed */
|
||||
private $signableHeaders = array('Content-MD5', 'Content-Type');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
|
||||
{
|
||||
// Ensure that the signable query string parameters are sorted
|
||||
sort($this->signableQueryString);
|
||||
|
||||
// Add the security token header if one is being used by the credentials
|
||||
if ($token = $credentials->getSecurityToken()) {
|
||||
$request->setHeader('x-amz-security-token', $token);
|
||||
|
|
@ -57,7 +73,7 @@ class S3Signature implements S3SignatureInterface
|
|||
|
||||
// Add a date header if one is not set
|
||||
if (!$request->hasHeader('date') && !$request->hasHeader('x-amz-date')) {
|
||||
$request->setHeader('Date', gmdate(DateFormat::RFC2822));
|
||||
$request->setHeader('Date', gmdate(\DateTime::RFC2822));
|
||||
}
|
||||
|
||||
$stringToSign = $this->createCanonicalizedString($request);
|
||||
|
|
@ -69,17 +85,57 @@ class S3Signature implements S3SignatureInterface
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createPresignedUrl(
|
||||
RequestInterface $request,
|
||||
CredentialsInterface $credentials,
|
||||
$expires
|
||||
) {
|
||||
if ($expires instanceof \DateTime) {
|
||||
$expires = $expires->getTimestamp();
|
||||
} elseif (!is_numeric($expires)) {
|
||||
$expires = strtotime($expires);
|
||||
}
|
||||
|
||||
// Operate on a clone of the request, so the original is not altered
|
||||
$request = clone $request;
|
||||
|
||||
// URL encoding already occurs in the URI template expansion. Undo that and encode using the same encoding as
|
||||
// GET object, PUT object, etc.
|
||||
$path = S3Client::encodeKey(rawurldecode($request->getPath()));
|
||||
$request->setPath($path);
|
||||
|
||||
// Make sure to handle temporary credentials
|
||||
if ($token = $credentials->getSecurityToken()) {
|
||||
$request->setHeader('x-amz-security-token', $token);
|
||||
$request->getQuery()->set('x-amz-security-token', $token);
|
||||
}
|
||||
|
||||
// Set query params required for pre-signed URLs
|
||||
$request->getQuery()
|
||||
->set('AWSAccessKeyId', $credentials->getAccessKeyId())
|
||||
->set('Expires', $expires)
|
||||
->set('Signature', $this->signString(
|
||||
$this->createCanonicalizedString($request, $expires),
|
||||
$credentials
|
||||
));
|
||||
|
||||
// Move X-Amz-* headers to the query string
|
||||
foreach ($request->getHeaders() as $name => $header) {
|
||||
$name = strtolower($name);
|
||||
if (strpos($name, 'x-amz-') === 0) {
|
||||
$request->getQuery()->set($name, (string) $header);
|
||||
$request->removeHeader($name);
|
||||
}
|
||||
}
|
||||
|
||||
return $request->getUrl();
|
||||
}
|
||||
|
||||
public function signString($string, CredentialsInterface $credentials)
|
||||
{
|
||||
return base64_encode(hash_hmac('sha1', $string, $credentials->getSecretKey(), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createCanonicalizedString(RequestInterface $request, $expires = null)
|
||||
{
|
||||
$buffer = $request->getMethod() . "\n";
|
||||
|
|
@ -106,12 +162,11 @@ class S3Signature implements S3SignatureInterface
|
|||
*
|
||||
* @return string Returns canonicalized AMZ headers.
|
||||
*/
|
||||
protected function createCanonicalizedAmzHeaders(RequestInterface $request)
|
||||
private function createCanonicalizedAmzHeaders(RequestInterface $request)
|
||||
{
|
||||
$headers = array();
|
||||
foreach ($request->getHeaders(true) as $header) {
|
||||
/** @var $header \Guzzle\Http\Message\Header */
|
||||
$name = strtolower($header->getName());
|
||||
foreach ($request->getHeaders() as $name => $header) {
|
||||
$name = strtolower($name);
|
||||
if (strpos($name, 'x-amz-') === 0) {
|
||||
$value = trim((string) $header);
|
||||
if ($value || $value === '0') {
|
||||
|
|
@ -120,13 +175,13 @@ class S3Signature implements S3SignatureInterface
|
|||
}
|
||||
}
|
||||
|
||||
if (empty($headers)) {
|
||||
if (!$headers) {
|
||||
return '';
|
||||
} else {
|
||||
ksort($headers);
|
||||
|
||||
return implode("\n", $headers) . "\n";
|
||||
}
|
||||
|
||||
ksort($headers);
|
||||
|
||||
return implode("\n", $headers) . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -136,7 +191,7 @@ class S3Signature implements S3SignatureInterface
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function createCanonicalizedResource(RequestInterface $request)
|
||||
private function createCanonicalizedResource(RequestInterface $request)
|
||||
{
|
||||
$buffer = $request->getParams()->get('s3.resource');
|
||||
// When sending a raw HTTP request (e.g. $client->get())
|
||||
|
|
@ -157,11 +212,17 @@ class S3Signature implements S3SignatureInterface
|
|||
$query = $request->getQuery();
|
||||
$first = true;
|
||||
foreach ($this->signableQueryString as $key) {
|
||||
if ($value = $query->get($key)) {
|
||||
if ($query->hasKey($key)) {
|
||||
$value = $query[$key];
|
||||
$buffer .= $first ? '?' : '&';
|
||||
$first = false;
|
||||
$buffer .= $key;
|
||||
if ($value !== QueryString::BLANK) {
|
||||
// Don't add values for empty sub-resources
|
||||
if ($value !== '' &&
|
||||
$value !== false &&
|
||||
$value !== null &&
|
||||
$value !== QueryString::BLANK
|
||||
) {
|
||||
$buffer .= "={$value}";
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +238,7 @@ class S3Signature implements S3SignatureInterface
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function parseBucketName(RequestInterface $request)
|
||||
private function parseBucketName(RequestInterface $request)
|
||||
{
|
||||
$baseUrl = Url::factory($request->getClient()->getBaseUrl());
|
||||
$baseHost = $baseUrl->getHost();
|
||||
|
|
|
|||
|
|
@ -17,32 +17,8 @@
|
|||
namespace Aws\S3;
|
||||
|
||||
use Aws\Common\Signature\SignatureInterface;
|
||||
use Aws\Common\Credentials\CredentialsInterface;
|
||||
use Guzzle\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Amazon S3 signature interface
|
||||
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html
|
||||
* @deprecated
|
||||
*/
|
||||
interface S3SignatureInterface extends SignatureInterface
|
||||
{
|
||||
/**
|
||||
* Sign a string for Amazon S3
|
||||
*
|
||||
* @param string $string String to sign
|
||||
* @param CredentialsInterface $credentials Credentials used to sign
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function signString($string, CredentialsInterface $credentials);
|
||||
|
||||
/**
|
||||
* Create a canonicalized string for a signature.
|
||||
*
|
||||
* @param RequestInterface $request Base on the request
|
||||
* @param string $expires Pass a UNIX timestamp if creating a query signature
|
||||
*
|
||||
* @return string Returns a canonicalized string for an Amazon S3 signature.
|
||||
*/
|
||||
public function createCanonicalizedString(RequestInterface $request, $expires = null);
|
||||
}
|
||||
interface S3SignatureInterface extends SignatureInterface {}
|
||||
|
|
|
|||
64
apps/files_external/3rdparty/aws-sdk-php/Aws/S3/S3SignatureV4.php
vendored
Normal file
64
apps/files_external/3rdparty/aws-sdk-php/Aws/S3/S3SignatureV4.php
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://aws.amazon.com/apache2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Aws\S3;
|
||||
|
||||
use Aws\Common\Signature\SignatureV4;
|
||||
use Aws\Common\Credentials\CredentialsInterface;
|
||||
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
|
||||
use Guzzle\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Amazon S3 signature version 4 overrides.
|
||||
*/
|
||||
class S3SignatureV4 extends SignatureV4 implements S3SignatureInterface
|
||||
{
|
||||
/**
|
||||
* Always add a x-amz-content-sha-256 for data integrity.
|
||||
*/
|
||||
public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
|
||||
{
|
||||
if (!$request->hasHeader('x-amz-content-sha256')) {
|
||||
$request->setHeader('x-amz-content-sha256', $this->getPresignedPayload($request));
|
||||
}
|
||||
|
||||
parent::signRequest($request, $credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override used to allow pre-signed URLs to be created for an
|
||||
* in-determinate request payload.
|
||||
*/
|
||||
protected function getPresignedPayload(RequestInterface $request)
|
||||
{
|
||||
$result = parent::getPresignedPayload($request);
|
||||
|
||||
// If the body is empty, then sign with 'UNSIGNED-PAYLOAD'
|
||||
if ($result === self::DEFAULT_PAYLOAD) {
|
||||
$result = hash('sha256', 'UNSIGNED-PAYLOAD');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Amazon S3 does not double-encode the path component in the canonical req
|
||||
*/
|
||||
protected function createCanonicalizedPath(RequestInterface $request)
|
||||
{
|
||||
return '/' . ltrim($request->getPath(), '/');
|
||||
}
|
||||
}
|
||||
|
|
@ -65,18 +65,6 @@ class SocketTimeoutChecker extends AbstractBackoffStrategy
|
|||
&& $response->getStatusCode() == 400
|
||||
&& strpos($response->getBody(), self::ERR)
|
||||
) {
|
||||
// Check if the request is sending a local file, and if so, clear the stat cache and recalculate the size.
|
||||
if ($request instanceof EntityEnclosingRequestInterface) {
|
||||
if ($request->getBody()->getWrapper() == 'plainfile') {
|
||||
$filename = $request->getBody()->getUri();
|
||||
// Clear the cache so that we send accurate file sizes
|
||||
clearstatcache(true, $filename);
|
||||
$length = filesize($filename);
|
||||
$request->getBody()->setSize($length);
|
||||
$request->setHeader('Content-Length', $length);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
68
apps/files_external/3rdparty/aws-sdk-php/Aws/S3/SseCpkListener.php
vendored
Normal file
68
apps/files_external/3rdparty/aws-sdk-php/Aws/S3/SseCpkListener.php
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Aws\S3;
|
||||
|
||||
use Aws\Common\Exception\RuntimeException;
|
||||
use Guzzle\Common\Event;
|
||||
use Guzzle\Service\Command\CommandInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* This listener simplifies the SSE-C process by encoding and hashing the key.
|
||||
*/
|
||||
class SseCpkListener implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array('command.before_prepare' => 'onCommandBeforePrepare');
|
||||
}
|
||||
|
||||
public function onCommandBeforePrepare(Event $event)
|
||||
{
|
||||
/** @var CommandInterface $command */
|
||||
$command = $event['command'];
|
||||
|
||||
// Allows only HTTPS connections when using SSE-C
|
||||
if ($command['SSECustomerKey'] ||
|
||||
$command['CopySourceSSECustomerKey']
|
||||
) {
|
||||
$this->validateScheme($command);
|
||||
}
|
||||
|
||||
// Prepare the normal SSE-CPK headers
|
||||
if ($command['SSECustomerKey']) {
|
||||
$this->prepareSseParams($command);
|
||||
}
|
||||
|
||||
// If it's a copy operation, prepare the SSE-CPK headers for the source.
|
||||
if ($command['CopySourceSSECustomerKey']) {
|
||||
$this->prepareSseParams($command, true);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateScheme(CommandInterface $command)
|
||||
{
|
||||
if ($command->getClient()->getConfig('scheme') !== 'https') {
|
||||
throw new RuntimeException('You must configure your S3 client to '
|
||||
. 'use HTTPS in order to use the SSE-C features.');
|
||||
}
|
||||
}
|
||||
|
||||
private function prepareSseParams(
|
||||
CommandInterface $command,
|
||||
$isCopy = false
|
||||
) {
|
||||
$prefix = $isCopy ? 'CopySource' : '';
|
||||
|
||||
// Base64 encode the provided key
|
||||
$key = $command[$prefix . 'SSECustomerKey'];
|
||||
$command[$prefix . 'SSECustomerKey'] = base64_encode($key);
|
||||
|
||||
// Base64 the provided MD5 or, generate an MD5 if not provided
|
||||
if ($md5 = $command[$prefix . 'SSECustomerKeyMD5']) {
|
||||
$command[$prefix . 'SSECustomerKeyMD5'] = base64_encode($md5);
|
||||
} else {
|
||||
$command[$prefix . 'SSECustomerKeyMD5'] = base64_encode(md5($key, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,9 +20,10 @@ use Aws\Common\Exception\RuntimeException;
|
|||
use Aws\S3\Exception\S3Exception;
|
||||
use Aws\S3\Exception\NoSuchKeyException;
|
||||
use Aws\S3\Iterator\ListObjectsIterator;
|
||||
use Guzzle\Http\QueryString;
|
||||
use Guzzle\Http\EntityBody;
|
||||
use Guzzle\Http\CachingEntityBody;
|
||||
use Guzzle\Http\Mimetypes;
|
||||
use Guzzle\Iterator\FilterIterator;
|
||||
use Guzzle\Stream\PhpStreamRequestFactory;
|
||||
use Guzzle\Service\Command\CommandInterface;
|
||||
|
||||
|
|
@ -71,7 +72,6 @@ use Guzzle\Service\Command\CommandInterface;
|
|||
* Stream context options:
|
||||
*
|
||||
* - "seekable": Set to true to create a seekable "r" (read only) stream by using a php://temp stream buffer
|
||||
* - "throw_exceptions": Set to true to throw exceptions instead of trigger_errors
|
||||
* - For "unlink" only: Any option that can be passed to the DeleteObject operation
|
||||
*/
|
||||
class StreamWrapper
|
||||
|
|
@ -132,7 +132,7 @@ class StreamWrapper
|
|||
stream_wrapper_unregister('s3');
|
||||
}
|
||||
|
||||
stream_wrapper_register('s3', __CLASS__, STREAM_IS_URL);
|
||||
stream_wrapper_register('s3', get_called_class(), STREAM_IS_URL);
|
||||
self::$client = $client;
|
||||
}
|
||||
|
||||
|
|
@ -172,8 +172,8 @@ class StreamWrapper
|
|||
}
|
||||
|
||||
// When using mode "x" validate if the file exists before attempting to read
|
||||
if ($mode == 'x' && !self::$client->doesObjectExist($params['Bucket'], $params['Key'], $this->getOptions())) {
|
||||
$errors[] = "{$path} does not exist on Amazon S3";
|
||||
if ($mode == 'x' && self::$client->doesObjectExist($params['Bucket'], $params['Key'], $this->getOptions())) {
|
||||
$errors[] = "{$path} already exists on Amazon S3";
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
|
|
@ -210,6 +210,14 @@ class StreamWrapper
|
|||
$params = $this->params;
|
||||
$params['Body'] = $this->body;
|
||||
|
||||
// Attempt to guess the ContentType of the upload based on the
|
||||
// file extension of the key
|
||||
if (!isset($params['ContentType']) &&
|
||||
($type = Mimetypes::getInstance()->fromFilename($params['Key']))
|
||||
) {
|
||||
$params['ContentType'] = $type;
|
||||
}
|
||||
|
||||
try {
|
||||
self::$client->putObject($params);
|
||||
return true;
|
||||
|
|
@ -314,25 +322,30 @@ class StreamWrapper
|
|||
|
||||
$parts = $this->getParams($path);
|
||||
|
||||
// Stat a bucket or just s3://
|
||||
if (!$parts['Key'] && (!$parts['Bucket'] || self::$client->doesBucketExist($parts['Bucket']))) {
|
||||
return $this->formatUrlStat($path);
|
||||
}
|
||||
|
||||
// You must pass either a bucket or a bucket + key
|
||||
if (!$parts['Key']) {
|
||||
return $this->triggerError("File or directory not found: {$path}", $flags);
|
||||
// Stat "directories": buckets, or "s3://"
|
||||
if (!$parts['Bucket'] || self::$client->doesBucketExist($parts['Bucket'])) {
|
||||
return $this->formatUrlStat($path);
|
||||
} else {
|
||||
return $this->triggerError("File or directory not found: {$path}", $flags);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
// Attempt to stat and cache regular object
|
||||
return $this->formatUrlStat(self::$client->headObject($parts)->toArray());
|
||||
$result = self::$client->headObject($parts)->toArray();
|
||||
if (substr($parts['Key'], -1, 1) == '/' && $result['ContentLength'] == 0) {
|
||||
// Return as if it is a bucket to account for console bucket objects (e.g., zero-byte object "foo/")
|
||||
return $this->formatUrlStat($path);
|
||||
} else {
|
||||
// Attempt to stat and cache regular object
|
||||
return $this->formatUrlStat($result);
|
||||
}
|
||||
} catch (NoSuchKeyException $e) {
|
||||
// Maybe this isn't an actual key, but a prefix. Do a prefix listing of objects to determine.
|
||||
$result = self::$client->listObjects(array(
|
||||
'Bucket' => $parts['Bucket'],
|
||||
'Prefix' => $parts['Key'],
|
||||
'Prefix' => rtrim($parts['Key'], '/') . '/',
|
||||
'MaxKeys' => 1
|
||||
));
|
||||
if (!$result['Contents'] && !$result['CommonPrefixes']) {
|
||||
|
|
@ -352,7 +365,7 @@ class StreamWrapper
|
|||
* @param string $path Directory which should be created.
|
||||
* @param int $mode Permissions. 700-range permissions map to ACL_PUBLIC. 600-range permissions map to
|
||||
* ACL_AUTH_READ. All other permissions map to ACL_PRIVATE. Expects octal form.
|
||||
* @param int $options A bitwise mask of values, such as STREAM_MKDIR_RECURSIVE. (unused)
|
||||
* @param int $options A bitwise mask of values, such as STREAM_MKDIR_RECURSIVE.
|
||||
*
|
||||
* @return bool
|
||||
* @link http://www.php.net/manual/en/streamwrapper.mkdir.php
|
||||
|
|
@ -360,28 +373,17 @@ class StreamWrapper
|
|||
public function mkdir($path, $mode, $options)
|
||||
{
|
||||
$params = $this->getParams($path);
|
||||
$this->clearStatInfo($path);
|
||||
|
||||
if (!$params['Bucket'] || $params['Key']) {
|
||||
if (!$params['Bucket']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!isset($params['ACL'])) {
|
||||
$mode = decoct($mode);
|
||||
if ($mode >= 700 and $mode <= 799) {
|
||||
$params['ACL'] = 'public-read';
|
||||
} elseif ($mode >= 600 && $mode <= 699) {
|
||||
$params['ACL'] = 'authenticated-read';
|
||||
} else {
|
||||
$params['ACL'] = 'private';
|
||||
}
|
||||
}
|
||||
self::$client->createBucket($params);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return $this->triggerError($e->getMessage());
|
||||
if (!isset($params['ACL'])) {
|
||||
$params['ACL'] = $this->determineAcl($mode);
|
||||
}
|
||||
|
||||
return !isset($params['Key']) || $params['Key'] === '/'
|
||||
? $this->createBucket($path, $params)
|
||||
: $this->createPseudoDirectory($path, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -397,14 +399,40 @@ class StreamWrapper
|
|||
$params = $this->getParams($path);
|
||||
if (!$params['Bucket']) {
|
||||
return $this->triggerError('You cannot delete s3://. Please specify a bucket.');
|
||||
} elseif ($params['Key']) {
|
||||
return $this->triggerError('rmdir() only supports bucket deletion');
|
||||
}
|
||||
|
||||
try {
|
||||
self::$client->deleteBucket(array('Bucket' => $params['Bucket']));
|
||||
$this->clearStatInfo($path);
|
||||
return true;
|
||||
|
||||
if (!$params['Key']) {
|
||||
self::$client->deleteBucket(array('Bucket' => $params['Bucket']));
|
||||
$this->clearStatInfo($path);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use a key that adds a trailing slash if needed.
|
||||
$prefix = rtrim($params['Key'], '/') . '/';
|
||||
|
||||
$result = self::$client->listObjects(array(
|
||||
'Bucket' => $params['Bucket'],
|
||||
'Prefix' => $prefix,
|
||||
'MaxKeys' => 1
|
||||
));
|
||||
|
||||
// Check if the bucket contains keys other than the placeholder
|
||||
if ($result['Contents']) {
|
||||
foreach ($result['Contents'] as $key) {
|
||||
if ($key['Key'] == $prefix) {
|
||||
continue;
|
||||
}
|
||||
return $this->triggerError('Psuedo folder is not empty');
|
||||
}
|
||||
return $this->unlink(rtrim($path, '/') . '/');
|
||||
}
|
||||
|
||||
return $result['CommonPrefixes']
|
||||
? $this->triggerError('Pseudo folder contains nested folders')
|
||||
: true;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->triggerError($e->getMessage());
|
||||
}
|
||||
|
|
@ -413,6 +441,11 @@ class StreamWrapper
|
|||
/**
|
||||
* Support for opendir().
|
||||
*
|
||||
* The opendir() method of the Amazon S3 stream wrapper supports a stream
|
||||
* context option of "listFilter". listFilter must be a callable that
|
||||
* accepts an associative array of object data and returns true if the
|
||||
* object should be yielded when iterating the keys in a bucket.
|
||||
*
|
||||
* @param string $path The path to the directory (e.g. "s3://dir[</prefix>]")
|
||||
* @param string $options Whether or not to enforce safe_mode (0x04). Unused.
|
||||
*
|
||||
|
|
@ -425,14 +458,14 @@ class StreamWrapper
|
|||
$this->clearStatInfo();
|
||||
$params = $this->getParams($path);
|
||||
$delimiter = $this->getOption('delimiter');
|
||||
$filterFn = $this->getOption('listFilter');
|
||||
|
||||
if ($delimiter === null) {
|
||||
$delimiter = '/';
|
||||
}
|
||||
|
||||
if ($params['Key']) {
|
||||
$suffix = $delimiter ?: '/';
|
||||
$params['Key'] = rtrim($params['Key'], $suffix) . $suffix;
|
||||
$params['Key'] = rtrim($params['Key'], $delimiter) . $delimiter;
|
||||
}
|
||||
|
||||
$this->openedBucket = $params['Bucket'];
|
||||
|
|
@ -443,11 +476,22 @@ class StreamWrapper
|
|||
$operationParams['Delimiter'] = $delimiter;
|
||||
}
|
||||
|
||||
$this->objectIterator = self::$client->getIterator('ListObjects', $operationParams, array(
|
||||
$objectIterator = self::$client->getIterator('ListObjects', $operationParams, array(
|
||||
'return_prefixes' => true,
|
||||
'sort_results' => true
|
||||
));
|
||||
|
||||
// Filter our "/" keys added by the console as directories, and ensure
|
||||
// that if a filter function is provided that it passes the filter.
|
||||
$this->objectIterator = new FilterIterator(
|
||||
$objectIterator,
|
||||
function ($key) use ($filterFn) {
|
||||
// Each yielded results can contain a "Key" or "Prefix"
|
||||
return (!$filterFn || call_user_func($filterFn, $key)) &&
|
||||
(!isset($key['Key']) || substr($key['Key'], -1, 1) !== '/');
|
||||
}
|
||||
);
|
||||
|
||||
$this->objectIterator->next();
|
||||
|
||||
return true;
|
||||
|
|
@ -487,26 +531,32 @@ class StreamWrapper
|
|||
*/
|
||||
public function dir_readdir()
|
||||
{
|
||||
$result = false;
|
||||
if ($this->objectIterator->valid()) {
|
||||
$current = $this->objectIterator->current();
|
||||
if (isset($current['Prefix'])) {
|
||||
// Include "directories"
|
||||
$result = str_replace($this->openedBucketPrefix, '', $current['Prefix']);
|
||||
$key = "s3://{$this->openedBucket}/{$current['Prefix']}";
|
||||
$stat = $this->formatUrlStat($current['Prefix']);
|
||||
} else {
|
||||
// Remove the prefix from the result to emulate other stream wrappers
|
||||
$result = str_replace($this->openedBucketPrefix, '', $current['Key']);
|
||||
$key = "s3://{$this->openedBucket}/{$current['Key']}";
|
||||
$stat = $this->formatUrlStat($current);
|
||||
}
|
||||
|
||||
// Cache the object data for quick url_stat lookups used with RecursiveDirectoryIterator
|
||||
self::$nextStat = array($key => $stat);
|
||||
$this->objectIterator->next();
|
||||
// Skip empty result keys
|
||||
if (!$this->objectIterator->valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$current = $this->objectIterator->current();
|
||||
if (isset($current['Prefix'])) {
|
||||
// Include "directories". Be sure to strip a trailing "/"
|
||||
// on prefixes.
|
||||
$prefix = rtrim($current['Prefix'], '/');
|
||||
$result = str_replace($this->openedBucketPrefix, '', $prefix);
|
||||
$key = "s3://{$this->openedBucket}/{$prefix}";
|
||||
$stat = $this->formatUrlStat($prefix);
|
||||
} else {
|
||||
// Remove the prefix from the result to emulate other
|
||||
// stream wrappers.
|
||||
$result = str_replace($this->openedBucketPrefix, '', $current['Key']);
|
||||
$key = "s3://{$this->openedBucket}/{$current['Key']}";
|
||||
$stat = $this->formatUrlStat($current);
|
||||
}
|
||||
|
||||
// Cache the object data for quick url_stat lookups used with
|
||||
// RecursiveDirectoryIterator.
|
||||
self::$nextStat = array($key => $stat);
|
||||
$this->objectIterator->next();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
@ -602,7 +652,6 @@ class StreamWrapper
|
|||
|
||||
$params = $this->getOptions();
|
||||
unset($params['seekable']);
|
||||
unset($params['throw_exceptions']);
|
||||
|
||||
return array(
|
||||
'Bucket' => $parts[0],
|
||||
|
|
@ -695,14 +744,19 @@ class StreamWrapper
|
|||
*/
|
||||
protected function triggerError($errors, $flags = null)
|
||||
{
|
||||
if ($flags != STREAM_URL_STAT_QUIET) {
|
||||
if ($this->getOption('throw_exceptions')) {
|
||||
throw new RuntimeException(implode("\n", (array) $errors));
|
||||
} else {
|
||||
trigger_error(implode("\n", (array) $errors), E_USER_WARNING);
|
||||
}
|
||||
if ($flags & STREAM_URL_STAT_QUIET) {
|
||||
// This is triggered with things like file_exists()
|
||||
|
||||
if ($flags & STREAM_URL_STAT_LINK) {
|
||||
// This is triggered for things like is_link()
|
||||
return $this->formatUrlStat(false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is triggered when doing things like lstat() or stat()
|
||||
trigger_error(implode("\n", (array) $errors), E_USER_WARNING);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -732,19 +786,18 @@ class StreamWrapper
|
|||
);
|
||||
|
||||
$stat = $statTemplate;
|
||||
$type = gettype($result);
|
||||
|
||||
// Determine what type of data is being cached
|
||||
if (!$result || is_string($result)) {
|
||||
if ($type == 'NULL' || $type == 'string') {
|
||||
// Directory with 0777 access - see "man 2 stat".
|
||||
$stat['mode'] = $stat[2] = 0040777;
|
||||
} elseif (is_array($result) && isset($result['LastModified'])) {
|
||||
} elseif ($type == 'array' && isset($result['LastModified'])) {
|
||||
// ListObjects or HeadObject result
|
||||
$stat['mtime'] = $stat[9] = $stat['ctime'] = $stat[10] = strtotime($result['LastModified']);
|
||||
$stat['size'] = $stat[7] = (isset($result['ContentLength']) ? $result['ContentLength'] : $result['Size']);
|
||||
// Regular file with 0777 access - see "man 2 stat".
|
||||
$stat['mode'] = $stat[2] = 0100777;
|
||||
} else {
|
||||
$stat['mode'] = $stat[2] = 0100777;
|
||||
}
|
||||
|
||||
return $stat;
|
||||
|
|
@ -762,4 +815,77 @@ class StreamWrapper
|
|||
clearstatcache(true, $path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bucket for the given parameters.
|
||||
*
|
||||
* @param string $path Stream wrapper path
|
||||
* @param array $params A result of StreamWrapper::getParams()
|
||||
*
|
||||
* @return bool Returns true on success or false on failure
|
||||
*/
|
||||
private function createBucket($path, array $params)
|
||||
{
|
||||
if (self::$client->doesBucketExist($params['Bucket'])) {
|
||||
return $this->triggerError("Directory already exists: {$path}");
|
||||
}
|
||||
|
||||
try {
|
||||
self::$client->createBucket($params);
|
||||
$this->clearStatInfo($path);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return $this->triggerError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a pseudo-folder by creating an empty "/" suffixed key
|
||||
*
|
||||
* @param string $path Stream wrapper path
|
||||
* @param array $params A result of StreamWrapper::getParams()
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function createPseudoDirectory($path, array $params)
|
||||
{
|
||||
// Ensure the path ends in "/" and the body is empty.
|
||||
$params['Key'] = rtrim($params['Key'], '/') . '/';
|
||||
$params['Body'] = '';
|
||||
|
||||
// Fail if this pseudo directory key already exists
|
||||
if (self::$client->doesObjectExist($params['Bucket'], $params['Key'])) {
|
||||
return $this->triggerError("Directory already exists: {$path}");
|
||||
}
|
||||
|
||||
try {
|
||||
self::$client->putObject($params);
|
||||
$this->clearStatInfo($path);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return $this->triggerError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the most appropriate ACL based on a file mode.
|
||||
*
|
||||
* @param int $mode File mode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function determineAcl($mode)
|
||||
{
|
||||
$mode = decoct($mode);
|
||||
|
||||
if ($mode >= 700 && $mode <= 799) {
|
||||
return 'public-read';
|
||||
}
|
||||
|
||||
if ($mode >= 600 && $mode <= 699) {
|
||||
return 'authenticated-read';
|
||||
}
|
||||
|
||||
return 'private';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,8 @@
|
|||
namespace Aws\S3\Sync;
|
||||
|
||||
use Aws\S3\S3Client;
|
||||
use Aws\S3\Model\MultipartUpload\AbstractTransfer;
|
||||
use Guzzle\Common\AbstractHasDispatcher;
|
||||
use Guzzle\Common\Collection;
|
||||
use Guzzle\Http\EntityBody;
|
||||
use Guzzle\Iterator\ChunkedIterator;
|
||||
use Guzzle\Service\Command\CommandInterface;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ namespace Aws\S3\Sync;
|
|||
|
||||
use Aws\Common\Exception\RuntimeException;
|
||||
use Aws\Common\Exception\UnexpectedValueException;
|
||||
use Aws\Common\Model\MultipartUpload\AbstractTransfer;
|
||||
use Aws\Common\Model\MultipartUpload\TransferInterface;
|
||||
use Aws\S3\S3Client;
|
||||
use Aws\S3\Iterator\OpendirIterator;
|
||||
|
|
@ -169,7 +168,8 @@ abstract class AbstractSyncBuilder
|
|||
*/
|
||||
public function setKeyPrefix($keyPrefix)
|
||||
{
|
||||
$this->keyPrefix = $keyPrefix;
|
||||
// Removing leading slash
|
||||
$this->keyPrefix = ltrim($keyPrefix, '/');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -275,6 +275,7 @@ abstract class AbstractSyncBuilder
|
|||
|
||||
// Only wrap the source iterator in a changed files iterator if we are not forcing the transfers
|
||||
if (!$this->forcing) {
|
||||
$this->sourceIterator->rewind();
|
||||
$this->sourceIterator = new ChangedFilesIterator(
|
||||
new \NoRewindIterator($this->sourceIterator),
|
||||
$this->getTargetIterator(),
|
||||
|
|
@ -391,11 +392,6 @@ abstract class AbstractSyncBuilder
|
|||
function (Event $e) use ($params) {
|
||||
if ($e['command'] instanceof CommandInterface) {
|
||||
$e['command']->overwriteWith($params);
|
||||
} elseif ($e['command'] instanceof TransferInterface) {
|
||||
// Multipart upload transfer object
|
||||
foreach ($params as $k => $v) {
|
||||
$e['command']->setOption($k, $v);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -410,12 +406,30 @@ abstract class AbstractSyncBuilder
|
|||
{
|
||||
// Ensure that the stream wrapper is registered
|
||||
$this->client->registerStreamWrapper();
|
||||
// Calculate the opendir() bucket and optional key prefix location
|
||||
// Remove the delimiter as it is not needed for this
|
||||
$dir = rtrim('s3://' . $this->bucket . ($this->keyPrefix ? ('/' . $this->keyPrefix) : ''), '/');
|
||||
// Use opendir so that we can pass stream context to the iterator
|
||||
$dh = opendir($dir, stream_context_create(array('s3' => array('delimiter' => ''))));
|
||||
|
||||
return $this->filterIterator(new \NoRewindIterator(new OpendirIterator($dh, $dir . '/')));
|
||||
// Calculate the opendir() bucket and optional key prefix location
|
||||
$dir = "s3://{$this->bucket}";
|
||||
if ($this->keyPrefix) {
|
||||
$dir .= '/' . ltrim($this->keyPrefix, '/ ');
|
||||
}
|
||||
|
||||
// Use opendir so that we can pass stream context to the iterator
|
||||
$dh = opendir($dir, stream_context_create(array(
|
||||
's3' => array(
|
||||
'delimiter' => '',
|
||||
'listFilter' => function ($obj) {
|
||||
// Ensure that we do not try to download a glacier object.
|
||||
return !isset($obj['StorageClass']) ||
|
||||
$obj['StorageClass'] != 'GLACIER';
|
||||
}
|
||||
)
|
||||
)));
|
||||
|
||||
// Add the trailing slash for the OpendirIterator concatenation
|
||||
if (!$this->keyPrefix) {
|
||||
$dir .= '/';
|
||||
}
|
||||
|
||||
return $this->filterIterator(new \NoRewindIterator(new OpendirIterator($dh, $dir)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
namespace Aws\S3\Sync;
|
||||
|
||||
use Aws\S3\S3Client;
|
||||
|
||||
/**
|
||||
* Iterator used to filter an internal iterator to only yield files that do not exist in the target iterator or files
|
||||
* that have changed
|
||||
|
|
@ -60,7 +58,7 @@ class ChangedFilesIterator extends \FilterIterator
|
|||
public function accept()
|
||||
{
|
||||
$current = $this->current();
|
||||
$key = $this->sourceConverter->convert((string) $current);
|
||||
$key = $this->sourceConverter->convert($this->normalize($current));
|
||||
if (!($data = $this->getTargetData($key))) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -88,6 +86,8 @@ class ChangedFilesIterator extends \FilterIterator
|
|||
*/
|
||||
protected function getTargetData($key)
|
||||
{
|
||||
$key = $this->cleanKey($key);
|
||||
|
||||
if (isset($this->cache[$key])) {
|
||||
$result = $this->cache[$key];
|
||||
unset($this->cache[$key]);
|
||||
|
|
@ -99,14 +99,27 @@ class ChangedFilesIterator extends \FilterIterator
|
|||
while ($it->valid()) {
|
||||
$value = $it->current();
|
||||
$data = array($value->getSize(), $value->getMTime());
|
||||
$filename = $this->targetConverter->convert((string) $value);
|
||||
$filename = $this->targetConverter->convert($this->normalize($value));
|
||||
$filename = $this->cleanKey($filename);
|
||||
|
||||
if ($filename == $key) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$this->cache[$filename] = $data;
|
||||
$it->next();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function normalize($current)
|
||||
{
|
||||
return $current->getRealPath() ?: (string) $current;
|
||||
}
|
||||
|
||||
private function cleanKey($key)
|
||||
{
|
||||
return ltrim($key, '/');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,6 @@ namespace Aws\S3\Sync;
|
|||
|
||||
use Aws\Common\Exception\RuntimeException;
|
||||
use Aws\S3\ResumableDownload;
|
||||
use Aws\S3\S3Client;
|
||||
use Aws\S3\Model\MultipartUpload\AbstractTransfer;
|
||||
use Guzzle\Http\EntityBody;
|
||||
|
||||
/**
|
||||
* Downloads and Amazon S3 bucket to a local directory
|
||||
|
|
@ -31,8 +28,7 @@ class DownloadSync extends AbstractSync
|
|||
{
|
||||
$sourceFilename = $file->getPathname();
|
||||
list($bucket, $key) = explode('/', substr($sourceFilename, 5), 2);
|
||||
$filename = '/' . ltrim($this->options['source_converter']->convert($sourceFilename), '/');
|
||||
|
||||
$filename = $this->options['source_converter']->convert($sourceFilename);
|
||||
$this->createDirectory($filename);
|
||||
|
||||
// Some S3 buckets contains nested files under the same name as a directory
|
||||
|
|
|
|||
|
|
@ -17,9 +17,7 @@
|
|||
namespace Aws\S3\Sync;
|
||||
|
||||
use Aws\Common\Exception\RuntimeException;
|
||||
use Aws\Common\Model\MultipartUpload\AbstractTransfer;
|
||||
use Aws\S3\ResumableDownload;
|
||||
use Aws\S3\S3Client;
|
||||
use Guzzle\Common\Event;
|
||||
use Guzzle\Http\EntityBodyInterface;
|
||||
use Guzzle\Service\Command\CommandInterface;
|
||||
|
|
|
|||
|
|
@ -37,20 +37,31 @@ class KeyConverter implements FilenameConverterInterface
|
|||
*/
|
||||
public function __construct($baseDir = '', $prefix = '', $delimiter = '/')
|
||||
{
|
||||
$this->baseDir = $baseDir;
|
||||
$this->baseDir = (string) $baseDir;
|
||||
$this->prefix = $prefix;
|
||||
$this->delimiter = $delimiter;
|
||||
}
|
||||
|
||||
public function convert($filename)
|
||||
{
|
||||
// Remove base directory from the key
|
||||
$key = str_replace($this->baseDir, '', $filename);
|
||||
$key = $filename;
|
||||
|
||||
// Remove base directory from the key (only the first occurrence)
|
||||
if ($this->baseDir && (false !== $pos = strpos($filename, $this->baseDir))) {
|
||||
$key = substr_replace($key, '', $pos, strlen($this->baseDir));
|
||||
}
|
||||
|
||||
// Replace Windows directory separators to become Unix style, and convert that to the custom dir separator
|
||||
$key = str_replace('/', $this->delimiter, str_replace('\\', '/', $key));
|
||||
// Add the key prefix and remove double slashes
|
||||
$key = str_replace($this->delimiter . $this->delimiter, $this->delimiter, $this->prefix . $key);
|
||||
|
||||
return ltrim($key, $this->delimiter);
|
||||
// Add the key prefix and remove double slashes that are not in the protocol (e.g. prefixed with ":")
|
||||
$delim = preg_quote($this->delimiter);
|
||||
$key = preg_replace(
|
||||
"#(?<!:){$delim}{$delim}#",
|
||||
$this->delimiter,
|
||||
$this->prefix . $key
|
||||
);
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
namespace Aws\S3\Sync;
|
||||
|
||||
use Aws\Common\Exception\RuntimeException;
|
||||
use Aws\S3\S3Client;
|
||||
use Aws\S3\Model\MultipartUpload\UploadBuilder;
|
||||
use Aws\S3\Model\MultipartUpload\AbstractTransfer;
|
||||
use Guzzle\Http\EntityBody;
|
||||
|
|
@ -27,6 +26,8 @@ use Guzzle\Http\EntityBody;
|
|||
*/
|
||||
class UploadSync extends AbstractSync
|
||||
{
|
||||
const BEFORE_MULTIPART_BUILD = 's3.sync.before_multipart_build';
|
||||
|
||||
protected function init()
|
||||
{
|
||||
if (null == $this->options['multipart_upload_size']) {
|
||||
|
|
@ -37,10 +38,11 @@ class UploadSync extends AbstractSync
|
|||
protected function createTransferAction(\SplFileInfo $file)
|
||||
{
|
||||
// Open the file for reading
|
||||
$filename = $file->getPathName();
|
||||
$filename = $file->getRealPath() ?: $file->getPathName();
|
||||
|
||||
if (!($resource = fopen($filename, 'r'))) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new RuntimeException("Could not open {$filename} for reading");
|
||||
throw new RuntimeException('Could not open ' . $file->getPathname() . ' for reading');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
|
|
@ -57,15 +59,21 @@ class UploadSync extends AbstractSync
|
|||
|
||||
// Use a multi-part upload if the file is larger than the cutoff size and is a regular file
|
||||
if ($body->getWrapper() == 'plainfile' && $file->getSize() >= $this->options['multipart_upload_size']) {
|
||||
return UploadBuilder::newInstance()
|
||||
$builder = UploadBuilder::newInstance()
|
||||
->setBucket($this->options['bucket'])
|
||||
->setKey($key)
|
||||
->setMinPartSize($this->options['multipart_upload_size'])
|
||||
->setOption($aclType, $acl)
|
||||
->setClient($this->options['client'])
|
||||
->setSource($body)
|
||||
->setConcurrency($this->options['concurrency'])
|
||||
->build();
|
||||
->setConcurrency($this->options['concurrency']);
|
||||
|
||||
$this->dispatch(
|
||||
self::BEFORE_MULTIPART_BUILD,
|
||||
array('builder' => $builder, 'file' => $file)
|
||||
);
|
||||
|
||||
return $builder->build();
|
||||
}
|
||||
|
||||
return $this->options['client']->getCommand('PutObject', array(
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Aws\S3\Sync;
|
|||
use \FilesystemIterator as FI;
|
||||
use Aws\Common\Model\MultipartUpload\AbstractTransfer;
|
||||
use Aws\S3\Model\Acp;
|
||||
use Aws\S3\S3Client;
|
||||
use Guzzle\Common\HasDispatcherInterface;
|
||||
use Guzzle\Common\Event;
|
||||
use Guzzle\Service\Command\CommandInterface;
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ class UploadSyncBuilder extends AbstractSyncBuilder
|
|||
*/
|
||||
public function uploadFromDirectory($path)
|
||||
{
|
||||
$this->baseDir = $path;
|
||||
$this->baseDir = realpath($path);
|
||||
$this->sourceIterator = $this->filterIterator(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(
|
||||
$path,
|
||||
FI::SKIP_DOTS | FI::UNIX_PATHS | FI::FOLLOW_SYMLINKS
|
||||
|
|
@ -125,6 +125,21 @@ class UploadSyncBuilder extends AbstractSyncBuilder
|
|||
return $sync;
|
||||
}
|
||||
|
||||
protected function addCustomParamListener(HasDispatcherInterface $sync)
|
||||
{
|
||||
// Handle the special multi-part upload event
|
||||
parent::addCustomParamListener($sync);
|
||||
$params = $this->params;
|
||||
$sync->getEventDispatcher()->addListener(
|
||||
UploadSync::BEFORE_MULTIPART_BUILD,
|
||||
function (Event $e) use ($params) {
|
||||
foreach ($params as $k => $v) {
|
||||
$e['builder']->setOption($k, $v);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function getTargetIterator()
|
||||
{
|
||||
return $this->createS3Iterator();
|
||||
|
|
@ -167,7 +182,7 @@ class UploadSyncBuilder extends AbstractSyncBuilder
|
|||
$size = $command['Body']->getContentLength();
|
||||
$percentage = number_format(($progress / $totalSize) * 100, 2);
|
||||
fwrite($resource, "- Part {$command['PartNumber']} ({$size} bytes, {$percentage}%)\n");
|
||||
$progress .= $size;
|
||||
$progress += $size;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* APC cache provider.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author David Abdemoulaie <dave@hobodave.com>
|
||||
*/
|
||||
class ApcCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
return apc_fetch($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return apc_exists($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
return (bool) apc_store($id, $data, (int) $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return apc_delete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
return apc_clear_cache() && apc_clear_cache('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
$info = apc_cache_info();
|
||||
$sma = apc_sma_info();
|
||||
|
||||
return array(
|
||||
Cache::STATS_HITS => $info['num_hits'],
|
||||
Cache::STATS_MISSES => $info['num_misses'],
|
||||
Cache::STATS_UPTIME => $info['start_time'],
|
||||
Cache::STATS_MEMORY_USAGE => $info['mem_size'],
|
||||
Cache::STATS_MEMORY_AVAILIABLE => $sma['avail_mem'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Array cache driver.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author David Abdemoulaie <dave@hobodave.com>
|
||||
*/
|
||||
class ArrayCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var array $data
|
||||
*/
|
||||
private $data = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
return (isset($this->data[$id])) ? $this->data[$id] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return isset($this->data[$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
$this->data[$id] = $data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
unset($this->data[$id]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
$this->data = array();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Interface for cache drivers.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
interface Cache
|
||||
{
|
||||
const STATS_HITS = 'hits';
|
||||
const STATS_MISSES = 'misses';
|
||||
const STATS_UPTIME = 'uptime';
|
||||
const STATS_MEMORY_USAGE = 'memory_usage';
|
||||
const STATS_MEMORY_AVAILIABLE = 'memory_available';
|
||||
|
||||
/**
|
||||
* Fetches an entry from the cache.
|
||||
*
|
||||
* @param string $id cache id The id of the cache entry to fetch.
|
||||
* @return mixed The cached data or FALSE, if no cache entry exists for the given id.
|
||||
*/
|
||||
function fetch($id);
|
||||
|
||||
/**
|
||||
* Test if an entry exists in the cache.
|
||||
*
|
||||
* @param string $id cache id The cache id of the entry to check for.
|
||||
* @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise.
|
||||
*/
|
||||
function contains($id);
|
||||
|
||||
/**
|
||||
* Puts data into the cache.
|
||||
*
|
||||
* @param string $id The cache id.
|
||||
* @param mixed $data The cache entry/data.
|
||||
* @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime).
|
||||
* @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise.
|
||||
*/
|
||||
function save($id, $data, $lifeTime = 0);
|
||||
|
||||
/**
|
||||
* Deletes a cache entry.
|
||||
*
|
||||
* @param string $id cache id
|
||||
* @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise.
|
||||
*/
|
||||
function delete($id);
|
||||
|
||||
/**
|
||||
* Retrieves cached information from data store
|
||||
*
|
||||
* The server's statistics array has the following values:
|
||||
*
|
||||
* - <b>hits</b>
|
||||
* Number of keys that have been requested and found present.
|
||||
*
|
||||
* - <b>misses</b>
|
||||
* Number of items that have been requested and not found.
|
||||
*
|
||||
* - <b>uptime</b>
|
||||
* Time that the server is running.
|
||||
*
|
||||
* - <b>memory_usage</b>
|
||||
* Memory used by this server to store items.
|
||||
*
|
||||
* - <b>memory_available</b>
|
||||
* Memory allowed to use for storage.
|
||||
*
|
||||
* @since 2.2
|
||||
* @return array Associative array with server's statistics if available, NULL otherwise.
|
||||
*/
|
||||
function getStats();
|
||||
}
|
||||
|
|
@ -1,231 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Base class for cache provider implementations.
|
||||
*
|
||||
* @since 2.2
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
abstract class CacheProvider implements Cache
|
||||
{
|
||||
const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]';
|
||||
|
||||
/**
|
||||
* @var string The namespace to prefix all cache ids with
|
||||
*/
|
||||
private $namespace = '';
|
||||
|
||||
/**
|
||||
* @var string The namespace version
|
||||
*/
|
||||
private $namespaceVersion;
|
||||
|
||||
/**
|
||||
* Set the namespace to prefix all cache ids with.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return void
|
||||
*/
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
$this->namespace = (string) $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the namespace that prefixes all cache ids.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($id)
|
||||
{
|
||||
return $this->doFetch($this->getNamespacedId($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function contains($id)
|
||||
{
|
||||
return $this->doContains($this->getNamespacedId($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($id, $data, $lifeTime = 0)
|
||||
{
|
||||
return $this->doSave($this->getNamespacedId($id), $data, $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
return $this->doDelete($this->getNamespacedId($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStats()
|
||||
{
|
||||
return $this->doGetStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all cache entries.
|
||||
*
|
||||
* @return boolean TRUE if the cache entries were successfully flushed, FALSE otherwise.
|
||||
*/
|
||||
public function flushAll()
|
||||
{
|
||||
return $this->doFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all cache entries.
|
||||
*
|
||||
* @return boolean TRUE if the cache entries were successfully deleted, FALSE otherwise.
|
||||
*/
|
||||
public function deleteAll()
|
||||
{
|
||||
$namespaceCacheKey = $this->getNamespaceCacheKey();
|
||||
$namespaceVersion = $this->getNamespaceVersion() + 1;
|
||||
|
||||
$this->namespaceVersion = $namespaceVersion;
|
||||
|
||||
return $this->doSave($namespaceCacheKey, $namespaceVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefix the passed id with the configured namespace value
|
||||
*
|
||||
* @param string $id The id to namespace
|
||||
* @return string $id The namespaced id
|
||||
*/
|
||||
private function getNamespacedId($id)
|
||||
{
|
||||
$namespaceVersion = $this->getNamespaceVersion();
|
||||
|
||||
return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespace cache key
|
||||
*
|
||||
* @return string $namespaceCacheKey
|
||||
*/
|
||||
private function getNamespaceCacheKey()
|
||||
{
|
||||
return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespace version
|
||||
*
|
||||
* @return string $namespaceVersion
|
||||
*/
|
||||
private function getNamespaceVersion()
|
||||
{
|
||||
if (null !== $this->namespaceVersion) {
|
||||
return $this->namespaceVersion;
|
||||
}
|
||||
|
||||
$namespaceCacheKey = $this->getNamespaceCacheKey();
|
||||
$namespaceVersion = $this->doFetch($namespaceCacheKey);
|
||||
|
||||
if (false === $namespaceVersion) {
|
||||
$namespaceVersion = 1;
|
||||
|
||||
$this->doSave($namespaceCacheKey, $namespaceVersion);
|
||||
}
|
||||
|
||||
$this->namespaceVersion = $namespaceVersion;
|
||||
|
||||
return $this->namespaceVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an entry from the cache.
|
||||
*
|
||||
* @param string $id cache id The id of the cache entry to fetch.
|
||||
* @return string The cached data or FALSE, if no cache entry exists for the given id.
|
||||
*/
|
||||
abstract protected function doFetch($id);
|
||||
|
||||
/**
|
||||
* Test if an entry exists in the cache.
|
||||
*
|
||||
* @param string $id cache id The cache id of the entry to check for.
|
||||
* @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise.
|
||||
*/
|
||||
abstract protected function doContains($id);
|
||||
|
||||
/**
|
||||
* Puts data into the cache.
|
||||
*
|
||||
* @param string $id The cache id.
|
||||
* @param string $data The cache entry/data.
|
||||
* @param bool|int $lifeTime The lifetime. If != false, sets a specific lifetime for this
|
||||
* cache entry (null => infinite lifeTime).
|
||||
*
|
||||
* @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise.
|
||||
*/
|
||||
abstract protected function doSave($id, $data, $lifeTime = false);
|
||||
|
||||
/**
|
||||
* Deletes a cache entry.
|
||||
*
|
||||
* @param string $id cache id
|
||||
* @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise.
|
||||
*/
|
||||
abstract protected function doDelete($id);
|
||||
|
||||
/**
|
||||
* Deletes all cache entries.
|
||||
*
|
||||
* @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise.
|
||||
*/
|
||||
abstract protected function doFlush();
|
||||
|
||||
/**
|
||||
* Retrieves cached information from data store
|
||||
*
|
||||
* @since 2.2
|
||||
* @return array An associative array with server's statistics if available, NULL otherwise.
|
||||
*/
|
||||
abstract protected function doGetStats();
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
use \Couchbase;
|
||||
|
||||
/**
|
||||
* Couchbase cache provider.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.4
|
||||
* @author Michael Nitschinger <michael@nitschinger.at>
|
||||
*/
|
||||
class CouchbaseCache extends CacheProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Couchbase
|
||||
*/
|
||||
private $couchbase;
|
||||
|
||||
/**
|
||||
* Sets the Couchbase instance to use.
|
||||
*
|
||||
* @param Couchbase $couchbase
|
||||
*/
|
||||
public function setCouchbase(Couchbase $couchbase)
|
||||
{
|
||||
$this->couchbase = $couchbase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Couchbase instance used by the cache.
|
||||
*
|
||||
* @return Couchbase
|
||||
*/
|
||||
public function getCouchbase()
|
||||
{
|
||||
return $this->couchbase;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
return $this->couchbase->get($id) ?: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return (null !== $this->couchbase->get($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
if ($lifeTime > 30 * 24 * 3600) {
|
||||
$lifeTime = time() + $lifeTime;
|
||||
}
|
||||
return $this->couchbase->set($id, $data, (int) $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->couchbase->delete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
return $this->couchbase->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
$stats = $this->couchbase->getStats();
|
||||
$servers = $this->couchbase->getServers();
|
||||
$server = explode(":", $servers[0]);
|
||||
$key = $server[0] . ":" . "11210";
|
||||
$stats = $stats[$key];
|
||||
return array(
|
||||
Cache::STATS_HITS => $stats['get_hits'],
|
||||
Cache::STATS_MISSES => $stats['get_misses'],
|
||||
Cache::STATS_UPTIME => $stats['uptime'],
|
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
|
||||
Cache::STATS_MEMORY_AVAILIABLE => $stats['limit_maxbytes'],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Base file cache driver.
|
||||
*
|
||||
* @since 2.3
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
abstract class FileCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var string Cache directory.
|
||||
*/
|
||||
protected $directory;
|
||||
|
||||
/**
|
||||
* @var string Cache file extension.
|
||||
*/
|
||||
protected $extension;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $directory Cache directory.
|
||||
* @param string $directory Cache file extension.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct($directory, $extension = null)
|
||||
{
|
||||
if ( ! is_dir($directory) && ! @mkdir($directory, 0777, true)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'The directory "%s" does not exist and could not be created.',
|
||||
$directory
|
||||
));
|
||||
}
|
||||
|
||||
if ( ! is_writable($directory)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'The directory "%s" is not writable.',
|
||||
$directory
|
||||
));
|
||||
}
|
||||
|
||||
$this->directory = realpath($directory);
|
||||
$this->extension = $extension ?: $this->extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDirectory()
|
||||
{
|
||||
return $this->directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache file extension.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExtension()
|
||||
{
|
||||
return $this->extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getFilename($id)
|
||||
{
|
||||
$path = implode(str_split(md5($id), 12), DIRECTORY_SEPARATOR);
|
||||
$path = $this->directory . DIRECTORY_SEPARATOR . $path;
|
||||
|
||||
return $path . DIRECTORY_SEPARATOR . $id . $this->extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return @unlink($this->getFilename($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
$pattern = '/^.+\\' . $this->extension . '$/i';
|
||||
$iterator = new \RecursiveDirectoryIterator($this->directory);
|
||||
$iterator = new \RecursiveIteratorIterator($iterator);
|
||||
$iterator = new \RegexIterator($iterator, $pattern);
|
||||
|
||||
foreach ($iterator as $name => $file) {
|
||||
@unlink($name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Filesystem cache driver.
|
||||
*
|
||||
* @since 2.3
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
class FilesystemCache extends FileCache
|
||||
{
|
||||
const EXTENSION = '.doctrinecache.data';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $extension = self::EXTENSION;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
$data = '';
|
||||
$lifetime = -1;
|
||||
$filename = $this->getFilename($id);
|
||||
|
||||
if ( ! is_file($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$resource = fopen($filename, "r");
|
||||
|
||||
if (false !== ($line = fgets($resource))) {
|
||||
$lifetime = (integer) $line;
|
||||
}
|
||||
|
||||
if ($lifetime !== 0 && $lifetime < time()) {
|
||||
fclose($resource);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
while (false !== ($line = fgets($resource))) {
|
||||
$data .= $line;
|
||||
}
|
||||
|
||||
fclose($resource);
|
||||
|
||||
return unserialize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
$lifetime = -1;
|
||||
$filename = $this->getFilename($id);
|
||||
|
||||
if ( ! is_file($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$resource = fopen($filename, "r");
|
||||
|
||||
if (false !== ($line = fgets($resource))) {
|
||||
$lifetime = (integer) $line;
|
||||
}
|
||||
|
||||
fclose($resource);
|
||||
|
||||
return $lifetime === 0 || $lifetime > time();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
if ($lifeTime > 0) {
|
||||
$lifeTime = time() + $lifeTime;
|
||||
}
|
||||
|
||||
$data = serialize($data);
|
||||
$filename = $this->getFilename($id);
|
||||
$filepath = pathinfo($filename, PATHINFO_DIRNAME);
|
||||
|
||||
if ( ! is_dir($filepath)) {
|
||||
mkdir($filepath, 0777, true);
|
||||
}
|
||||
|
||||
return file_put_contents($filename, $lifeTime . PHP_EOL . $data);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
use \Memcache;
|
||||
|
||||
/**
|
||||
* Memcache cache provider.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author David Abdemoulaie <dave@hobodave.com>
|
||||
*/
|
||||
class MemcacheCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var Memcache
|
||||
*/
|
||||
private $memcache;
|
||||
|
||||
/**
|
||||
* Sets the memcache instance to use.
|
||||
*
|
||||
* @param Memcache $memcache
|
||||
*/
|
||||
public function setMemcache(Memcache $memcache)
|
||||
{
|
||||
$this->memcache = $memcache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the memcache instance used by the cache.
|
||||
*
|
||||
* @return Memcache
|
||||
*/
|
||||
public function getMemcache()
|
||||
{
|
||||
return $this->memcache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
return $this->memcache->get($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return (bool) $this->memcache->get($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
if ($lifeTime > 30 * 24 * 3600) {
|
||||
$lifeTime = time() + $lifeTime;
|
||||
}
|
||||
return $this->memcache->set($id, $data, 0, (int) $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->memcache->delete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
return $this->memcache->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
$stats = $this->memcache->getStats();
|
||||
return array(
|
||||
Cache::STATS_HITS => $stats['get_hits'],
|
||||
Cache::STATS_MISSES => $stats['get_misses'],
|
||||
Cache::STATS_UPTIME => $stats['uptime'],
|
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
|
||||
Cache::STATS_MEMORY_AVAILIABLE => $stats['limit_maxbytes'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
use \Memcached;
|
||||
|
||||
/**
|
||||
* Memcached cache provider.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.2
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author David Abdemoulaie <dave@hobodave.com>
|
||||
*/
|
||||
class MemcachedCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var Memcached
|
||||
*/
|
||||
private $memcached;
|
||||
|
||||
/**
|
||||
* Sets the memcache instance to use.
|
||||
*
|
||||
* @param Memcached $memcached
|
||||
*/
|
||||
public function setMemcached(Memcached $memcached)
|
||||
{
|
||||
$this->memcached = $memcached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the memcached instance used by the cache.
|
||||
*
|
||||
* @return Memcached
|
||||
*/
|
||||
public function getMemcached()
|
||||
{
|
||||
return $this->memcached;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
return $this->memcached->get($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return (false !== $this->memcached->get($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
if ($lifeTime > 30 * 24 * 3600) {
|
||||
$lifeTime = time() + $lifeTime;
|
||||
}
|
||||
return $this->memcached->set($id, $data, (int) $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->memcached->delete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
return $this->memcached->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
$stats = $this->memcached->getStats();
|
||||
$servers = $this->memcached->getServerList();
|
||||
$key = $servers[0]['host'] . ':' . $servers[0]['port'];
|
||||
$stats = $stats[$key];
|
||||
return array(
|
||||
Cache::STATS_HITS => $stats['get_hits'],
|
||||
Cache::STATS_MISSES => $stats['get_misses'],
|
||||
Cache::STATS_UPTIME => $stats['uptime'],
|
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
|
||||
Cache::STATS_MEMORY_AVAILIABLE => $stats['limit_maxbytes'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Php file cache driver.
|
||||
*
|
||||
* @since 2.3
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
class PhpFileCache extends FileCache
|
||||
{
|
||||
const EXTENSION = '.doctrinecache.php';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $extension = self::EXTENSION;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
$filename = $this->getFilename($id);
|
||||
|
||||
if ( ! is_file($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = include $filename;
|
||||
|
||||
if ($value['lifetime'] !== 0 && $value['lifetime'] < time()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $value['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
$filename = $this->getFilename($id);
|
||||
|
||||
if ( ! is_file($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = include $filename;
|
||||
|
||||
return $value['lifetime'] === 0 || $value['lifetime'] > time();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
if ($lifeTime > 0) {
|
||||
$lifeTime = time() + $lifeTime;
|
||||
}
|
||||
|
||||
if (is_object($data) && ! method_exists($data, '__set_state')) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Invalid argument given, PhpFileCache only allows objects that implement __set_state() " .
|
||||
"and fully support var_export(). You can use the FilesystemCache to save arbitrary object " .
|
||||
"graphs using serialize()/deserialize()."
|
||||
);
|
||||
}
|
||||
|
||||
$filename = $this->getFilename($id);
|
||||
$filepath = pathinfo($filename, PATHINFO_DIRNAME);
|
||||
|
||||
if ( ! is_dir($filepath)) {
|
||||
mkdir($filepath, 0777, true);
|
||||
}
|
||||
|
||||
$value = array(
|
||||
'lifetime' => $lifeTime,
|
||||
'data' => $data
|
||||
);
|
||||
|
||||
$value = var_export($value, true);
|
||||
$code = sprintf('<?php return %s;', $value);
|
||||
|
||||
return file_put_contents($filename, $code);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
use Redis;
|
||||
|
||||
/**
|
||||
* Redis cache provider.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.2
|
||||
* @author Osman Ungur <osmanungur@gmail.com>
|
||||
*/
|
||||
class RedisCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var Redis
|
||||
*/
|
||||
private $redis;
|
||||
|
||||
/**
|
||||
* Sets the redis instance to use.
|
||||
*
|
||||
* @param Redis $redis
|
||||
*/
|
||||
public function setRedis(Redis $redis)
|
||||
{
|
||||
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);
|
||||
$this->redis = $redis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the redis instance used by the cache.
|
||||
*
|
||||
* @return Redis
|
||||
*/
|
||||
public function getRedis()
|
||||
{
|
||||
return $this->redis;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
return $this->redis->get($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return $this->redis->exists($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
$result = $this->redis->set($id, $data);
|
||||
if ($lifeTime > 0) {
|
||||
$this->redis->expire($id, $lifeTime);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->redis->delete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
return $this->redis->flushDB();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
$info = $this->redis->info();
|
||||
return array(
|
||||
Cache::STATS_HITS => false,
|
||||
Cache::STATS_MISSES => false,
|
||||
Cache::STATS_UPTIME => $info['uptime_in_seconds'],
|
||||
Cache::STATS_MEMORY_USAGE => $info['used_memory'],
|
||||
Cache::STATS_MEMORY_AVAILIABLE => false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* WinCache cache provider.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.2
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author David Abdemoulaie <dave@hobodave.com>
|
||||
*/
|
||||
class WinCacheCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
return wincache_ucache_get($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return wincache_ucache_exists($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
return (bool) wincache_ucache_set($id, $data, (int) $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return wincache_ucache_delete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
return wincache_ucache_clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
$info = wincache_ucache_info();
|
||||
$meminfo = wincache_ucache_meminfo();
|
||||
|
||||
return array(
|
||||
Cache::STATS_HITS => $info['total_hit_count'],
|
||||
Cache::STATS_MISSES => $info['total_miss_count'],
|
||||
Cache::STATS_UPTIME => $info['total_cache_uptime'],
|
||||
Cache::STATS_MEMORY_USAGE => $meminfo['memory_total'],
|
||||
Cache::STATS_MEMORY_AVAILIABLE => $meminfo['memory_free'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Xcache cache driver.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author David Abdemoulaie <dave@hobodave.com>
|
||||
*/
|
||||
class XcacheCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
return $this->doContains($id) ? unserialize(xcache_get($id)) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return xcache_isset($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
return xcache_set($id, serialize($data), (int) $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return xcache_unset($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
$this->checkAuthorization();
|
||||
|
||||
xcache_clear_cache(XC_TYPE_VAR, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that xcache.admin.enable_auth is Off
|
||||
*
|
||||
* @throws \BadMethodCallException When xcache.admin.enable_auth is On
|
||||
* @return void
|
||||
*/
|
||||
protected function checkAuthorization()
|
||||
{
|
||||
if (ini_get('xcache.admin.enable_auth')) {
|
||||
throw new \BadMethodCallException('To use all features of \Doctrine\Common\Cache\XcacheCache, you must set "xcache.admin.enable_auth" to "Off" in your php.ini.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
$this->checkAuthorization();
|
||||
|
||||
$info = xcache_info(XC_TYPE_VAR, 0);
|
||||
return array(
|
||||
Cache::STATS_HITS => $info['hits'],
|
||||
Cache::STATS_MISSES => $info['misses'],
|
||||
Cache::STATS_UPTIME => null,
|
||||
Cache::STATS_MEMORY_USAGE => $info['size'],
|
||||
Cache::STATS_MEMORY_AVAILIABLE => $info['avail'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Zend Data Cache cache driver.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Ralph Schindler <ralph.schindler@zend.com>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
*/
|
||||
class ZendDataCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
return zend_shm_cache_fetch($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return (false !== zend_shm_cache_fetch($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
return zend_shm_cache_store($id, $data, $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return zend_shm_cache_delete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
$namespace = $this->getNamespace();
|
||||
if (empty($namespace)) {
|
||||
return zend_shm_cache_clear();
|
||||
}
|
||||
return zend_shm_cache_clear($namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
31
apps/files_external/3rdparty/aws-sdk-php/Guzzle/Batch/composer.json
vendored
Normal file
31
apps/files_external/3rdparty/aws-sdk-php/Guzzle/Batch/composer.json
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "guzzle/batch",
|
||||
"description": "Guzzle batch component for batching requests, commands, or custom transfers",
|
||||
"homepage": "http://guzzlephp.org/",
|
||||
"keywords": ["batch", "HTTP", "REST", "guzzle"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.2",
|
||||
"guzzle/common": "self.version"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Guzzle\\Batch": "" }
|
||||
},
|
||||
"suggest": {
|
||||
"guzzle/http": "self.version",
|
||||
"guzzle/service": "self.version"
|
||||
},
|
||||
"target-dir": "Guzzle/Batch",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.7-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ namespace Guzzle\Cache;
|
|||
use Doctrine\Common\Cache\Cache;
|
||||
use Guzzle\Common\Version;
|
||||
use Guzzle\Common\Exception\InvalidArgumentException;
|
||||
use Guzzle\Common\Exception\RuntimeException;
|
||||
use Guzzle\Common\FromConfigInterface;
|
||||
use Zend\Cache\Storage\StorageInterface;
|
||||
|
||||
|
|
|
|||
27
apps/files_external/3rdparty/aws-sdk-php/Guzzle/Cache/composer.json
vendored
Normal file
27
apps/files_external/3rdparty/aws-sdk-php/Guzzle/Cache/composer.json
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "guzzle/cache",
|
||||
"description": "Guzzle cache adapter component",
|
||||
"homepage": "http://guzzlephp.org/",
|
||||
"keywords": ["cache", "adapter", "zf", "doctrine", "guzzle"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.2",
|
||||
"guzzle/common": "self.version"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Guzzle\\Cache": "" }
|
||||
},
|
||||
"target-dir": "Guzzle/Cache",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.7-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ class AbstractHasDispatcher implements HasDispatcherInterface
|
|||
|
||||
public function dispatch($eventName, array $context = array())
|
||||
{
|
||||
$this->getEventDispatcher()->dispatch($eventName, new Event($context));
|
||||
return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
|
||||
}
|
||||
|
||||
public function addSubscriber(EventSubscriberInterface $subscriber)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,15 @@ class ExceptionCollection extends \Exception implements GuzzleException, \Iterat
|
|||
/** @var array Array of Exceptions */
|
||||
protected $exceptions = array();
|
||||
|
||||
/** @var string Succinct exception message not including sub-exceptions */
|
||||
private $shortMessage;
|
||||
|
||||
public function __construct($message = '', $code = 0, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->shortMessage = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all of the exceptions
|
||||
*
|
||||
|
|
@ -36,19 +45,12 @@ class ExceptionCollection extends \Exception implements GuzzleException, \Iterat
|
|||
*/
|
||||
public function add($e)
|
||||
{
|
||||
$this->exceptions[] = $e;
|
||||
if ($this->message) {
|
||||
$this->message .= "\n";
|
||||
}
|
||||
|
||||
if ($e instanceof self) {
|
||||
$this->message .= '(' . get_class($e) . ")";
|
||||
foreach (explode("\n", $e->getMessage()) as $message) {
|
||||
$this->message .= "\n {$message}";
|
||||
}
|
||||
} elseif ($e instanceof \Exception) {
|
||||
$this->exceptions[] = $e;
|
||||
$this->message .= '(' . get_class($e) . ') ' . $e->getMessage();
|
||||
}
|
||||
$this->message .= $this->getExceptionMessage($e, 0);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -82,4 +84,25 @@ class ExceptionCollection extends \Exception implements GuzzleException, \Iterat
|
|||
{
|
||||
return $this->exceptions ? $this->exceptions[0] : null;
|
||||
}
|
||||
|
||||
private function getExceptionMessage(\Exception $e, $depth = 0)
|
||||
{
|
||||
static $sp = ' ';
|
||||
$prefix = $depth ? str_repeat($sp, $depth) : '';
|
||||
$message = "{$prefix}(" . get_class($e) . ') ' . $e->getFile() . ' line ' . $e->getLine() . "\n";
|
||||
|
||||
if ($e instanceof self) {
|
||||
if ($e->shortMessage) {
|
||||
$message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->shortMessage) . "\n";
|
||||
}
|
||||
foreach ($e as $ee) {
|
||||
$message .= "\n" . $this->getExceptionMessage($ee, $depth + 1);
|
||||
}
|
||||
} else {
|
||||
$message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getMessage()) . "\n";
|
||||
$message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getTraceAsString()) . "\n";
|
||||
}
|
||||
|
||||
return str_replace(getcwd(), '.', $message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ interface HasDispatcherInterface
|
|||
*
|
||||
* @param string $eventName Name of the event to dispatch
|
||||
* @param array $context Context of the event
|
||||
*
|
||||
* @return Event Returns the created event object
|
||||
*/
|
||||
public function dispatch($eventName, array $context = array());
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace Guzzle\Common;
|
|||
*/
|
||||
class Version
|
||||
{
|
||||
const VERSION = '3.7.0';
|
||||
const VERSION = '3.9.2';
|
||||
|
||||
/**
|
||||
* @var bool Set this value to true to enable warnings for deprecated functionality use. This should be on in your
|
||||
|
|
|
|||
20
apps/files_external/3rdparty/aws-sdk-php/Guzzle/Common/composer.json
vendored
Normal file
20
apps/files_external/3rdparty/aws-sdk-php/Guzzle/Common/composer.json
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "guzzle/common",
|
||||
"homepage": "http://guzzlephp.org/",
|
||||
"description": "Common libraries used by Guzzle",
|
||||
"keywords": ["common", "event", "exception", "collection"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=5.3.2",
|
||||
"symfony/event-dispatcher": ">=2.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Guzzle\\Common": "" }
|
||||
},
|
||||
"target-dir": "Guzzle/Common",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.7-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,8 @@ class Client extends AbstractHasDispatcher implements ClientInterface
|
|||
const CURL_OPTIONS = 'curl.options';
|
||||
const SSL_CERT_AUTHORITY = 'ssl.certificate_authority';
|
||||
const DISABLE_REDIRECTS = RedirectPlugin::DISABLE;
|
||||
const DEFAULT_SELECT_TIMEOUT = 1.0;
|
||||
const MAX_HANDLES = 3;
|
||||
|
||||
/** @var Collection Default HTTP headers to set on each request */
|
||||
protected $defaultHeaders;
|
||||
|
|
@ -109,11 +111,8 @@ class Client extends AbstractHasDispatcher implements ClientInterface
|
|||
*/
|
||||
public function setDefaultOption($keyOrPath, $value)
|
||||
{
|
||||
if (strpos($keyOrPath, '/')) {
|
||||
$this->config->setPath($keyOrPath, $value);
|
||||
} else {
|
||||
$this->config[$keyOrPath] = $value;
|
||||
}
|
||||
$keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
|
||||
$this->config->setPath($keyOrPath, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -127,7 +126,9 @@ class Client extends AbstractHasDispatcher implements ClientInterface
|
|||
*/
|
||||
public function getDefaultOption($keyOrPath)
|
||||
{
|
||||
return strpos($keyOrPath, '/') ? $this->config->getPath($keyOrPath) : $this->config[$keyOrPath];
|
||||
$keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
|
||||
|
||||
return $this->config->getPath($keyOrPath);
|
||||
}
|
||||
|
||||
final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2)
|
||||
|
|
@ -142,7 +143,7 @@ class Client extends AbstractHasDispatcher implements ClientInterface
|
|||
} elseif ($certificateAuthority === false) {
|
||||
unset($opts[CURLOPT_CAINFO]);
|
||||
$opts[CURLOPT_SSL_VERIFYPEER] = false;
|
||||
$opts[CURLOPT_SSL_VERIFYHOST] = 2;
|
||||
$opts[CURLOPT_SSL_VERIFYHOST] = 0;
|
||||
} elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) {
|
||||
throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean');
|
||||
} elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) {
|
||||
|
|
@ -178,7 +179,7 @@ class Client extends AbstractHasDispatcher implements ClientInterface
|
|||
} else {
|
||||
list($uri, $templateVars) = $uri;
|
||||
}
|
||||
if (substr($uri, 0, 4) === 'http') {
|
||||
if (strpos($uri, '://')) {
|
||||
// Use absolute URLs as-is
|
||||
$url = $this->expandTemplate($uri, $templateVars);
|
||||
} else {
|
||||
|
|
@ -244,7 +245,7 @@ class Client extends AbstractHasDispatcher implements ClientInterface
|
|||
|
||||
public function head($uri = null, $headers = null, array $options = array())
|
||||
{
|
||||
return $this->createRequest('HEAD', $uri, $headers, $options);
|
||||
return $this->createRequest('HEAD', $uri, $headers, null, $options);
|
||||
}
|
||||
|
||||
public function delete($uri = null, $headers = null, $body = null, array $options = array())
|
||||
|
|
@ -307,7 +308,10 @@ class Client extends AbstractHasDispatcher implements ClientInterface
|
|||
public function getCurlMulti()
|
||||
{
|
||||
if (!$this->curlMulti) {
|
||||
$this->curlMulti = new CurlMultiProxy();
|
||||
$this->curlMulti = new CurlMultiProxy(
|
||||
self::MAX_HANDLES,
|
||||
$this->getConfig('select_timeout') ?: self::DEFAULT_SELECT_TIMEOUT
|
||||
);
|
||||
}
|
||||
|
||||
return $this->curlMulti;
|
||||
|
|
@ -334,31 +338,6 @@ class Client extends AbstractHasDispatcher implements ClientInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the cacert.pem file from the phar if it is not in the temp folder and validate the MD5 checksum
|
||||
*
|
||||
* @param bool $md5Check Set to false to not perform the MD5 validation
|
||||
*
|
||||
* @return string Returns the path to the extracted cacert
|
||||
* @throws RuntimeException if the file cannot be copied or there is a MD5 mismatch
|
||||
*/
|
||||
public function preparePharCacert($md5Check = true)
|
||||
{
|
||||
$from = __DIR__ . '/Resources/cacert.pem';
|
||||
$certFile = sys_get_temp_dir() . '/guzzle-cacert.pem';
|
||||
if (!file_exists($certFile) && !copy($from, $certFile)) {
|
||||
throw new RuntimeException("Could not copy {$from} to {$certFile}: " . var_export(error_get_last(), true));
|
||||
} elseif ($md5Check) {
|
||||
$actualMd5 = md5_file($certFile);
|
||||
$expectedMd5 = trim(file_get_contents("{$from}.md5"));
|
||||
if ($actualMd5 != $expectedMd5) {
|
||||
throw new RuntimeException("{$certFile} MD5 mismatch: expected {$expectedMd5} but got {$actualMd5}");
|
||||
}
|
||||
}
|
||||
|
||||
return $certFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a URI template while merging client config settings into the template variables
|
||||
*
|
||||
|
|
@ -458,7 +437,9 @@ class Client extends AbstractHasDispatcher implements ClientInterface
|
|||
*/
|
||||
protected function initSsl()
|
||||
{
|
||||
if ('system' == ($authority = $this->config[self::SSL_CERT_AUTHORITY])) {
|
||||
$authority = $this->config[self::SSL_CERT_AUTHORITY];
|
||||
|
||||
if ($authority === 'system') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -467,13 +448,7 @@ class Client extends AbstractHasDispatcher implements ClientInterface
|
|||
}
|
||||
|
||||
if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') {
|
||||
$authority = $this->preparePharCacert();
|
||||
$that = $this;
|
||||
$this->getEventDispatcher()->addListener('request.before_send', function ($event) use ($authority, $that) {
|
||||
if ($authority == $event['request']->getCurlOptions()->get(CURLOPT_CAINFO)) {
|
||||
$that->preparePharCacert(false);
|
||||
}
|
||||
});
|
||||
$authority = self::extractPharCacert(__DIR__ . '/Resources/cacert.pem');
|
||||
}
|
||||
|
||||
$this->setSslVerification($authority);
|
||||
|
|
@ -504,4 +479,46 @@ class Client extends AbstractHasDispatcher implements ClientInterface
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function preparePharCacert($md5Check = true)
|
||||
{
|
||||
return sys_get_temp_dir() . '/guzzle-cacert.pem';
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the phar cacert from a phar into the temp directory.
|
||||
*
|
||||
* @param string $pharCacertPath Path to the phar cacert. For example:
|
||||
* 'phar://aws.phar/Guzzle/Http/Resources/cacert.pem'
|
||||
*
|
||||
* @return string Returns the path to the extracted cacert file.
|
||||
* @throws \RuntimeException Throws if the phar cacert cannot be found or
|
||||
* the file cannot be copied to the temp dir.
|
||||
*/
|
||||
public static function extractPharCacert($pharCacertPath)
|
||||
{
|
||||
// Copy the cacert.pem file from the phar if it is not in the temp
|
||||
// folder.
|
||||
$certFile = sys_get_temp_dir() . '/guzzle-cacert.pem';
|
||||
|
||||
if (!file_exists($pharCacertPath)) {
|
||||
throw new \RuntimeException("Could not find $pharCacertPath");
|
||||
}
|
||||
|
||||
if (!file_exists($certFile) ||
|
||||
filesize($certFile) != filesize($pharCacertPath)
|
||||
) {
|
||||
if (!copy($pharCacertPath, $certFile)) {
|
||||
throw new \RuntimeException(
|
||||
"Could not copy {$pharCacertPath} to {$certFile}: "
|
||||
. var_export(error_get_last(), true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $certFile;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,9 +48,16 @@ class CurlHandle
|
|||
$method = $request->getMethod();
|
||||
$bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING);
|
||||
|
||||
// Prepare url
|
||||
$url = (string)$request->getUrl();
|
||||
if(($pos = strpos($url, '#')) !== false ){
|
||||
// strip fragment from url
|
||||
$url = substr($url, 0, $pos);
|
||||
}
|
||||
|
||||
// Array of default cURL options.
|
||||
$curlOptions = array(
|
||||
CURLOPT_URL => $request->getUrl(),
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_CONNECTTIMEOUT => 150,
|
||||
CURLOPT_RETURNTRANSFER => false,
|
||||
CURLOPT_HEADER => false,
|
||||
|
|
@ -200,6 +207,12 @@ class CurlHandle
|
|||
$curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) {
|
||||
$args = func_get_args();
|
||||
$args[] = $handle;
|
||||
|
||||
// PHP 5.5 pushed the handle onto the start of the args
|
||||
if (is_resource($args[0])) {
|
||||
array_shift($args);
|
||||
}
|
||||
|
||||
call_user_func_array(array($mediator, 'progress'), $args);
|
||||
};
|
||||
$curlOptions[CURLOPT_NOPROGRESS] = false;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use Guzzle\Common\Event;
|
|||
use Guzzle\Http\Exception\MultiTransferException;
|
||||
use Guzzle\Http\Exception\CurlException;
|
||||
use Guzzle\Http\Message\RequestInterface;
|
||||
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
|
||||
use Guzzle\Http\Exception\RequestException;
|
||||
|
||||
/**
|
||||
* Send {@see RequestInterface} objects in parallel using curl_multi
|
||||
|
|
@ -39,8 +41,12 @@ class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!')
|
||||
);
|
||||
|
||||
public function __construct()
|
||||
/** @var float */
|
||||
protected $selectTimeout;
|
||||
|
||||
public function __construct($selectTimeout = 1.0)
|
||||
{
|
||||
$this->selectTimeout = $selectTimeout;
|
||||
$this->multiHandle = curl_multi_init();
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->multiHandle === false) {
|
||||
|
|
@ -76,13 +82,12 @@ class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
public function remove(RequestInterface $request)
|
||||
{
|
||||
$this->removeHandle($request);
|
||||
foreach ($this->requests as $i => $r) {
|
||||
if ($request === $r) {
|
||||
unset($this->requests[$i]);
|
||||
$this->requests = array_values($this->requests);
|
||||
$this->dispatch(self::REMOVE_REQUEST, array('request' => $request));
|
||||
return true;
|
||||
}
|
||||
if (($index = array_search($request, $this->requests, true)) !== false) {
|
||||
$request = $this->requests[$index];
|
||||
unset($this->requests[$index]);
|
||||
$this->requests = array_values($this->requests);
|
||||
$this->dispatch(self::REMOVE_REQUEST, array('request' => $request));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -130,8 +135,7 @@ class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
$multiException = new MultiTransferException('Errors during multi transfer');
|
||||
|
||||
while ($e = array_shift($exceptions)) {
|
||||
$multiException->add($e['exception']);
|
||||
$multiException->addFailedRequest($e['request']);
|
||||
$multiException->addFailedRequestWithException($e['request'], $e['exception']);
|
||||
}
|
||||
|
||||
// Add successful requests
|
||||
|
|
@ -155,10 +159,10 @@ class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
try {
|
||||
$state = $request->setState(RequestInterface::STATE_TRANSFER);
|
||||
if ($state == RequestInterface::STATE_TRANSFER) {
|
||||
// Add the request curl handle to the multi handle
|
||||
$this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $this->createCurlHandle($request)->getHandle()));
|
||||
$this->addHandle($request);
|
||||
} else {
|
||||
// Requests might decide they don't need to be sent just before transfer (e.g. CachePlugin)
|
||||
// Requests might decide they don't need to be sent just before
|
||||
// transfer (e.g. CachePlugin)
|
||||
$this->remove($request);
|
||||
if ($state == RequestInterface::STATE_COMPLETE) {
|
||||
$this->successful[] = $request;
|
||||
|
|
@ -170,6 +174,14 @@ class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
}
|
||||
}
|
||||
|
||||
private function addHandle(RequestInterface $request)
|
||||
{
|
||||
$handle = $this->createCurlHandle($request)->getHandle();
|
||||
$this->checkCurlResult(
|
||||
curl_multi_add_handle($this->multiHandle, $handle)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a curl handle for a request
|
||||
*
|
||||
|
|
@ -191,18 +203,9 @@ class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
*/
|
||||
protected function perform()
|
||||
{
|
||||
if (!$this->requests) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the handles with a very quick select timeout
|
||||
$active = $mrc = null;
|
||||
$this->executeHandles($active, $mrc, 0.001);
|
||||
$event = new Event(array('curl_multi' => $this));
|
||||
$this->processMessages();
|
||||
|
||||
while ($this->requests) {
|
||||
|
||||
// Notify each request as polling
|
||||
$blocking = $total = 0;
|
||||
foreach ($this->requests as $request) {
|
||||
|
|
@ -214,77 +217,60 @@ class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
++$blocking;
|
||||
}
|
||||
}
|
||||
|
||||
if ($blocking == $total) {
|
||||
// Sleep to prevent eating CPU because no requests are actually pending a select call
|
||||
usleep(500);
|
||||
} else {
|
||||
do {
|
||||
$this->executeHandles($active, $mrc, 1);
|
||||
} while ($active);
|
||||
$this->executeHandles();
|
||||
}
|
||||
$this->processMessages();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute and select curl handles
|
||||
*/
|
||||
private function executeHandles()
|
||||
{
|
||||
// The first curl_multi_select often times out no matter what, but is usually required for fast transfers
|
||||
$selectTimeout = 0.001;
|
||||
$active = false;
|
||||
do {
|
||||
while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM);
|
||||
$this->checkCurlResult($mrc);
|
||||
$this->processMessages();
|
||||
if ($active && curl_multi_select($this->multiHandle, $selectTimeout) === -1) {
|
||||
// Perform a usleep if a select returns -1: https://bugs.php.net/bug.php?id=61141
|
||||
usleep(150);
|
||||
}
|
||||
$selectTimeout = $this->selectTimeout;
|
||||
} while ($active);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process any received curl multi messages
|
||||
*/
|
||||
private function processMessages()
|
||||
{
|
||||
// Get messages from curl handles
|
||||
while ($done = curl_multi_info_read($this->multiHandle)) {
|
||||
$request = $this->resourceHash[(int) $done['handle']];
|
||||
try {
|
||||
$request = $this->resourceHash[(int) $done['handle']];
|
||||
$this->processResponse($request, $this->handles[$request], $done);
|
||||
$this->successful[] = $request;
|
||||
} catch (MultiTransferException $e) {
|
||||
$this->removeErroredRequest($request, $e, false);
|
||||
throw $e;
|
||||
} catch (\Exception $e) {
|
||||
$this->removeErroredRequest($request, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute and select curl handles until there is activity
|
||||
*
|
||||
* @param int $active Active value to update
|
||||
* @param int $mrc Multi result value to update
|
||||
* @param int $timeout Select timeout in seconds
|
||||
*/
|
||||
private function executeHandles(&$active, &$mrc, $timeout = 1)
|
||||
{
|
||||
do {
|
||||
$mrc = curl_multi_exec($this->multiHandle, $active);
|
||||
} while ($mrc == CURLM_CALL_MULTI_PERFORM && $active);
|
||||
$this->checkCurlResult($mrc);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
// Select the curl handles until there is any activity on any of the open file descriptors
|
||||
// See https://github.com/php/php-src/blob/master/ext/curl/multi.c#L170
|
||||
if ($active && $mrc == CURLM_OK && curl_multi_select($this->multiHandle, $timeout) == -1) {
|
||||
// Perform a usleep if a previously executed select returned -1
|
||||
// @see https://bugs.php.net/bug.php?id=61141
|
||||
usleep(100);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a request that encountered an exception
|
||||
*
|
||||
* @param RequestInterface $request Request to remove
|
||||
* @param \Exception $e Exception encountered
|
||||
* @param bool $buffer Set to false to not buffer the exception
|
||||
*/
|
||||
protected function removeErroredRequest(RequestInterface $request, \Exception $e = null, $buffer = true)
|
||||
protected function removeErroredRequest(RequestInterface $request, \Exception $e = null)
|
||||
{
|
||||
if ($buffer) {
|
||||
$this->exceptions[] = array('request' => $request, 'exception' => $e);
|
||||
}
|
||||
|
||||
$this->exceptions[] = array('request' => $request, 'exception' => $e);
|
||||
$this->remove($request);
|
||||
$this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions));
|
||||
}
|
||||
|
|
@ -310,23 +296,31 @@ class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
$this->removeHandle($request);
|
||||
|
||||
if (!$curlException) {
|
||||
$state = $request->setState(RequestInterface::STATE_COMPLETE, array('handle' => $handle));
|
||||
// Only remove the request if it wasn't resent as a result of the state change
|
||||
if ($state != RequestInterface::STATE_TRANSFER) {
|
||||
$this->remove($request);
|
||||
}
|
||||
} else {
|
||||
// Set the state of the request to an error
|
||||
$state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException));
|
||||
// Allow things to ignore the error if possible
|
||||
if ($state != RequestInterface::STATE_TRANSFER) {
|
||||
$this->remove($request);
|
||||
}
|
||||
// The error was not handled, so fail
|
||||
if ($state == RequestInterface::STATE_ERROR) {
|
||||
/** @var CurlException $curlException */
|
||||
throw $curlException;
|
||||
if ($this->validateResponseWasSet($request)) {
|
||||
$state = $request->setState(
|
||||
RequestInterface::STATE_COMPLETE,
|
||||
array('handle' => $handle)
|
||||
);
|
||||
// Only remove the request if it wasn't resent as a result of
|
||||
// the state change
|
||||
if ($state != RequestInterface::STATE_TRANSFER) {
|
||||
$this->remove($request);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the state of the request to an error
|
||||
$state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException));
|
||||
// Allow things to ignore the error if possible
|
||||
if ($state != RequestInterface::STATE_TRANSFER) {
|
||||
$this->remove($request);
|
||||
}
|
||||
|
||||
// The error was not handled, so fail
|
||||
if ($state == RequestInterface::STATE_ERROR) {
|
||||
/** @var CurlException $curlException */
|
||||
throw $curlException;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -339,9 +333,9 @@ class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
{
|
||||
if (isset($this->handles[$request])) {
|
||||
$handle = $this->handles[$request];
|
||||
curl_multi_remove_handle($this->multiHandle, $handle->getHandle());
|
||||
unset($this->handles[$request]);
|
||||
unset($this->resourceHash[(int) $handle->getHandle()]);
|
||||
curl_multi_remove_handle($this->multiHandle, $handle->getHandle());
|
||||
$handle->close();
|
||||
}
|
||||
}
|
||||
|
|
@ -387,4 +381,43 @@ class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://github.com/guzzle/guzzle/issues/710
|
||||
*/
|
||||
private function validateResponseWasSet(RequestInterface $request)
|
||||
{
|
||||
if ($request->getResponse()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$body = $request instanceof EntityEnclosingRequestInterface
|
||||
? $request->getBody()
|
||||
: null;
|
||||
|
||||
if (!$body) {
|
||||
$rex = new RequestException(
|
||||
'No response was received for a request with no body. This'
|
||||
. ' could mean that you are saturating your network.'
|
||||
);
|
||||
$rex->setRequest($request);
|
||||
$this->removeErroredRequest($request, $rex);
|
||||
} elseif (!$body->isSeekable() || !$body->seek(0)) {
|
||||
// Nothing we can do with this. Sorry!
|
||||
$rex = new RequestException(
|
||||
'The connection was unexpectedly closed. The request would'
|
||||
. ' have been retried, but attempting to rewind the'
|
||||
. ' request body failed.'
|
||||
);
|
||||
$rex->setRequest($request);
|
||||
$this->removeErroredRequest($request, $rex);
|
||||
} else {
|
||||
$this->remove($request);
|
||||
// Add the request back to the batch to retry automatically.
|
||||
$this->requests[] = $request;
|
||||
$this->addHandle($request);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,16 @@ class CurlMultiProxy extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
protected $groups = array();
|
||||
protected $queued = array();
|
||||
protected $maxHandles;
|
||||
protected $selectTimeout;
|
||||
|
||||
/**
|
||||
* @param int $maxHandles The maximum number of idle CurlMulti handles to allow to remain open
|
||||
* @param int $maxHandles The maximum number of idle CurlMulti handles to allow to remain open
|
||||
* @param float $selectTimeout timeout for curl_multi_select
|
||||
*/
|
||||
public function __construct($maxHandles = 3)
|
||||
public function __construct($maxHandles = 3, $selectTimeout = 1.0)
|
||||
{
|
||||
$this->maxHandles = $maxHandles;
|
||||
$this->selectTimeout = $selectTimeout;
|
||||
// You can get some weird "Too many open files" errors when sending a large amount of requests in parallel.
|
||||
// These two statements autoload classes before a system runs out of file descriptors so that you can get back
|
||||
// valuable error messages if you run out.
|
||||
|
|
@ -122,7 +125,7 @@ class CurlMultiProxy extends AbstractHasDispatcher implements CurlMultiInterface
|
|||
}
|
||||
|
||||
// All are claimed, so create one
|
||||
$handle = new CurlMulti();
|
||||
$handle = new CurlMulti($this->selectTimeout);
|
||||
$handle->setEventDispatcher($this->getEventDispatcher());
|
||||
$this->handles[] = $handle;
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,12 @@ class RequestMediator
|
|||
));
|
||||
}
|
||||
|
||||
return $this->request->getResponse()->getBody()->write($write);
|
||||
if ($response = $this->request->getResponse()) {
|
||||
return $response->getBody()->write($write);
|
||||
} else {
|
||||
// Unexpected data received before response headers - abort transfer
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -128,18 +133,15 @@ class RequestMediator
|
|||
*/
|
||||
public function readRequestBody($ch, $fd, $length)
|
||||
{
|
||||
$read = '';
|
||||
|
||||
if ($this->request->getBody()) {
|
||||
$read = $this->request->getBody()->read($length);
|
||||
if ($this->emitIo) {
|
||||
$this->request->dispatch('curl.callback.read', array(
|
||||
'request' => $this->request,
|
||||
'read' => $read
|
||||
));
|
||||
}
|
||||
if (!($body = $this->request->getBody())) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return !$read ? '' : $read;
|
||||
$read = (string) $body->read($length);
|
||||
if ($this->emitIo) {
|
||||
$this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read));
|
||||
}
|
||||
|
||||
return $read;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,9 +126,11 @@ class EntityBody extends Stream implements EntityBodyInterface
|
|||
|
||||
public function getContentMd5($rawOutput = false, $base64Encode = false)
|
||||
{
|
||||
$hash = self::getHash($this, 'md5', $rawOutput);
|
||||
|
||||
return $hash && $base64Encode ? base64_encode($hash) : $hash;
|
||||
if ($hash = self::getHash($this, 'md5', $rawOutput)) {
|
||||
return $hash && $base64Encode ? base64_encode($hash) : $hash;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ class BadResponseException extends RequestException
|
|||
} else {
|
||||
$label = 'Unsuccessful response';
|
||||
$class = __CLASS__;
|
||||
$e = new self();
|
||||
}
|
||||
|
||||
$message = $label . PHP_EOL . implode(PHP_EOL, array(
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ class MultiTransferException extends ExceptionCollection
|
|||
{
|
||||
protected $successfulRequests = array();
|
||||
protected $failedRequests = array();
|
||||
protected $exceptionForRequest = array();
|
||||
|
||||
/**
|
||||
* Get all of the requests in the transfer
|
||||
|
|
@ -51,6 +52,37 @@ class MultiTransferException extends ExceptionCollection
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the array of failed requests and associate with exceptions
|
||||
*
|
||||
* @param RequestInterface $request Failed request
|
||||
* @param \Exception $exception Exception to add and associate with
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addFailedRequestWithException(RequestInterface $request, \Exception $exception)
|
||||
{
|
||||
$this->add($exception)
|
||||
->addFailedRequest($request)
|
||||
->exceptionForRequest[spl_object_hash($request)] = $exception;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Exception that caused the given $request to fail
|
||||
*
|
||||
* @param RequestInterface $request Failed command
|
||||
*
|
||||
* @return \Exception|null
|
||||
*/
|
||||
public function getExceptionForFailedRequest(RequestInterface $request)
|
||||
{
|
||||
$oid = spl_object_hash($request);
|
||||
|
||||
return isset($this->exceptionForRequest[$oid]) ? $this->exceptionForRequest[$oid] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all of the successful requests
|
||||
*
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class IoEmittingEntityBody extends AbstractEntityBodyDecorator implements HasDis
|
|||
|
||||
public function dispatch($eventName, array $context = array())
|
||||
{
|
||||
$this->getEventDispatcher()->dispatch($eventName, new Event($context));
|
||||
return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use Guzzle\Http\EntityBodyInterface;
|
|||
use Guzzle\Http\QueryString;
|
||||
use Guzzle\Http\RedirectPlugin;
|
||||
use Guzzle\Http\Exception\RequestException;
|
||||
use Guzzle\Http\Mimetypes;
|
||||
|
||||
/**
|
||||
* HTTP request that sends an entity-body in the request message (POST, PUT, PATCH, DELETE)
|
||||
|
|
@ -61,7 +60,7 @@ class EntityEnclosingRequest extends Request implements EntityEnclosingRequestIn
|
|||
|
||||
// Auto detect the Content-Type from the path of the request if possible
|
||||
if ($contentType === null && !$this->hasHeader('Content-Type')) {
|
||||
$contentType = $this->body->getContentType() ?: Mimetypes::getInstance()->fromFilename($this->getPath());
|
||||
$contentType = $this->body->getContentType();
|
||||
}
|
||||
|
||||
if ($contentType) {
|
||||
|
|
@ -181,7 +180,7 @@ class EntityEnclosingRequest extends Request implements EntityEnclosingRequestIn
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function addPostFile($field, $filename = null, $contentType = null)
|
||||
public function addPostFile($field, $filename = null, $contentType = null, $postname = null)
|
||||
{
|
||||
$data = null;
|
||||
|
||||
|
|
@ -197,7 +196,7 @@ class EntityEnclosingRequest extends Request implements EntityEnclosingRequestIn
|
|||
throw new RequestException('The path to a file must be a string');
|
||||
} elseif (!empty($filename)) {
|
||||
// Adding an empty file will cause cURL to error out
|
||||
$data = new PostFile($field, $filename, $contentType);
|
||||
$data = new PostFile($field, $filename, $contentType, $postname);
|
||||
}
|
||||
|
||||
if ($data) {
|
||||
|
|
|
|||
|
|
@ -108,9 +108,10 @@ interface EntityEnclosingRequestInterface extends RequestInterface
|
|||
* @param string $filename Full path to the file. Do not include the @ symbol.
|
||||
* @param string $contentType Optional Content-Type to add to the Content-Disposition.
|
||||
* Default behavior is to guess. Set to false to not specify.
|
||||
* @param string $postname The name of the file, when posted. (e.g. rename the file)
|
||||
* @return self
|
||||
*/
|
||||
public function addPostFile($field, $filename = null, $contentType = null);
|
||||
public function addPostFile($field, $filename = null, $contentType = null, $postname = null);
|
||||
|
||||
/**
|
||||
* Add POST files to use in the upload
|
||||
|
|
|
|||
|
|
@ -81,7 +81,8 @@ class Header implements HeaderInterface
|
|||
|
||||
for ($i = 0, $total = count($values); $i < $total; $i++) {
|
||||
if (strpos($values[$i], $this->glue) !== false) {
|
||||
foreach (explode($this->glue, $values[$i]) as $v) {
|
||||
// Explode on glue when the glue is not inside of a comma
|
||||
foreach (preg_split('/' . preg_quote($this->glue) . '(?=([^"]*"[^"]*")*[^"]*$)/', $values[$i]) as $v) {
|
||||
$values[] = trim($v);
|
||||
}
|
||||
unset($values[$i]);
|
||||
|
|
@ -122,23 +123,24 @@ class Header implements HeaderInterface
|
|||
return new \ArrayIterator($this->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @todo Do not split semicolons when enclosed in quotes (e.g. foo="baz;bar")
|
||||
*/
|
||||
public function parseParams()
|
||||
{
|
||||
$params = array();
|
||||
$params = $matches = array();
|
||||
$callback = array($this, 'trimHeader');
|
||||
|
||||
// Normalize the header into a single array and iterate over all values
|
||||
foreach ($this->normalize()->toArray() as $val) {
|
||||
$part = array();
|
||||
foreach (explode(';', $val) as $kvp) {
|
||||
$pieces = array_map($callback, explode('=', $kvp, 2));
|
||||
foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
|
||||
if (!preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
|
||||
continue;
|
||||
}
|
||||
$pieces = array_map($callback, $matches[0]);
|
||||
$part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : '';
|
||||
}
|
||||
$params[] = $part;
|
||||
if ($part) {
|
||||
$params[] = $part;
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Guzzle\Http\Message\Header;
|
||||
|
||||
use Guzzle\Common\Collection;
|
||||
use Guzzle\Common\ToArrayInterface;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -14,16 +14,19 @@ class PostFile implements PostFileInterface
|
|||
protected $fieldName;
|
||||
protected $contentType;
|
||||
protected $filename;
|
||||
protected $postname;
|
||||
|
||||
/**
|
||||
* @param string $fieldName Name of the field
|
||||
* @param string $filename Path to the file
|
||||
* @param string $filename Local path to the file
|
||||
* @param string $postname Remote post file name
|
||||
* @param string $contentType Content-Type of the upload
|
||||
*/
|
||||
public function __construct($fieldName, $filename, $contentType = null)
|
||||
public function __construct($fieldName, $filename, $contentType = null, $postname = null)
|
||||
{
|
||||
$this->fieldName = $fieldName;
|
||||
$this->setFilename($filename);
|
||||
$this->postname = $postname ? $postname : basename($filename);
|
||||
$this->contentType = $contentType ?: $this->guessContentType();
|
||||
}
|
||||
|
||||
|
|
@ -55,11 +58,23 @@ class PostFile implements PostFileInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setPostname($postname)
|
||||
{
|
||||
$this->postname = $postname;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFilename()
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
public function getPostname()
|
||||
{
|
||||
return $this->postname;
|
||||
}
|
||||
|
||||
public function setContentType($type)
|
||||
{
|
||||
$this->contentType = $type;
|
||||
|
|
@ -77,11 +92,11 @@ class PostFile implements PostFileInterface
|
|||
// PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax
|
||||
// See: https://wiki.php.net/rfc/curl-file-upload
|
||||
if (function_exists('curl_file_create')) {
|
||||
return curl_file_create($this->filename, $this->contentType, basename($this->filename));
|
||||
return curl_file_create($this->filename, $this->contentType, $this->postname);
|
||||
}
|
||||
|
||||
// Use the old style if using an older version of PHP
|
||||
$value = "@{$this->filename};filename=" . basename($this->filename);
|
||||
$value = "@{$this->filename};filename=" . $this->postname;
|
||||
if ($this->contentType) {
|
||||
$value .= ';type=' . $this->contentType;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,15 @@ interface PostFileInterface
|
|||
*/
|
||||
public function setFilename($path);
|
||||
|
||||
/**
|
||||
* Set the post name of the file
|
||||
*
|
||||
* @param string $name The new name of the file
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setPostname($name);
|
||||
|
||||
/**
|
||||
* Get the full path to the file
|
||||
*
|
||||
|
|
@ -42,6 +51,13 @@ interface PostFileInterface
|
|||
*/
|
||||
public function getFilename();
|
||||
|
||||
/**
|
||||
* Get the post name of the file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPostname();
|
||||
|
||||
/**
|
||||
* Set the Content-Type of the file
|
||||
*
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ class Request extends AbstractMessage implements RequestInterface
|
|||
|
||||
// Include the port in the Host header if it is not the default port for the scheme of the URL
|
||||
$scheme = $this->url->getScheme();
|
||||
if (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443)) {
|
||||
if ($port && (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443))) {
|
||||
$this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port);
|
||||
} else {
|
||||
$this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost());
|
||||
|
|
@ -532,7 +532,8 @@ class Request extends AbstractMessage implements RequestInterface
|
|||
public function dispatch($eventName, array $context = array())
|
||||
{
|
||||
$context['request'] = $this;
|
||||
$this->getEventDispatcher()->dispatch($eventName, new Event($context));
|
||||
|
||||
return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
|
||||
}
|
||||
|
||||
public function addSubscriber(EventSubscriberInterface $subscriber)
|
||||
|
|
@ -542,21 +543,6 @@ class Request extends AbstractMessage implements RequestInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Adds a check for Host header changes
|
||||
*/
|
||||
public function addHeader($header, $value)
|
||||
{
|
||||
parent::addHeader($header, $value);
|
||||
|
||||
if ($header == 'host' || $header == 'Host') {
|
||||
$this->setHost((string) $this->getHeader('Host'));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array containing the request and response for event notifications
|
||||
*
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use Guzzle\Common\Exception\InvalidArgumentException;
|
|||
use Guzzle\Http\RedirectPlugin;
|
||||
use Guzzle\Http\Url;
|
||||
use Guzzle\Parser\ParserRegistry;
|
||||
use Guzzle\Plugin\Log\LogPlugin;
|
||||
|
||||
/**
|
||||
* Default HTTP request factory used to create the default {@see Request} and {@see EntityEnclosingRequest} objects.
|
||||
|
|
@ -85,7 +84,7 @@ class RequestFactory implements RequestFactoryInterface
|
|||
{
|
||||
$method = strtoupper($method);
|
||||
|
||||
if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE' || $method == 'OPTIONS') {
|
||||
if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE') {
|
||||
// Handle non-entity-enclosing request methods
|
||||
$request = new $this->requestClass($method, $url, $headers);
|
||||
if ($body) {
|
||||
|
|
@ -98,7 +97,7 @@ class RequestFactory implements RequestFactoryInterface
|
|||
} else {
|
||||
// Create an entity enclosing request by default
|
||||
$request = new $this->entityEnclosingRequestClass($method, $url, $headers);
|
||||
if ($body) {
|
||||
if ($body || $body === '0') {
|
||||
// Add POST fields and files to an entity enclosing request if an array is used
|
||||
if (is_array($body) || $body instanceof Collection) {
|
||||
// Normalize PHP style cURL uploads with a leading '@' symbol
|
||||
|
|
@ -139,7 +138,7 @@ class RequestFactory implements RequestFactoryInterface
|
|||
public function cloneRequestWithMethod(RequestInterface $request, $method)
|
||||
{
|
||||
// Create the request with the same client if possible
|
||||
if ($client = $request->getClient()) {
|
||||
if ($request->getClient()) {
|
||||
$cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders());
|
||||
} else {
|
||||
$cloned = $this->create($method, $request->getUrl(), $request->getHeaders());
|
||||
|
|
@ -270,7 +269,7 @@ class RequestFactory implements RequestFactoryInterface
|
|||
if ($value === false || $value === 0) {
|
||||
$dispatcher = $request->getEventDispatcher();
|
||||
foreach ($dispatcher->getListeners('request.error') as $listener) {
|
||||
if ($listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') {
|
||||
if (is_array($listener) && $listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') {
|
||||
$dispatcher->removeListener('request.error', $listener);
|
||||
break;
|
||||
}
|
||||
|
|
@ -294,22 +293,26 @@ class RequestFactory implements RequestFactoryInterface
|
|||
|
||||
protected function visit_timeout(RequestInterface $request, $value, $flags)
|
||||
{
|
||||
$request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000);
|
||||
if (defined('CURLOPT_TIMEOUT_MS')) {
|
||||
$request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000);
|
||||
} else {
|
||||
$request->getCurlOptions()->set(CURLOPT_TIMEOUT, $value);
|
||||
}
|
||||
}
|
||||
|
||||
protected function visit_connect_timeout(RequestInterface $request, $value, $flags)
|
||||
{
|
||||
$request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000);
|
||||
if (defined('CURLOPT_CONNECTTIMEOUT_MS')) {
|
||||
$request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000);
|
||||
} else {
|
||||
$request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, $value);
|
||||
}
|
||||
}
|
||||
|
||||
protected function visit_debug(RequestInterface $request, $value, $flags)
|
||||
{
|
||||
if (class_exists('Guzzle\Plugin\Log\LogPlugin')) {
|
||||
$request->addSubscriber(LogPlugin::getDebugPlugin());
|
||||
} else {
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($value) {
|
||||
$request->getCurlOptions()->set(CURLOPT_VERBOSE, true);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -333,4 +336,24 @@ class RequestFactory implements RequestFactoryInterface
|
|||
{
|
||||
$request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags);
|
||||
}
|
||||
|
||||
protected function visit_cert(RequestInterface $request, $value, $flags)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]);
|
||||
$request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]);
|
||||
} else {
|
||||
$request->getCurlOptions()->set(CURLOPT_SSLCERT, $value);
|
||||
}
|
||||
}
|
||||
|
||||
protected function visit_ssl_key(RequestInterface $request, $value, $flags)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]);
|
||||
$request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]);
|
||||
} else {
|
||||
$request->getCurlOptions()->set(CURLOPT_SSLKEY, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,12 @@ interface RequestFactoryInterface
|
|||
* indefinitely.
|
||||
* "verify": Set to true to enable SSL cert validation (the default), false to disable, or supply the path
|
||||
* to a CA bundle to enable verification using a custom certificate.
|
||||
* "cert": Set to a string to specify the path to a file containing a PEM formatted certificate. If a
|
||||
* password is required, then set an array containing the path to the PEM file followed by the the
|
||||
* password required for the certificate.
|
||||
* "ssl_key": Specify the path to a file containing a private SSL key in PEM format. If a password is
|
||||
* required, then set an array containing the path to the SSL key followed by the password required for
|
||||
* the certificate.
|
||||
* "proxy": Specify an HTTP proxy (e.g. "http://username:password@192.168.16.1:10")
|
||||
* "debug": Set to true to display all data sent over the wire
|
||||
* @param int $flags Bitwise flags to apply when applying the options to the request. Defaults to no special
|
||||
|
|
|
|||
|
|
@ -865,18 +865,38 @@ class Response extends AbstractMessage implements \Serializable
|
|||
}
|
||||
|
||||
/**
|
||||
* Parse the XML response body and return a SimpleXMLElement
|
||||
* Parse the XML response body and return a \SimpleXMLElement.
|
||||
*
|
||||
* In order to prevent XXE attacks, this method disables loading external
|
||||
* entities. If you rely on external entities, then you must parse the
|
||||
* XML response manually by accessing the response body directly.
|
||||
*
|
||||
* @return \SimpleXMLElement
|
||||
* @throws RuntimeException if the response body is not in XML format
|
||||
* @link http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html
|
||||
*/
|
||||
public function xml()
|
||||
{
|
||||
$errorMessage = null;
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
$disableEntities = libxml_disable_entity_loader(true);
|
||||
libxml_clear_errors();
|
||||
|
||||
try {
|
||||
// Allow XML to be retrieved even if there is no response body
|
||||
$xml = new \SimpleXMLElement((string) $this->body ?: '<root />');
|
||||
$xml = new \SimpleXMLElement((string) $this->body ?: '<root />', LIBXML_NONET);
|
||||
if ($error = libxml_get_last_error()) {
|
||||
$errorMessage = $error->message;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new RuntimeException('Unable to parse response body into XML: ' . $e->getMessage());
|
||||
$errorMessage = $e->getMessage();
|
||||
}
|
||||
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
libxml_disable_entity_loader($disableEntities);
|
||||
|
||||
if ($errorMessage) {
|
||||
throw new RuntimeException('Unable to parse response body into XML: ' . $errorMessage);
|
||||
}
|
||||
|
||||
return $xml;
|
||||
|
|
|
|||
|
|
@ -943,6 +943,8 @@ class Mimetypes
|
|||
*/
|
||||
public function fromExtension($extension)
|
||||
{
|
||||
$extension = strtolower($extension);
|
||||
|
||||
return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
namespace Guzzle\Http;
|
||||
|
||||
use Guzzle\Common\Collection;
|
||||
use Guzzle\Common\Exception\RuntimeException;
|
||||
use Guzzle\Http\QueryAggregator\DuplicateAggregator;
|
||||
use Guzzle\Http\QueryAggregator\QueryAggregatorInterface;
|
||||
use Guzzle\Http\QueryAggregator\PhpAggregator;
|
||||
|
||||
|
|
@ -33,7 +35,7 @@ class QueryString extends Collection
|
|||
protected $aggregator;
|
||||
|
||||
/** @var array Cached PHP aggregator */
|
||||
protected static $defaultAggregator = null;
|
||||
private static $defaultAggregator = null;
|
||||
|
||||
/**
|
||||
* Parse a query string into a QueryString object
|
||||
|
|
@ -45,31 +47,40 @@ class QueryString extends Collection
|
|||
public static function fromString($query)
|
||||
{
|
||||
$q = new static();
|
||||
if ($query === '') {
|
||||
return $q;
|
||||
}
|
||||
|
||||
if ($query || $query === '0') {
|
||||
if ($query[0] == '?') {
|
||||
$query = substr($query, 1);
|
||||
$foundDuplicates = $foundPhpStyle = false;
|
||||
|
||||
foreach (explode('&', $query) as $kvp) {
|
||||
$parts = explode('=', $kvp, 2);
|
||||
$key = rawurldecode($parts[0]);
|
||||
if ($paramIsPhpStyleArray = substr($key, -2) == '[]') {
|
||||
$foundPhpStyle = true;
|
||||
$key = substr($key, 0, -2);
|
||||
}
|
||||
foreach (explode('&', $query) as $kvp) {
|
||||
$parts = explode('=', $kvp, 2);
|
||||
$key = rawurldecode($parts[0]);
|
||||
|
||||
if ($paramIsPhpStyleArray = substr($key, -2) == '[]') {
|
||||
$key = substr($key, 0, -2);
|
||||
}
|
||||
|
||||
if (isset($parts[1])) {
|
||||
$value = rawurldecode(str_replace('+', '%20', $parts[1]));
|
||||
if ($paramIsPhpStyleArray && !$q->hasKey($key)) {
|
||||
$value = array($value);
|
||||
}
|
||||
if (isset($parts[1])) {
|
||||
$value = rawurldecode(str_replace('+', '%20', $parts[1]));
|
||||
if (isset($q[$key])) {
|
||||
$q->add($key, $value);
|
||||
$foundDuplicates = true;
|
||||
} elseif ($paramIsPhpStyleArray) {
|
||||
$q[$key] = array($value);
|
||||
} else {
|
||||
$q->add($key, null);
|
||||
$q[$key] = $value;
|
||||
}
|
||||
} else {
|
||||
// Uses false by default to represent keys with no trailing "=" sign.
|
||||
$q->add($key, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Use the duplicate aggregator if duplicates were found and not using PHP style arrays
|
||||
if ($foundDuplicates && !$foundPhpStyle) {
|
||||
$q->setAggregator(new DuplicateAggregator());
|
||||
}
|
||||
|
||||
return $q;
|
||||
}
|
||||
|
||||
|
|
@ -77,6 +88,7 @@ class QueryString extends Collection
|
|||
* Convert the query string parameters to a query string string
|
||||
*
|
||||
* @return string
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
|
|
@ -84,21 +96,12 @@ class QueryString extends Collection
|
|||
return '';
|
||||
}
|
||||
|
||||
$queryString = '';
|
||||
|
||||
$queryList = array();
|
||||
foreach ($this->prepareData($this->data) as $name => $value) {
|
||||
foreach ((array) $value as $v) {
|
||||
if ($queryString) {
|
||||
$queryString .= $this->fieldSeparator;
|
||||
}
|
||||
$queryString .= $name;
|
||||
if ($v !== self::BLANK) {
|
||||
$queryString .= $this->valueSeparator . $v;
|
||||
}
|
||||
}
|
||||
$queryList[] = $this->convertKvp($name, $value);
|
||||
}
|
||||
|
||||
return $queryString;
|
||||
return implode($this->fieldSeparator, $queryList);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -255,7 +258,10 @@ class QueryString extends Collection
|
|||
|
||||
$temp = array();
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
if ($value === false || $value === null) {
|
||||
// False and null will not include the "=". Use an empty string to include the "=".
|
||||
$temp[$this->encodeValue($key)] = $value;
|
||||
} elseif (is_array($value)) {
|
||||
$temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this));
|
||||
} else {
|
||||
$temp[$this->encodeValue($key)] = $this->encodeValue($value);
|
||||
|
|
@ -264,4 +270,28 @@ class QueryString extends Collection
|
|||
|
||||
return $temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a key value pair that can contain strings, nulls, false, or arrays
|
||||
* into a single string.
|
||||
*
|
||||
* @param string $name Name of the field
|
||||
* @param mixed $value Value of the field
|
||||
* @return string
|
||||
*/
|
||||
private function convertKvp($name, $value)
|
||||
{
|
||||
if ($value === self::BLANK || $value === null || $value === false) {
|
||||
return $name;
|
||||
} elseif (!is_array($value)) {
|
||||
return $name . $this->valueSeparator . $value;
|
||||
}
|
||||
|
||||
$result = '';
|
||||
foreach ($value as $v) {
|
||||
$result .= $this->convertKvp($name, $v) . $this->fieldSeparator;
|
||||
}
|
||||
|
||||
return rtrim($result, $this->fieldSeparator);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ class ReadLimitEntityBody extends AbstractEntityBodyDecorator
|
|||
{
|
||||
parent::__construct($body);
|
||||
$this->setLimit($limit)->setOffset($offset);
|
||||
$this->body->seek($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -36,7 +35,8 @@ class ReadLimitEntityBody extends AbstractEntityBodyDecorator
|
|||
|
||||
public function isConsumed()
|
||||
{
|
||||
return (($this->offset + $this->limit) - $this->body->ftell()) <= 0;
|
||||
return $this->body->isConsumed() ||
|
||||
($this->body->ftell() >= $this->offset + $this->limit);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ class RedirectPlugin implements EventSubscriberInterface
|
|||
// Trace the original request based on parameter history
|
||||
$original = $this->getOriginalRequest($request);
|
||||
|
||||
// Terminating condition to set the effective repsonse on the original request
|
||||
// Terminating condition to set the effective response on the original request
|
||||
if (!$response->isRedirect() || !$response->hasHeader('Location')) {
|
||||
if ($request !== $original) {
|
||||
// This is a terminating redirect response, so set it on the original request
|
||||
|
|
@ -123,9 +123,9 @@ class RedirectPlugin implements EventSubscriberInterface
|
|||
$redirectRequest = null;
|
||||
$strict = $original->getParams()->get(self::STRICT_REDIRECTS);
|
||||
|
||||
// Use a GET request if this is an entity enclosing request and we are not forcing RFC compliance, but rather
|
||||
// emulating what all browsers would do
|
||||
if ($request instanceof EntityEnclosingRequestInterface && !$strict && $statusCode <= 302) {
|
||||
// Switch method to GET for 303 redirects. 301 and 302 redirects also switch to GET unless we are forcing RFC
|
||||
// compliance to emulate what most browsers do. NOTE: IE only switches methods on 301/302 when coming from a POST.
|
||||
if ($request instanceof EntityEnclosingRequestInterface && ($statusCode == 303 || (!$strict && $statusCode <= 302))) {
|
||||
$redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET');
|
||||
} else {
|
||||
$redirectRequest = clone $request;
|
||||
|
|
@ -141,7 +141,7 @@ class RedirectPlugin implements EventSubscriberInterface
|
|||
$originalUrl = $redirectRequest->getUrl(true);
|
||||
// Remove query string parameters and just take what is present on the redirect Location header
|
||||
$originalUrl->getQuery()->clear();
|
||||
$location = $originalUrl->combine((string) $location);
|
||||
$location = $originalUrl->combine((string) $location, true);
|
||||
}
|
||||
|
||||
$redirectRequest->setUrl($location);
|
||||
|
|
@ -174,7 +174,7 @@ class RedirectPlugin implements EventSubscriberInterface
|
|||
/**
|
||||
* Prepare the request for redirection and enforce the maximum number of allowed redirects per client
|
||||
*
|
||||
* @param RequestInterface $original Origina request
|
||||
* @param RequestInterface $original Original request
|
||||
* @param RequestInterface $request Request to prepare and validate
|
||||
* @param Response $response The current response
|
||||
*
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue