namespace Aws\Common;
+use Aws\Common\Facade\Facade;
use Guzzle\Service\Builder\ServiceBuilder;
use Guzzle\Service\Builder\ServiceBuilderLoader;
/**
* @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
*/
public function enableFacades($namespace = null)
{
- $facadeClass = 'Aws\\Common\\Facade\\Facade';
- if (class_exists($facadeClass)) {
- $facadeClass::mountFacades($this, $namespace);
- }
+ Facade::mountFacades($this, $namespace);
return $this;
}
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;
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;
// 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);
return $scheme . '://' . $regions[$region]['hostname'];
}
- /**
- * {@inheritdoc}
- */
public function getCredentials()
{
return $this->credentials;
}
- /**
- * {@inheritdoc}
- */
public function setCredentials(CredentialsInterface $credentials)
{
$formerCredentials = $this->credentials;
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();
return $this;
}
- /**
- * {@inheritdoc}
- */
public function waitUntil($waiter, array $input = array())
{
$this->getWaiter($waiter, $input)->wait();
return $this;
}
- /**
- * {@inheritdoc}
- */
public function getWaiter($waiter, array $input = array())
{
return $this->getWaiterFactory()->build($waiter)
->setConfig($input);
}
- /**
- * {@inheritdoc}
- */
public function setWaiterFactory(WaiterFactoryInterface $waiterFactory)
{
$this->waiterFactory = $waiterFactory;
return $this;
}
- /**
- * {@inheritdoc}
- */
public function getWaiterFactory()
{
if (!$this->waiterFactory) {
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;
}
+ public function getApiVersion()
+ {
+ return $this->serviceDescription->getApiVersion();
+ }
+
/**
* {@inheritdoc}
+ * @throws \Aws\Common\Exception\TransferException
*/
- public function getApiVersion()
+ public function send($requests)
{
- return $this->serviceDescription->getApiVersion();
+ 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;
+ }
}
}
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;
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;
(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) {
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()
}
/**
- * 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
*/
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;
+ }
}
*
* 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
*
namespace Aws\Common\Client;
+use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Event;
use Guzzle\Http\EntityBody;
use Guzzle\Service\Command\AbstractCommand as Command;
* 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)
{
$body = fopen($source, 'r');
}
+ // Prepare the body parameter and remove the source file parameter
if (null !== $body) {
- $body = EntityBody::factory($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.");
}
-
- // Prepare the body parameter and remove the source file parameter
- $command->remove($this->sourceParameter);
- $command->set($this->bodyParameter, $body);
}
}
}
*/
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);
} 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 {
*/
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;
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
{
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;
/**
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
// 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 {
// 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
$this->ttd = $expiration;
}
- /**
- * {@inheritdoc}
- */
public function serialize()
{
return json_encode(array(
));
}
- /**
- * {@inheritdoc}
- */
public function unserialize($serialized)
{
$data = json_decode($serialized, true);
$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;
return $this;
}
- /**
- * {@inheritdoc}
- */
public function setSecretKey($secret)
{
$this->secret = $secret;
return $this;
}
- /**
- * {@inheritdoc}
- */
public function setSecurityToken($token)
{
$this->token = $token;
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);
+ }
}
--- /dev/null
+<?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.
+ }
+}
*/
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
*/
* 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
{
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';
}
--- /dev/null
+<?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 {}
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
}
}
+class CloudTrail extends Facade
+{
+ public static function getServiceBuilderKey()
+ {
+ return 'cloudtrail';
+ }
+}
+
class CloudWatch extends Facade
{
public static function getServiceBuilderKey()
}
}
+class Kinesis extends Facade
+{
+ public static function getServiceBuilderKey()
+ {
+ return 'kinesis';
+ }
+}
+
class OpsWorks extends Facade
{
public static function getServiceBuilderKey()
* @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)
{
* @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)
{
$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);
{
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
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
*/
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)) {
}
}
- /**
- * {@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);
}
}
/**
* @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
/**
* @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;
}
+ public function canBuild(CommandInterface $command)
+ {
+ if ($this->primaryIteratorFactory) {
+ return $this->primaryIteratorFactory->canBuild($command);
+ } else {
+ return isset($this->config[$command->getName()]);
+ }
+ }
+
/**
- * {@inheritdoc}
+ * @param array $config The config for a single operation
+ *
+ * @return array The modified config with legacy options translated
*/
- public function canBuild(CommandInterface $command)
+ private function translateLegacyConfigOptions($config)
{
- return ($this->primaryIteratorFactory && $this->primaryIteratorFactory->canBuild($command))
- || $this->operations->hasKey($command->getName());
+ foreach (self::$legacyConfigOptions as $legacyOption => $newOption) {
+ if (isset($config[$legacyOption])) {
+ $config[$newOption] = $config[$legacyOption];
+ unset($config[$legacyOption]);
+ }
+ }
+
+ return $config;
}
}
'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',
'class' => 'Aws\Glacier\GlacierClient'
),
+ 'kinesis' => array(
+ 'alias' => 'Kinesis',
+ 'extends' => 'default_settings',
+ 'class' => 'Aws\Kinesis\KinesisClient'
+ ),
+
'iam' => array(
'alias' => 'Iam',
'extends' => 'default_settings',
'class' => 'Aws\Route53\Route53Client'
),
+ 'route53domains' => array(
+ 'alias' => 'Route53Domains',
+ 'extends' => 'default_settings',
+ 'class' => 'Aws\Route53Domains\Route53DomainsClient'
+ ),
+
's3' => array(
'alias' => 'S3',
'extends' => 'default_settings',
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');
}
}
* @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
+ );
}
*/
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());
*
* @return string
*/
- public function getCanonicalizedParameterString(RequestInterface $request)
+ private function getCanonicalizedParameterString(RequestInterface $request)
{
if ($request->getMethod() == 'POST') {
$params = $request->getPostFields()->toArray();
+++ /dev/null
-<?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));
- }
-}
/**
* 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
}
// 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
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 */
+ protected $cacheSize = 0;
+
/**
- * @var int Size of the hash cache
+ * @param string $serviceName Bind the signing to a particular service name
+ * @param string $regionName Bind the signing to a particular region name
*/
- protected $cacheSize = 0;
+ 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
return $this;
}
- /**
- * {@inheritdoc}
- */
public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
{
- // Refresh the cached timestamp
- $this->getTimestamp(true);
-
- $longDate = $this->getDateTime(DateFormat::ISO8601);
- $shortDate = $this->getDateTime(DateFormat::SHORT);
+ $timestamp = $this->getTimestamp();
+ $longDate = gmdate(DateFormat::ISO8601, $timestamp);
+ $shortDate = substr($longDate, 0, 8);
- // 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
}
// 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());
$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]);
+ }
}
}
// 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,
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);
+ }
+ }
+ }
}
*/
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;
+++ /dev/null
-# 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
+++ /dev/null
-# 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.
namespace Aws\S3\Command;
-use Aws\S3\Exception\RedirectException;
use Guzzle\Service\Command\OperationCommand;
use Guzzle\Service\Resource\Model;
use Guzzle\Common\Event;
--- /dev/null
+<?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';
+}
class Status extends Enum
{
const ENABLED = 'Enabled';
- const DISABLED = 'Disabled';
+ const SUSPENDED = 'Suspended';
}
* 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
{
* 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
{
* 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
{
* 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
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;
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
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(
namespace Aws\S3\Model;
-use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Enum\DateFormat;
use Aws\S3\S3Client;
use Guzzle\Common\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
$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(
// 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(
$policy['conditions'][] = array(
'success_action_status' => (string) $status
);
- $options->remove('success_action_status');
+ unset($options['success_action_status']);
}
// Add other options
}
}
- // 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;
}
{
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
+ ));
+ }
}
'https' => true,
'hostname' => 's3-sa-east-1.amazonaws.com',
),
+ 'cn-north-1' => array(
+ 'http' => true,
+ 'https' => true,
+ 'hostname' => 's3.cn-north-1.amazonaws.com.cn',
+ ),
'us-gov-west-1' => array(
'http' => true,
'https' => true,
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'AbortMultipartUploadOutput',
'responseType' => 'model',
- 'summary' => 'Aborts a multipart upload.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadAbort.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadAbort.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'CompleteMultipartUploadOutput',
'responseType' => 'model',
- 'summary' => 'Completes a multipart upload by assembling previously uploaded parts.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadComplete.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadComplete.html',
'data' => array(
'xmlRoot' => array(
- 'name' => 'MultipartUpload',
+ 'name' => 'CompleteMultipartUpload',
'namespaces' => array(
'http://s3.amazonaws.com/doc/2006-03-01/',
),
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'CompletedPart',
'type' => 'object',
'sentAs' => 'Part',
'properties' => array(
'ETag' => array(
- 'description' => 'Entity tag returned when the part was uploaded.',
'type' => 'string',
),
'PartNumber' => array(
- 'description' => 'Part number that identifies the part.',
'type' => 'numeric',
),
),
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'CopyObjectOutput',
'responseType' => 'model',
- 'summary' => 'Creates a copy of an object that is already stored in Amazon S3.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectCOPY.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectCOPY.html',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'CopyObjectRequest',
+ 'namespaces' => array(
+ 'http://s3.amazonaws.com/doc/2006-03-01/',
+ ),
+ ),
+ ),
'parameters' => array(
'ACL' => array(
- 'description' => 'The canned ACL to apply to the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-acl',
- 'enum' => array(
- 'private',
- 'public-read',
- 'public-read-write',
- 'authenticated-read',
- 'bucket-owner-read',
- 'bucket-owner-full-control',
- ),
),
'Bucket' => array(
'required' => true,
'location' => 'uri',
),
'CacheControl' => array(
- 'description' => 'Specifies caching behavior along the request/reply chain.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Cache-Control',
),
'ContentDisposition' => array(
- 'description' => 'Specifies presentational information for the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Disposition',
),
'ContentEncoding' => array(
- 'description' => 'Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Encoding',
),
'ContentLanguage' => array(
- 'description' => 'The language the content is in.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Language',
),
'ContentType' => array(
- 'description' => 'A standard MIME type describing the format of the object data.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Type',
),
'CopySource' => array(
'required' => true,
- 'description' => 'The name of the source bucket and key name of the source object, separated by a slash (/). Must be URL-encoded.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-copy-source',
),
'CopySourceIfMatch' => array(
- 'description' => 'Copies the object if its entity tag (ETag) matches the specified tag.',
- 'type' => array(
- 'object',
- 'string',
- 'integer',
- ),
- 'format' => 'date-time-http',
+ 'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-copy-source-if-match',
),
'CopySourceIfModifiedSince' => array(
- 'description' => 'Copies the object if it has been modified since the specified time.',
'type' => array(
'object',
'string',
'sentAs' => 'x-amz-copy-source-if-modified-since',
),
'CopySourceIfNoneMatch' => array(
- 'description' => 'Copies the object if its entity tag (ETag) is different than the specified ETag.',
- 'type' => array(
- 'object',
- 'string',
- 'integer',
- ),
- 'format' => 'date-time-http',
+ 'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-copy-source-if-none-match',
),
'CopySourceIfUnmodifiedSince' => array(
- 'description' => 'Copies the object if it hasn\'t been modified since the specified time.',
'type' => array(
'object',
'string',
'sentAs' => 'x-amz-copy-source-if-unmodified-since',
),
'Expires' => array(
- 'description' => 'The date and time at which the object is no longer cacheable.',
'type' => array(
'object',
'string',
'location' => 'header',
),
'GrantFullControl' => array(
- 'description' => 'Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-full-control',
),
'GrantRead' => array(
- 'description' => 'Allows grantee to read the object data and its metadata.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read',
),
'GrantReadACP' => array(
- 'description' => 'Allows grantee to read the object ACL.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read-acp',
),
'GrantWriteACP' => array(
- 'description' => 'Allows grantee to write the ACL for the applicable object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-write-acp',
),
),
'Metadata' => array(
- 'description' => 'A map of metadata to store with the object in S3.',
'type' => 'object',
'location' => 'header',
'sentAs' => 'x-amz-meta-',
'additionalProperties' => array(
- 'description' => 'The metadata key. This will be prefixed with x-amz-meta- before sending to S3 as a header. The x-amz-meta- header will be stripped from the key when retrieving headers.',
'type' => 'string',
),
),
'MetadataDirective' => array(
- 'description' => 'Specifies whether the metadata is copied from the source object or replaced with metadata provided in the request.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-metadata-directive',
- 'enum' => array(
- 'COPY',
- 'REPLACE',
- ),
),
'ServerSideEncryption' => array(
- 'description' => 'The Server-side encryption algorithm used when storing this object in S3.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption',
- 'enum' => array(
- 'AES256',
- ),
),
'StorageClass' => array(
- 'description' => 'The type of storage to use for the object. Defaults to \'STANDARD\'.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-storage-class',
- 'enum' => array(
- 'STANDARD',
- 'REDUCED_REDUNDANCY',
- ),
),
'WebsiteRedirectLocation' => array(
- 'description' => 'If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-website-redirect-location',
),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
+ ),
+ 'CopySourceSSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-algorithm',
+ ),
+ 'CopySourceSSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-key',
+ ),
+ 'CopySourceSSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-key-MD5',
+ ),
'ACP' => array(
- 'description' => 'Pass an Aws\\S3\\Model\\Acp object as an alternative way to add access control policy headers to the operation',
'type' => 'object',
'additionalProperties' => true,
),
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'CreateBucketOutput',
'responseType' => 'model',
- 'summary' => 'Creates a new bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUT.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUT.html',
'data' => array(
'xmlRoot' => array(
'name' => 'CreateBucketConfiguration',
),
'parameters' => array(
'ACL' => array(
- 'description' => 'The canned ACL to apply to the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-acl',
- 'enum' => array(
- 'private',
- 'public-read',
- 'public-read-write',
- 'authenticated-read',
- 'bucket-owner-read',
- 'bucket-owner-full-control',
- ),
),
'Bucket' => array(
'required' => true,
'location' => 'uri',
),
'LocationConstraint' => array(
- 'description' => 'Specifies the region where the bucket will be created.',
'type' => 'string',
'location' => 'xml',
- 'enum' => array(
- 'EU',
- 'eu-west-1',
- 'us-west-1',
- 'us-west-2',
- 'ap-southeast-1',
- 'ap-northeast-1',
- 'sa-east-1',
- ),
),
'GrantFullControl' => array(
- 'description' => 'Allows grantee the read, write, read ACP, and write ACP permissions on the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-full-control',
),
'GrantRead' => array(
- 'description' => 'Allows grantee to list the objects in the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read',
),
'GrantReadACP' => array(
- 'description' => 'Allows grantee to read the bucket ACL.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read-acp',
),
'GrantWrite' => array(
- 'description' => 'Allows grantee to create, overwrite, and delete any object in the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-write',
),
'GrantWriteACP' => array(
- 'description' => 'Allows grantee to write the ACL for the applicable bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-write-acp',
),
'ACP' => array(
- 'description' => 'Pass an Aws\\S3\\Model\\Acp object as an alternative way to add access control policy headers to the operation',
'type' => 'object',
'additionalProperties' => true,
),
),
'CreateMultipartUpload' => array(
'httpMethod' => 'POST',
- 'uri' => '/{Bucket}{/Key*}',
+ 'uri' => '/{Bucket}{/Key*}?uploads',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'CreateMultipartUploadOutput',
'responseType' => 'model',
- 'summary' => 'Initiates a multipart upload and returns an upload ID.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadInitiate.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadInitiate.html',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'CreateMultipartUploadRequest',
+ 'namespaces' => array(
+ 'http://s3.amazonaws.com/doc/2006-03-01/',
+ ),
+ ),
+ ),
'parameters' => array(
'ACL' => array(
- 'description' => 'The canned ACL to apply to the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-acl',
- 'enum' => array(
- 'private',
- 'public-read',
- 'public-read-write',
- 'authenticated-read',
- 'bucket-owner-read',
- 'bucket-owner-full-control',
- ),
),
'Bucket' => array(
'required' => true,
'location' => 'uri',
),
'CacheControl' => array(
- 'description' => 'Specifies caching behavior along the request/reply chain.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Cache-Control',
),
'ContentDisposition' => array(
- 'description' => 'Specifies presentational information for the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Disposition',
),
'ContentEncoding' => array(
- 'description' => 'Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Encoding',
),
'ContentLanguage' => array(
- 'description' => 'The language the content is in.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Language',
),
'ContentType' => array(
- 'description' => 'A standard MIME type describing the format of the object data.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Type',
),
'Expires' => array(
- 'description' => 'The date and time at which the object is no longer cacheable.',
'type' => array(
'object',
'string',
'location' => 'header',
),
'GrantFullControl' => array(
- 'description' => 'Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-full-control',
),
'GrantRead' => array(
- 'description' => 'Allows grantee to read the object data and its metadata.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read',
),
'GrantReadACP' => array(
- 'description' => 'Allows grantee to read the object ACL.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read-acp',
),
'GrantWriteACP' => array(
- 'description' => 'Allows grantee to write the ACL for the applicable object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-write-acp',
),
),
'Metadata' => array(
- 'description' => 'A map of metadata to store with the object in S3.',
'type' => 'object',
'location' => 'header',
'sentAs' => 'x-amz-meta-',
'additionalProperties' => array(
- 'description' => 'The metadata key. This will be prefixed with x-amz-meta- before sending to S3 as a header. The x-amz-meta- header will be stripped from the key when retrieving headers.',
'type' => 'string',
),
),
'ServerSideEncryption' => array(
- 'description' => 'The Server-side encryption algorithm used when storing this object in S3.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption',
- 'enum' => array(
- 'AES256',
- ),
),
'StorageClass' => array(
- 'description' => 'The type of storage to use for the object. Defaults to \'STANDARD\'.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-storage-class',
- 'enum' => array(
- 'STANDARD',
- 'REDUCED_REDUNDANCY',
- ),
),
'WebsiteRedirectLocation' => array(
- 'description' => 'If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-website-redirect-location',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'uploads',
- 'default' => '_guzzle_blank_',
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
'ACP' => array(
- 'description' => 'Pass an Aws\\S3\\Model\\Acp object as an alternative way to add access control policy headers to the operation',
'type' => 'object',
'additionalProperties' => true,
),
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'DeleteBucketOutput',
'responseType' => 'model',
- 'summary' => 'Deletes the bucket. All objects (including all object versions and Delete Markers) in the bucket must be deleted before the bucket itself can be deleted.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETE.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETE.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
),
'DeleteBucketCors' => array(
'httpMethod' => 'DELETE',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?cors',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'DeleteBucketCorsOutput',
'responseType' => 'model',
- 'summary' => 'Deletes the cors configuration information set for the bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETEcors.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETEcors.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'cors',
- 'default' => '_guzzle_blank_',
- ),
),
),
'DeleteBucketLifecycle' => array(
'httpMethod' => 'DELETE',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?lifecycle',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'DeleteBucketLifecycleOutput',
'responseType' => 'model',
- 'summary' => 'Deletes the lifecycle configuration from the bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETElifecycle.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETElifecycle.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'lifecycle',
- 'default' => '_guzzle_blank_',
- ),
),
),
'DeleteBucketPolicy' => array(
'httpMethod' => 'DELETE',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?policy',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'DeleteBucketPolicyOutput',
'responseType' => 'model',
- 'summary' => 'Deletes the policy from the bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETEpolicy.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETEpolicy.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'policy',
- 'default' => '_guzzle_blank_',
- ),
),
),
'DeleteBucketTagging' => array(
'httpMethod' => 'DELETE',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?tagging',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'DeleteBucketTaggingOutput',
'responseType' => 'model',
- 'summary' => 'Deletes the tags from the bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETEtagging.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETEtagging.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'tagging',
- 'default' => '_guzzle_blank_',
- ),
),
),
'DeleteBucketWebsite' => array(
'httpMethod' => 'DELETE',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?website',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'DeleteBucketWebsiteOutput',
'responseType' => 'model',
- 'summary' => 'This operation removes the website configuration from the bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETEwebsite.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETEwebsite.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'website',
- 'default' => '_guzzle_blank_',
- ),
),
),
'DeleteObject' => array(
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'DeleteObjectOutput',
'responseType' => 'model',
- 'summary' => 'Removes the null version (if there is one) of an object and inserts a delete marker, which becomes the latest version of the object. If there isn\'t a null version, Amazon S3 does not remove any objects.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectDELETE.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'Aws\\S3\\S3Client::explodeKey',
),
),
+ 'MFA' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-mfa',
+ ),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'versionId',
+ ),
),
),
'DeleteObjects' => array(
'httpMethod' => 'POST',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?delete',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'DeleteObjectsOutput',
'responseType' => 'model',
- 'summary' => 'This operation enables you to delete multiple objects from a bucket using a single HTTP request. You may specify up to 1000 keys.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/multiobjectdeleteapi.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html',
'data' => array(
'xmlRoot' => array(
'name' => 'Delete',
'http://s3.amazonaws.com/doc/2006-03-01/',
),
),
+ 'contentMd5' => true,
),
'parameters' => array(
'Bucket' => array(
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'ObjectIdentifier',
'type' => 'object',
'sentAs' => 'Object',
'properties' => array(
'Key' => array(
'required' => true,
- 'description' => 'Key name of the object to delete.',
'type' => 'string',
),
'VersionId' => array(
- 'description' => 'VersionId for the specific version of the object to delete.',
'type' => 'string',
),
),
),
),
'Quiet' => array(
- 'description' => 'Element to enable quiet mode for the request. When you add this element, you must set its value to true.',
'type' => 'boolean',
'format' => 'boolean-string',
'location' => 'xml',
),
'MFA' => array(
- 'description' => 'The concatenation of the authentication device\'s serial number, a space, and the value that is displayed on your authentication device.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-mfa',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'delete',
- 'default' => '_guzzle_blank_',
- ),
- 'ContentMD5' => array(
- 'required' => true,
- 'default' => true,
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
),
'GetBucketAcl' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?acl',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetBucketAclOutput',
'responseType' => 'model',
- 'summary' => 'Gets the access control policy for the bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETacl.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETacl.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'acl',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
),
'GetBucketCors' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?cors',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetBucketCorsOutput',
'responseType' => 'model',
- 'summary' => 'Returns the cors configuration for the bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETcors.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETcors.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'cors',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
),
'GetBucketLifecycle' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?lifecycle',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetBucketLifecycleOutput',
'responseType' => 'model',
- 'summary' => 'Returns the lifecycle configuration information set on the bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETlifecycle.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETlifecycle.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'lifecycle',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
),
'GetBucketLocation' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?location',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetBucketLocationOutput',
'responseType' => 'model',
- 'summary' => 'Returns the region the bucket resides in.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETlocation.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETlocation.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'location',
- 'default' => '_guzzle_blank_',
- ),
),
),
'GetBucketLogging' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?logging',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetBucketLoggingOutput',
'responseType' => 'model',
- 'summary' => 'Returns the logging status of a bucket and the permissions users have to view and modify that status. To use GET, you must be the bucket owner.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETlogging.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETlogging.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'logging',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
),
'GetBucketNotification' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?notification',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetBucketNotificationOutput',
'responseType' => 'model',
- 'summary' => 'Return the notification configuration of a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETnotification.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETnotification.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'notification',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
),
'GetBucketPolicy' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?policy',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetBucketPolicyOutput',
'responseType' => 'model',
- 'summary' => 'Returns the policy of a specified bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETpolicy.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETpolicy.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'policy',
- 'default' => '_guzzle_blank_',
- ),
),
),
'GetBucketRequestPayment' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?requestPayment',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetBucketRequestPaymentOutput',
'responseType' => 'model',
- 'summary' => 'Returns the request payment configuration of a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTrequestPaymentGET.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTrequestPaymentGET.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'requestPayment',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
),
'GetBucketTagging' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?tagging',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetBucketTaggingOutput',
'responseType' => 'model',
- 'summary' => 'Returns the tag set associated with the bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETtagging.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETtagging.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'tagging',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
),
'GetBucketVersioning' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?versioning',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetBucketVersioningOutput',
'responseType' => 'model',
- 'summary' => 'Returns the versioning state of a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETversioningStatus.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETversioningStatus.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'versioning',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
),
'GetBucketWebsite' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?website',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetBucketWebsiteOutput',
'responseType' => 'model',
- 'summary' => 'Returns the website configuration for a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETwebsite.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETwebsite.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'website',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetObjectOutput',
'responseType' => 'model',
- 'summary' => 'Retrieves objects from Amazon S3.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGET.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'location' => 'uri',
),
'IfMatch' => array(
- 'description' => 'Return the object only if its entity tag (ETag) is the same as the one specified, otherwise return a 412 (precondition failed).',
'type' => 'string',
'location' => 'header',
'sentAs' => 'If-Match',
),
'IfModifiedSince' => array(
- 'description' => 'Return the object only if it has been modified since the specified time, otherwise return a 304 (not modified).',
'type' => array(
'object',
'string',
'sentAs' => 'If-Modified-Since',
),
'IfNoneMatch' => array(
- 'description' => 'Return the object only if its entity tag (ETag) is different from the one specified, otherwise return a 304 (not modified).',
'type' => 'string',
'location' => 'header',
'sentAs' => 'If-None-Match',
),
'IfUnmodifiedSince' => array(
- 'description' => 'Return the object only if it has not been modified since the specified time, otherwise return a 412 (precondition failed).',
'type' => array(
'object',
'string',
),
),
'Range' => array(
- 'description' => 'Downloads the specified range bytes of an object. For more information about the HTTP Range header, go to http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.',
'type' => 'string',
'location' => 'header',
),
'ResponseCacheControl' => array(
- 'description' => 'Sets the Cache-Control header of the response.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'response-cache-control',
),
'ResponseContentDisposition' => array(
- 'description' => 'Sets the Content-Disposition header of the response',
'type' => 'string',
'location' => 'query',
'sentAs' => 'response-content-disposition',
),
'ResponseContentEncoding' => array(
- 'description' => 'Sets the Content-Encoding header of the response.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'response-content-encoding',
),
'ResponseContentLanguage' => array(
- 'description' => 'Sets the Content-Language header of the response.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'response-content-language',
),
'ResponseContentType' => array(
- 'description' => 'Sets the Content-Type header of the response.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'response-content-type',
),
'ResponseExpires' => array(
- 'description' => 'Sets the Expires header of the response.',
'type' => array(
'object',
'string',
'sentAs' => 'response-expires',
),
'VersionId' => array(
- 'description' => 'VersionId used to reference a specific version of the object.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'versionId',
),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
+ ),
'SaveAs' => array(
- 'description' => 'Specify where the contents of the object should be downloaded. Can be the path to a file, a resource returned by fopen, or a Guzzle\\Http\\EntityBodyInterface object.',
'location' => 'response_body',
),
),
),
'GetObjectAcl' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}{/Key*}',
+ 'uri' => '/{Bucket}{/Key*}?acl',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetObjectAclOutput',
'responseType' => 'model',
- 'summary' => 'Returns the access control list (ACL) of an object.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGETacl.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGETacl.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
),
),
'VersionId' => array(
- 'description' => 'VersionId used to reference a specific version of the object.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'versionId',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'acl',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
),
'GetObjectTorrent' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}{/Key*}',
+ 'uri' => '/{Bucket}{/Key*}?torrent',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'GetObjectTorrentOutput',
'responseType' => 'model',
- 'summary' => 'Return torrent files from a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGETtorrent.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGETtorrent.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'Aws\\S3\\S3Client::explodeKey',
),
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'torrent',
- 'default' => '_guzzle_blank_',
- ),
),
),
'HeadBucket' => array(
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'HeadBucketOutput',
'responseType' => 'model',
- 'summary' => 'This operation is useful to determine if a bucket exists and you have permission to access it.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketHEAD.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketHEAD.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'HeadObjectOutput',
'responseType' => 'model',
- 'summary' => 'The HEAD operation retrieves metadata from an object without returning the object itself. This operation is useful if you\'re only interested in an object\'s metadata. To use HEAD, you must have READ access to the object.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectHEAD.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectHEAD.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'location' => 'uri',
),
'IfMatch' => array(
- 'description' => 'Return the object only if its entity tag (ETag) is the same as the one specified, otherwise return a 412 (precondition failed).',
'type' => 'string',
'location' => 'header',
'sentAs' => 'If-Match',
),
'IfModifiedSince' => array(
- 'description' => 'Return the object only if it has been modified since the specified time, otherwise return a 304 (not modified).',
'type' => array(
'object',
'string',
'sentAs' => 'If-Modified-Since',
),
'IfNoneMatch' => array(
- 'description' => 'Return the object only if its entity tag (ETag) is different from the one specified, otherwise return a 304 (not modified).',
'type' => 'string',
'location' => 'header',
'sentAs' => 'If-None-Match',
),
'IfUnmodifiedSince' => array(
- 'description' => 'Return the object only if it has not been modified since the specified time, otherwise return a 412 (precondition failed).',
'type' => array(
'object',
'string',
),
),
'Range' => array(
- 'description' => 'Downloads the specified range bytes of an object. For more information about the HTTP Range header, go to http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.',
'type' => 'string',
'location' => 'header',
),
'VersionId' => array(
- 'description' => 'VersionId used to reference a specific version of the object.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'versionId',
),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
+ ),
),
'errorResponses' => array(
array(
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'ListBucketsOutput',
'responseType' => 'model',
- 'summary' => 'Returns a list of all buckets owned by the authenticated sender of the request.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTServiceGET.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTServiceGET.html',
'parameters' => array(
'command.expects' => array(
'static' => true,
),
'ListMultipartUploads' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?uploads',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'ListMultipartUploadsOutput',
'responseType' => 'model',
- 'summary' => 'This operation lists in-progress multipart uploads.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadListMPUpload.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadListMPUpload.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'location' => 'uri',
),
'Delimiter' => array(
- 'description' => 'Character you use to group keys.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'delimiter',
),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'encoding-type',
+ ),
'KeyMarker' => array(
- 'description' => 'Together with upload-id-marker, this parameter specifies the multipart upload after which listing should begin.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'key-marker',
),
'MaxUploads' => array(
- 'description' => 'Sets the maximum number of multipart uploads, from 1 to 1,000, to return in the response body. 1,000 is the maximum number of uploads that can be returned in a response.',
'type' => 'numeric',
'location' => 'query',
'sentAs' => 'max-uploads',
),
'Prefix' => array(
- 'description' => 'Lists in-progress uploads only for those keys that begin with the specified prefix.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'prefix',
),
'UploadIdMarker' => array(
- 'description' => 'Together with key-marker, specifies the multipart upload after which listing should begin. If key-marker is not specified, the upload-id-marker parameter is ignored.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'upload-id-marker',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'uploads',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
),
'ListObjectVersions' => array(
'httpMethod' => 'GET',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?versions',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'ListObjectVersionsOutput',
'responseType' => 'model',
- 'summary' => 'Returns metadata about all of the versions of objects in a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETVersion.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETVersion.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'location' => 'uri',
),
'Delimiter' => array(
- 'description' => 'A delimiter is a character you use to group keys.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'delimiter',
),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'encoding-type',
+ ),
'KeyMarker' => array(
- 'description' => 'Specifies the key to start with when listing objects in a bucket.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'key-marker',
),
'MaxKeys' => array(
- 'description' => 'Sets the maximum number of keys returned in the response. The response might contain fewer keys but will never contain more.',
'type' => 'numeric',
'location' => 'query',
'sentAs' => 'max-keys',
),
'Prefix' => array(
- 'description' => 'Limits the response to keys that begin with the specified prefix.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'prefix',
),
'VersionIdMarker' => array(
- 'description' => 'Specifies the object version you want to start listing from.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'version-id-marker',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'versions',
- 'default' => '_guzzle_blank_',
- ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'ListObjectsOutput',
'responseType' => 'model',
- 'summary' => 'Returns some or all (up to 1000) of the objects in a bucket. You can use the request parameters as selection criteria to return a subset of the objects in a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGET.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
'location' => 'uri',
),
'Delimiter' => array(
- 'description' => 'A delimiter is a character you use to group keys.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'delimiter',
),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'encoding-type',
+ ),
'Marker' => array(
- 'description' => 'Specifies the key to start with when listing objects in a bucket.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'marker',
),
'MaxKeys' => array(
- 'description' => 'Sets the maximum number of keys returned in the response. The response might contain fewer keys but will never contain more.',
'type' => 'numeric',
'location' => 'query',
'sentAs' => 'max-keys',
),
'Prefix' => array(
- 'description' => 'Limits the response to keys that begin with the specified prefix.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'prefix',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'ListPartsOutput',
'responseType' => 'model',
- 'summary' => 'Lists the parts that have been uploaded for a specific multipart upload.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadListParts.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadListParts.html',
'parameters' => array(
'Bucket' => array(
'required' => true,
),
),
'MaxParts' => array(
- 'description' => 'Sets the maximum number of parts to return.',
'type' => 'numeric',
'location' => 'query',
'sentAs' => 'max-parts',
),
'PartNumberMarker' => array(
- 'description' => 'Specifies the part after which listing should begin. Only parts with higher part numbers will be listed.',
- 'type' => 'string',
+ 'type' => 'numeric',
'location' => 'query',
'sentAs' => 'part-number-marker',
),
'UploadId' => array(
'required' => true,
- 'description' => 'Upload ID identifying the multipart upload whose parts are being listed.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'uploadId',
),
'PutBucketAcl' => array(
'httpMethod' => 'PUT',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?acl',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutBucketAclOutput',
'responseType' => 'model',
- 'summary' => 'Sets the permissions on a bucket using access control lists (ACL).',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTacl.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTacl.html',
'data' => array(
'xmlRoot' => array(
'name' => 'AccessControlPolicy',
),
'parameters' => array(
'ACL' => array(
- 'description' => 'The canned ACL to apply to the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-acl',
- 'enum' => array(
- 'private',
- 'public-read',
- 'public-read-write',
- 'authenticated-read',
- 'bucket-owner-read',
- 'bucket-owner-full-control',
- ),
),
'Grants' => array(
- 'description' => 'A list of grants.',
'type' => 'array',
'location' => 'xml',
'sentAs' => 'AccessControlList',
'type' => 'object',
'properties' => array(
'DisplayName' => array(
- 'description' => 'Screen name of the grantee.',
'type' => 'string',
),
'EmailAddress' => array(
- 'description' => 'Email address of the grantee.',
'type' => 'string',
),
'ID' => array(
- 'description' => 'The canonical user ID of the grantee.',
'type' => 'string',
),
'Type' => array(
'required' => true,
- 'description' => 'Type of grantee',
'type' => 'string',
'sentAs' => 'xsi:type',
'data' => array(
'xmlAttribute' => true,
'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
),
- 'enum' => array(
- 'CanonicalUser',
- 'AmazonCustomerByEmail',
- 'Group',
- ),
),
'URI' => array(
- 'description' => 'URI of the grantee group.',
'type' => 'string',
),
),
),
'Permission' => array(
- 'description' => 'Specifies the permission given to the grantee.',
'type' => 'string',
- 'enum' => array(
- 'FULL_CONTROL',
- 'WRITE',
- 'WRITE_ACP',
- 'READ',
- 'READ_ACP',
- ),
),
),
),
'type' => 'string',
'location' => 'uri',
),
- 'ContentMD5' => array(
- 'default' => true,
- ),
'GrantFullControl' => array(
- 'description' => 'Allows grantee the read, write, read ACP, and write ACP permissions on the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-full-control',
),
'GrantRead' => array(
- 'description' => 'Allows grantee to list the objects in the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read',
),
'GrantReadACP' => array(
- 'description' => 'Allows grantee to read the bucket ACL.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read-acp',
),
'GrantWrite' => array(
- 'description' => 'Allows grantee to create, overwrite, and delete any object in the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-write',
),
'GrantWriteACP' => array(
- 'description' => 'Allows grantee to write the ACL for the applicable bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-write-acp',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'acl',
- 'default' => '_guzzle_blank_',
- ),
'ACP' => array(
- 'description' => 'Pass an Aws\\S3\\Model\\Acp object as an alternative way to add an access control policy to the operation',
'type' => 'object',
'additionalProperties' => true,
),
),
'PutBucketCors' => array(
'httpMethod' => 'PUT',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?cors',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutBucketCorsOutput',
'responseType' => 'model',
- 'summary' => 'Sets the cors configuration for a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTcors.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTcors.html',
'data' => array(
'xmlRoot' => array(
'name' => 'CORSConfiguration',
'http://s3.amazonaws.com/doc/2006-03-01/',
),
),
+ 'contentMd5' => true,
),
'parameters' => array(
'Bucket' => array(
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'CORSRule',
'type' => 'object',
'sentAs' => 'CORSRule',
'properties' => array(
'AllowedHeaders' => array(
- 'description' => 'Specifies which headers are allowed in a pre-flight OPTIONS request.',
'type' => 'array',
'data' => array(
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'AllowedHeader',
'type' => 'string',
'sentAs' => 'AllowedHeader',
),
),
'AllowedMethods' => array(
- 'description' => 'Identifies HTTP methods that the domain/origin specified in the rule is allowed to execute.',
'type' => 'array',
'data' => array(
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'AllowedMethod',
'type' => 'string',
'sentAs' => 'AllowedMethod',
),
),
'AllowedOrigins' => array(
- 'description' => 'One or more origins you want customers to be able to access the bucket from.',
'type' => 'array',
'data' => array(
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'AllowedOrigin',
'type' => 'string',
'sentAs' => 'AllowedOrigin',
),
),
'ExposeHeaders' => array(
- 'description' => 'One or more headers in the response that you want customers to be able to access from their applications (for example, from a JavaScript XMLHttpRequest object).',
'type' => 'array',
'data' => array(
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'ExposeHeader',
'type' => 'string',
'sentAs' => 'ExposeHeader',
),
),
'MaxAgeSeconds' => array(
- 'description' => 'The time in seconds that your browser is to cache the preflight response for the specified resource.',
'type' => 'numeric',
),
),
),
),
- 'ContentMD5' => array(
- 'default' => true,
- ),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'cors',
- 'default' => '_guzzle_blank_',
- ),
),
),
'PutBucketLifecycle' => array(
'httpMethod' => 'PUT',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?lifecycle',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutBucketLifecycleOutput',
'responseType' => 'model',
- 'summary' => 'Sets lifecycle configuration for your bucket. If a lifecycle configuration exists, it replaces it.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTlifecycle.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTlifecycle.html',
'data' => array(
'xmlRoot' => array(
'name' => 'LifecycleConfiguration',
'http://s3.amazonaws.com/doc/2006-03-01/',
),
),
+ 'contentMd5' => true,
),
'parameters' => array(
'Bucket' => array(
'type' => 'string',
'location' => 'uri',
),
- 'ContentMD5' => array(
- 'default' => true,
- ),
'Rules' => array(
'required' => true,
'type' => 'array',
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'Rule',
'type' => 'object',
'sentAs' => 'Rule',
'properties' => array(
'type' => 'object',
'properties' => array(
'Date' => array(
- 'description' => 'Indicates at what date the object is to be moved or deleted. Should be in GMT ISO 8601 Format.',
'type' => array(
'object',
'string',
'format' => 'date-time',
),
'Days' => array(
- 'description' => 'Indicates the lifetime, in days, of the objects that are subject to the rule. The value must be a non-zero positive integer.',
'type' => 'numeric',
),
),
),
'ID' => array(
- 'description' => 'Unique identifier for the rule. The value cannot be longer than 255 characters.',
'type' => 'string',
),
'Prefix' => array(
'required' => true,
- 'description' => 'Prefix identifying one or more objects to which the rule applies.',
'type' => 'string',
),
'Status' => array(
'required' => true,
- 'description' => 'If \'Enabled\', the rule is currently being applied. If \'Disabled\', the rule is not currently being applied.',
'type' => 'string',
- 'enum' => array(
- 'Enabled',
- 'Disabled',
- ),
),
'Transition' => array(
'type' => 'object',
'properties' => array(
'Date' => array(
- 'description' => 'Indicates at what date the object is to be moved or deleted. Should be in GMT ISO 8601 Format.',
'type' => array(
'object',
'string',
'format' => 'date-time',
),
'Days' => array(
- 'description' => 'Indicates the lifetime, in days, of the objects that are subject to the rule. The value must be a non-zero positive integer.',
'type' => 'numeric',
),
'StorageClass' => array(
- 'description' => 'The class of storage used to store the object.',
'type' => 'string',
- 'enum' => array(
- 'STANDARD',
- 'REDUCED_REDUNDANCY',
- 'GLACIER',
- ),
+ ),
+ ),
+ ),
+ 'NoncurrentVersionTransition' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'NoncurrentDays' => array(
+ 'type' => 'numeric',
+ ),
+ 'StorageClass' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'NoncurrentVersionExpiration' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'NoncurrentDays' => array(
+ 'type' => 'numeric',
),
),
),
),
),
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'lifecycle',
- 'default' => '_guzzle_blank_',
- ),
),
),
'PutBucketLogging' => array(
'httpMethod' => 'PUT',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?logging',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutBucketLoggingOutput',
'responseType' => 'model',
- 'summary' => 'Set the logging parameters for a bucket and to specify permissions for who can view and modify the logging parameters. To set the logging status of a bucket, you must be the bucket owner.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTlogging.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTlogging.html',
'data' => array(
'xmlRoot' => array(
'name' => 'BucketLoggingStatus',
'http://s3.amazonaws.com/doc/2006-03-01/',
),
),
+ 'xmlAllowEmpty' => true,
),
'parameters' => array(
'Bucket' => array(
'location' => 'uri',
),
'LoggingEnabled' => array(
- 'required' => true,
'type' => 'object',
'location' => 'xml',
'properties' => array(
'TargetBucket' => array(
- 'description' => 'Specifies the bucket where you want Amazon S3 to store server access logs. You can have your logs delivered to any bucket that you own, including the same bucket that is being logged. You can also configure multiple buckets to deliver their logs to the same target bucket. In this case you should choose a different TargetPrefix for each source bucket so that the delivered log files can be distinguished by key.',
'type' => 'string',
),
'TargetGrants' => array(
'type' => 'object',
'properties' => array(
'DisplayName' => array(
- 'description' => 'Screen name of the grantee.',
'type' => 'string',
),
'EmailAddress' => array(
- 'description' => 'Email address of the grantee.',
'type' => 'string',
),
'ID' => array(
- 'description' => 'The canonical user ID of the grantee.',
'type' => 'string',
),
'Type' => array(
'required' => true,
- 'description' => 'Type of grantee',
'type' => 'string',
'sentAs' => 'xsi:type',
'data' => array(
'xmlAttribute' => true,
'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
),
- 'enum' => array(
- 'CanonicalUser',
- 'AmazonCustomerByEmail',
- 'Group',
- ),
),
'URI' => array(
- 'description' => 'URI of the grantee group.',
'type' => 'string',
),
),
),
),
'TargetPrefix' => array(
- 'description' => 'This element lets you specify a prefix for the keys that the log files will be stored under.',
'type' => 'string',
),
),
),
- 'ContentMD5' => array(
- 'default' => true,
- ),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'logging',
- 'default' => '_guzzle_blank_',
- ),
),
),
'PutBucketNotification' => array(
'httpMethod' => 'PUT',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?notification',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutBucketNotificationOutput',
'responseType' => 'model',
- 'summary' => 'Enables notifications of specified events for a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTnotification.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTnotification.html',
'data' => array(
'xmlRoot' => array(
'name' => 'NotificationConfiguration',
'type' => 'string',
'location' => 'uri',
),
- 'ContentMD5' => array(
- 'default' => true,
- ),
'TopicConfiguration' => array(
'required' => true,
'type' => 'object',
'location' => 'xml',
'properties' => array(
'Event' => array(
- 'description' => 'Bucket event for which to send notifications.',
'type' => 'string',
- 'enum' => array(
- 's3:ReducedRedundancyLostObject',
- ),
),
'Topic' => array(
- 'description' => 'Amazon SNS topic to which Amazon S3 will publish a message to report the specified events for the bucket.',
'type' => 'string',
),
),
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'notification',
- 'default' => '_guzzle_blank_',
- ),
),
),
'PutBucketPolicy' => array(
'httpMethod' => 'PUT',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?policy',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutBucketPolicyOutput',
'responseType' => 'model',
- 'summary' => 'Replaces a policy on a bucket. If the bucket already has a policy, the one in this request completely replaces it.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTpolicy.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTpolicy.html',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'PutBucketPolicyRequest',
+ 'namespaces' => array(
+ 'http://s3.amazonaws.com/doc/2006-03-01/',
+ ),
+ ),
+ ),
'parameters' => array(
'Bucket' => array(
'required' => true,
'type' => 'string',
'location' => 'uri',
),
- 'ContentMD5' => array(
- 'default' => true,
- ),
'Policy' => array(
'required' => true,
- 'description' => 'The bucket policy as a JSON document.',
'type' => array(
'string',
'object',
),
'location' => 'body',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'policy',
- 'default' => '_guzzle_blank_',
- ),
),
),
'PutBucketRequestPayment' => array(
'httpMethod' => 'PUT',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?requestPayment',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutBucketRequestPaymentOutput',
'responseType' => 'model',
- 'summary' => 'Sets the request payment configuration for a bucket. By default, the bucket owner pays for downloads from the bucket. This configuration parameter enables the bucket owner (only) to specify that the person requesting the download will be charged for the download.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTrequestPaymentPUT.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTrequestPaymentPUT.html',
'data' => array(
'xmlRoot' => array(
'name' => 'RequestPaymentConfiguration',
'type' => 'string',
'location' => 'uri',
),
- 'ContentMD5' => array(
- 'default' => true,
- ),
'Payer' => array(
'required' => true,
- 'description' => 'Specifies who pays for the download and request fees.',
'type' => 'string',
'location' => 'xml',
- 'enum' => array(
- 'Requester',
- 'BucketOwner',
- ),
- ),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'requestPayment',
- 'default' => '_guzzle_blank_',
),
),
),
'PutBucketTagging' => array(
'httpMethod' => 'PUT',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?tagging',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutBucketTaggingOutput',
'responseType' => 'model',
- 'summary' => 'Sets the tags for a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTtagging.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTtagging.html',
'data' => array(
'xmlRoot' => array(
'name' => 'Tagging',
'http://s3.amazonaws.com/doc/2006-03-01/',
),
),
+ 'contentMd5' => true,
),
'parameters' => array(
'Bucket' => array(
'type' => 'string',
'location' => 'uri',
),
- 'ContentMD5' => array(
- 'default' => true,
- ),
'TagSet' => array(
'required' => true,
'type' => 'array',
'location' => 'xml',
'items' => array(
'name' => 'Tag',
- 'required' => true,
'type' => 'object',
'properties' => array(
'Key' => array(
'required' => true,
- 'description' => 'Name of the tag.',
'type' => 'string',
),
'Value' => array(
'required' => true,
- 'description' => 'Value of the tag.',
'type' => 'string',
),
),
),
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'tagging',
- 'default' => '_guzzle_blank_',
- ),
),
),
'PutBucketVersioning' => array(
'httpMethod' => 'PUT',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?versioning',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutBucketVersioningOutput',
'responseType' => 'model',
- 'summary' => 'Sets the versioning state of an existing bucket. To set the versioning state, you must be the bucket owner.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTVersioningStatus.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTVersioningStatus.html',
'data' => array(
'xmlRoot' => array(
'name' => 'VersioningConfiguration',
'type' => 'string',
'location' => 'uri',
),
- 'ContentMD5' => array(
- 'default' => true,
- ),
'MFA' => array(
- 'description' => 'The value is the concatenation of the authentication device\'s serial number, a space, and the value displayed on your authentication device.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-mfa',
),
'MFADelete' => array(
- 'description' => 'Specifies whether MFA delete is enabled in the bucket versioning configuration. This element is only returned if the bucket has been configured with MFA delete. If the bucket has never been so configured, this element is not returned.',
'type' => 'string',
'location' => 'xml',
- 'enum' => array(
- 'Enabled',
- 'Disabled',
- ),
+ 'sentAs' => 'MfaDelete',
),
'Status' => array(
- 'description' => 'The versioning state of the bucket.',
'type' => 'string',
'location' => 'xml',
- 'enum' => array(
- 'Enabled',
- 'Disabled',
- ),
- ),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'versioning',
- 'default' => '_guzzle_blank_',
),
),
),
'PutBucketWebsite' => array(
'httpMethod' => 'PUT',
- 'uri' => '/{Bucket}',
+ 'uri' => '/{Bucket}?website',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutBucketWebsiteOutput',
'responseType' => 'model',
- 'summary' => 'Set the website configuration for a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTwebsite.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTwebsite.html',
'data' => array(
'xmlRoot' => array(
'name' => 'WebsiteConfiguration',
'type' => 'string',
'location' => 'uri',
),
- 'ContentMD5' => array(
- 'default' => true,
- ),
'ErrorDocument' => array(
'type' => 'object',
'location' => 'xml',
'properties' => array(
'Key' => array(
'required' => true,
- 'description' => 'The object key name to use when a 4XX class error occurs.',
'type' => 'string',
),
),
'properties' => array(
'Suffix' => array(
'required' => true,
- 'description' => 'A suffix that is appended to a request that is for a directory on the website endpoint (e.g. if the suffix is index.html and you make a request to samplebucket/images/ the data that is returned will be for the object with the key name images/index.html) The suffix must not be empty and must not include a slash character.',
'type' => 'string',
),
),
'properties' => array(
'HostName' => array(
'required' => true,
- 'description' => 'Name of the host where requests will be redirected.',
'type' => 'string',
),
'Protocol' => array(
- 'description' => 'Protocol to use (http, https) when redirecting requests. The default is the protocol that is used in the original request.',
'type' => 'string',
- 'enum' => array(
- 'http',
- 'https',
- ),
),
),
),
'type' => 'object',
'properties' => array(
'Condition' => array(
- 'description' => 'A container for describing a condition that must be met for the specified redirect to apply. For example, 1. If request is for pages in the /docs folder, redirect to the /documents folder. 2. If request results in HTTP error 4xx, redirect request to another host where you might process the error.',
'type' => 'object',
'properties' => array(
'HttpErrorCodeReturnedEquals' => array(
- 'description' => 'The HTTP error code when the redirect is applied. In the event of an error, if the error code equals this value, then the specified redirect is applied. Required when parent element Condition is specified and sibling KeyPrefixEquals is not specified. If both are specified, then both must be true for the redirect to be applied.',
'type' => 'string',
),
'KeyPrefixEquals' => array(
- 'description' => 'The object key name prefix when the redirect is applied. For example, to redirect requests for ExamplePage.html, the key prefix will be ExamplePage.html. To redirect request for all pages with the prefix docs/, the key prefix will be /docs, which identifies all objects in the docs/ folder. Required when the parent element Condition is specified and sibling HttpErrorCodeReturnedEquals is not specified. If both conditions are specified, both must be true for the redirect to be applied.',
'type' => 'string',
),
),
),
'Redirect' => array(
'required' => true,
- 'description' => 'Container for redirect information. You can redirect requests to another host, to another page, or with another protocol. In the event of an error, you can can specify a different error code to return.',
'type' => 'object',
'properties' => array(
'HostName' => array(
- 'required' => true,
- 'description' => 'Name of the host where requests will be redirected.',
'type' => 'string',
),
'HttpRedirectCode' => array(
- 'description' => 'The HTTP redirect code to use on the response. Not required if one of the siblings is present.',
'type' => 'string',
),
'Protocol' => array(
- 'description' => 'Protocol to use (http, https) when redirecting requests. The default is the protocol that is used in the original request.',
'type' => 'string',
- 'enum' => array(
- 'http',
- 'https',
- ),
),
'ReplaceKeyPrefixWith' => array(
- 'description' => 'The object key prefix to use in the redirect request. For example, to redirect requests for all pages with prefix docs/ (objects in the docs/ folder) to documents/, you can set a condition block with KeyPrefixEquals set to docs/ and in the Redirect set ReplaceKeyPrefixWith to /documents. Not required if one of the siblings is present. Can be present only if ReplaceKeyWith is not provided.',
'type' => 'string',
),
'ReplaceKeyWith' => array(
- 'description' => 'The specific object key to use in the redirect request. For example, redirect request to error.html. Not required if one of the sibling is present. Can be present only if ReplaceKeyPrefixWith is not provided.',
'type' => 'string',
),
),
),
),
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'website',
- 'default' => '_guzzle_blank_',
- ),
),
),
'PutObject' => array(
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutObjectOutput',
'responseType' => 'model',
- 'summary' => 'Adds an object to a bucket.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectPUT.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'PutObjectRequest',
+ 'namespaces' => array(
+ 'http://s3.amazonaws.com/doc/2006-03-01/',
+ ),
+ ),
+ ),
'parameters' => array(
'ACL' => array(
- 'description' => 'The canned ACL to apply to the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-acl',
- 'enum' => array(
- 'private',
- 'public-read',
- 'public-read-write',
- 'authenticated-read',
- 'bucket-owner-read',
- 'bucket-owner-full-control',
- ),
),
'Body' => array(
- 'description' => 'Pass a string containing the body, a handle returned by fopen, or a Guzzle\\Http\\EntityBodyInterface object',
'type' => array(
'string',
'object',
'location' => 'uri',
),
'CacheControl' => array(
- 'description' => 'Specifies caching behavior along the request/reply chain.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Cache-Control',
),
'ContentDisposition' => array(
- 'description' => 'Specifies presentational information for the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Disposition',
),
'ContentEncoding' => array(
- 'description' => 'Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Encoding',
),
'ContentLanguage' => array(
- 'description' => 'The language the content is in.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Language',
),
'ContentLength' => array(
- 'description' => 'Size of the body in bytes. This parameter is useful when the size of the body cannot be determined automatically.',
'type' => 'numeric',
'location' => 'header',
'sentAs' => 'Content-Length',
),
'ContentMD5' => array(
- 'description' => 'Content-MD5 checksum of the body. Set to false to disable',
- 'default' => true,
+ 'type' => array(
+ 'string',
+ 'boolean',
+ ),
+ 'location' => 'header',
+ 'sentAs' => 'Content-MD5',
),
'ContentType' => array(
- 'description' => 'A standard MIME type describing the format of the object data.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Type',
),
'Expires' => array(
- 'description' => 'The date and time at which the object is no longer cacheable.',
'type' => array(
'object',
'string',
'location' => 'header',
),
'GrantFullControl' => array(
- 'description' => 'Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-full-control',
),
'GrantRead' => array(
- 'description' => 'Allows grantee to read the object data and its metadata.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read',
),
'GrantReadACP' => array(
- 'description' => 'Allows grantee to read the object ACL.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read-acp',
),
'GrantWriteACP' => array(
- 'description' => 'Allows grantee to write the ACL for the applicable object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-write-acp',
),
),
'Metadata' => array(
- 'description' => 'A map of metadata to store with the object in S3.',
'type' => 'object',
'location' => 'header',
'sentAs' => 'x-amz-meta-',
'additionalProperties' => array(
- 'description' => 'The metadata key. This will be prefixed with x-amz-meta- before sending to S3 as a header. The x-amz-meta- header will be stripped from the key when retrieving headers.',
'type' => 'string',
),
),
'ServerSideEncryption' => array(
- 'description' => 'The Server-side encryption algorithm used when storing this object in S3.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption',
- 'enum' => array(
- 'AES256',
- ),
),
'StorageClass' => array(
- 'description' => 'The type of storage to use for the object. Defaults to \'STANDARD\'.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-storage-class',
- 'enum' => array(
- 'STANDARD',
- 'REDUCED_REDUNDANCY',
- ),
),
'WebsiteRedirectLocation' => array(
- 'description' => 'If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-website-redirect-location',
),
- 'ValidateMD5' => array(
- 'description' => 'Whether or not the Content-MD5 header of the response is validated. Default is true.',
- 'default' => true,
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
'ACP' => array(
- 'description' => 'Pass an Aws\\S3\\Model\\Acp object as an alternative way to add access control policy headers to the operation',
'type' => 'object',
'additionalProperties' => true,
),
),
'PutObjectAcl' => array(
'httpMethod' => 'PUT',
- 'uri' => '/{Bucket}{/Key*}',
+ 'uri' => '/{Bucket}{/Key*}?acl',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'PutObjectAclOutput',
'responseType' => 'model',
- 'summary' => 'uses the acl subresource to set the access control list (ACL) permissions for an object that already exists in a bucket',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectPUTacl.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUTacl.html',
'data' => array(
'xmlRoot' => array(
'name' => 'AccessControlPolicy',
),
'parameters' => array(
'ACL' => array(
- 'description' => 'The canned ACL to apply to the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-acl',
- 'enum' => array(
- 'private',
- 'public-read',
- 'public-read-write',
- 'authenticated-read',
- 'bucket-owner-read',
- 'bucket-owner-full-control',
- ),
),
'Grants' => array(
- 'description' => 'A list of grants.',
'type' => 'array',
'location' => 'xml',
'sentAs' => 'AccessControlList',
'type' => 'object',
'properties' => array(
'DisplayName' => array(
- 'description' => 'Screen name of the grantee.',
'type' => 'string',
),
'EmailAddress' => array(
- 'description' => 'Email address of the grantee.',
'type' => 'string',
),
'ID' => array(
- 'description' => 'The canonical user ID of the grantee.',
'type' => 'string',
),
'Type' => array(
'required' => true,
- 'description' => 'Type of grantee',
'type' => 'string',
'sentAs' => 'xsi:type',
'data' => array(
'xmlAttribute' => true,
'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
),
- 'enum' => array(
- 'CanonicalUser',
- 'AmazonCustomerByEmail',
- 'Group',
- ),
),
'URI' => array(
- 'description' => 'URI of the grantee group.',
'type' => 'string',
),
),
),
'Permission' => array(
- 'description' => 'Specifies the permission given to the grantee.',
'type' => 'string',
- 'enum' => array(
- 'FULL_CONTROL',
- 'WRITE',
- 'WRITE_ACP',
- 'READ',
- 'READ_ACP',
- ),
),
),
),
'type' => 'string',
'location' => 'uri',
),
- 'ContentMD5' => array(
- 'default' => true,
- ),
'GrantFullControl' => array(
- 'description' => 'Allows grantee the read, write, read ACP, and write ACP permissions on the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-full-control',
),
'GrantRead' => array(
- 'description' => 'Allows grantee to list the objects in the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read',
),
'GrantReadACP' => array(
- 'description' => 'Allows grantee to read the bucket ACL.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-read-acp',
),
'GrantWrite' => array(
- 'description' => 'Allows grantee to create, overwrite, and delete any object in the bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-write',
),
'GrantWriteACP' => array(
- 'description' => 'Allows grantee to write the ACL for the applicable bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-grant-write-acp',
'Aws\\S3\\S3Client::explodeKey',
),
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'acl',
- 'default' => '_guzzle_blank_',
- ),
'ACP' => array(
- 'description' => 'Pass an Aws\\S3\\Model\\Acp object as an alternative way to add an access control policy to the operation',
'type' => 'object',
'additionalProperties' => true,
),
),
'RestoreObject' => array(
'httpMethod' => 'POST',
- 'uri' => '/{Bucket}{/Key*}',
+ 'uri' => '/{Bucket}{/Key*}?restore',
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'RestoreObjectOutput',
'responseType' => 'model',
- 'summary' => 'Restores an archived copy of an object back into Amazon S3',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectRestore.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectRestore.html',
'data' => array(
'xmlRoot' => array(
'name' => 'RestoreRequest',
),
'Days' => array(
'required' => true,
- 'description' => 'Lifetime of the active copy in days',
'type' => 'numeric',
'location' => 'xml',
),
- 'SubResource' => array(
- 'required' => true,
- 'static' => true,
- 'location' => 'query',
- 'sentAs' => 'restore',
- 'default' => '_guzzle_blank_',
- ),
),
'errorResponses' => array(
array(
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'UploadPartOutput',
'responseType' => 'model',
- 'summary' => 'Uploads a part in a multipart upload.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadUploadPart.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadUploadPart.html',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'UploadPartRequest',
+ 'namespaces' => array(
+ 'http://s3.amazonaws.com/doc/2006-03-01/',
+ ),
+ ),
+ ),
'parameters' => array(
'Body' => array(
- 'description' => 'Pass a string containing the body, a handle returned by fopen, or a Guzzle\\Http\\EntityBodyInterface object',
'type' => array(
'string',
'object',
'location' => 'uri',
),
'ContentLength' => array(
- 'description' => 'Size of the body in bytes. This parameter is useful when the size of the body cannot be determined automatically.',
'type' => 'numeric',
'location' => 'header',
'sentAs' => 'Content-Length',
),
+ 'ContentMD5' => array(
+ 'type' => array(
+ 'string',
+ 'boolean',
+ ),
+ 'location' => 'header',
+ 'sentAs' => 'Content-MD5',
+ ),
'Key' => array(
'required' => true,
'type' => 'string',
),
'PartNumber' => array(
'required' => true,
- 'description' => 'Part number of part being uploaded.',
- 'type' => 'string',
+ 'type' => 'numeric',
'location' => 'query',
'sentAs' => 'partNumber',
),
'UploadId' => array(
'required' => true,
- 'description' => 'Upload ID identifying the multipart upload whose part is being uploaded.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'uploadId',
),
- 'ContentMD5' => array(
- 'description' => 'Content-MD5 checksum of the body. Set to false to disable',
- 'default' => true,
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key',
),
- 'ValidateMD5' => array(
- 'description' => 'Whether or not the Content-MD5 header of the response is validated. Default is true.',
- 'default' => true,
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
),
),
'class' => 'Aws\\S3\\Command\\S3Command',
'responseClass' => 'UploadPartCopyOutput',
'responseType' => 'model',
- 'summary' => 'Uploads a part by copying data from an existing object as data source.',
- 'documentationUrl' => 'http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadUploadPartCopy.html',
+ 'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadUploadPartCopy.html',
+ 'data' => array(
+ 'xmlRoot' => array(
+ 'name' => 'UploadPartCopyRequest',
+ 'namespaces' => array(
+ 'http://s3.amazonaws.com/doc/2006-03-01/',
+ ),
+ ),
+ ),
'parameters' => array(
'Bucket' => array(
'required' => true,
),
'CopySource' => array(
'required' => true,
- 'description' => 'The name of the source bucket and key name of the source object, separated by a slash (/). Must be URL-encoded.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-copy-source',
),
'CopySourceIfMatch' => array(
- 'description' => 'Copies the object if its entity tag (ETag) matches the specified tag.',
- 'type' => array(
- 'object',
- 'string',
- 'integer',
- ),
- 'format' => 'date-time-http',
+ 'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-copy-source-if-match',
),
'CopySourceIfModifiedSince' => array(
- 'description' => 'Copies the object if it has been modified since the specified time.',
'type' => array(
'object',
'string',
'sentAs' => 'x-amz-copy-source-if-modified-since',
),
'CopySourceIfNoneMatch' => array(
- 'description' => 'Copies the object if its entity tag (ETag) is different than the specified ETag.',
- 'type' => array(
- 'object',
- 'string',
- 'integer',
- ),
- 'format' => 'date-time-http',
+ 'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-copy-source-if-none-match',
),
'CopySourceIfUnmodifiedSince' => array(
- 'description' => 'Copies the object if it hasn\'t been modified since the specified time.',
'type' => array(
'object',
'string',
'sentAs' => 'x-amz-copy-source-if-unmodified-since',
),
'CopySourceRange' => array(
- 'description' => 'The range of bytes to copy from the source object. The range value must use the form bytes=first-last, where the first and last are the zero-based byte offsets to copy. For example, bytes=0-9 indicates that you want to copy the first ten bytes of the source. You can copy a range only if the source object is greater than 5 GB.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-copy-source-range',
),
'PartNumber' => array(
'required' => true,
- 'description' => 'Part number of part being copied.',
- 'type' => 'string',
+ 'type' => 'numeric',
'location' => 'query',
'sentAs' => 'partNumber',
),
'UploadId' => array(
'required' => true,
- 'description' => 'Upload ID identifying the multipart upload whose part is being copied.',
'type' => 'string',
'location' => 'query',
'sentAs' => 'uploadId',
),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
+ ),
+ 'CopySourceSSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-algorithm',
+ ),
+ 'CopySourceSSECustomerKey' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-key',
+ ),
+ 'CopySourceSSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-key-MD5',
+ ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'location' => 'xml',
),
'Expiration' => array(
- 'description' => 'If the object expiration is configured, this will contain the expiration date (expiry-date) and rule ID (rule-id). The value of rule-id is URL encoded.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-expiration',
),
'ETag' => array(
- 'description' => 'Entity tag of the object.',
'type' => 'string',
'location' => 'xml',
),
'ServerSideEncryption' => array(
- 'description' => 'The Server-side encryption algorithm used when storing this object in S3.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption',
),
'VersionId' => array(
- 'description' => 'Version of the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-version-id',
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'location' => 'xml',
),
'Expiration' => array(
- 'description' => 'If the object expiration is configured, the response includes this header.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-expiration',
'sentAs' => 'x-amz-copy-source-version-id',
),
'ServerSideEncryption' => array(
- 'description' => 'The Server-side encryption algorithm used when storing this object in S3.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption',
),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
+ ),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'location' => 'header',
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'Bucket' => array(
- 'description' => 'Name of the bucket to which the multipart upload was initiated.',
'type' => 'string',
'location' => 'xml',
'sentAs' => 'Bucket',
),
'Key' => array(
- 'description' => 'Object key for which the multipart upload was initiated.',
'type' => 'string',
'location' => 'xml',
),
'UploadId' => array(
- 'description' => 'ID for the initiated multipart upload.',
'type' => 'string',
'location' => 'xml',
),
'ServerSideEncryption' => array(
- 'description' => 'The Server-side encryption algorithm used when storing this object in S3.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption',
),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
+ ),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'DeleteMarker' => array(
- 'description' => 'Specifies whether the versioned object that was permanently deleted was (true) or was not (false) a delete marker.',
- 'type' => 'string',
+ 'type' => 'boolean',
'location' => 'header',
'sentAs' => 'x-amz-delete-marker',
),
'VersionId' => array(
- 'description' => 'Returns the version ID of the delete marker created as a result of the DELETE operation.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-version-id',
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'DeletedObject',
'type' => 'object',
'properties' => array(
'Key' => array(
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'Error',
'type' => 'object',
'sentAs' => 'Error',
'properties' => array(
),
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'type' => 'object',
'location' => 'xml',
'properties' => array(
- 'ID' => array(
+ 'DisplayName' => array(
'type' => 'string',
),
- 'DisplayName' => array(
+ 'ID' => array(
'type' => 'string',
),
),
),
'Grants' => array(
- 'description' => 'A list of grants.',
'type' => 'array',
'location' => 'xml',
'sentAs' => 'AccessControlList',
'Grantee' => array(
'type' => 'object',
'properties' => array(
- 'Type' => array(
- 'description' => 'Type of grantee',
+ 'DisplayName' => array(
'type' => 'string',
- 'sentAs' => 'xsi:type',
- 'data' => array(
- 'xmlAttribute' => true,
- 'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
- ),
),
- 'ID' => array(
- 'description' => 'The canonical user ID of the grantee.',
+ 'EmailAddress' => array(
'type' => 'string',
),
- 'DisplayName' => array(
- 'description' => 'Screen name of the grantee.',
+ 'ID' => array(
'type' => 'string',
),
- 'EmailAddress' => array(
- 'description' => 'Email address of the grantee.',
+ 'Type' => array(
'type' => 'string',
+ 'sentAs' => 'xsi:type',
+ 'data' => array(
+ 'xmlAttribute' => true,
+ 'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
+ ),
),
'URI' => array(
- 'description' => 'URI of the grantee group.',
'type' => 'string',
),
),
),
'Permission' => array(
- 'description' => 'Specifies the permission given to the grantee.',
'type' => 'string',
),
),
),
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'CORSRule',
'type' => 'object',
'sentAs' => 'CORSRule',
'properties' => array(
'AllowedHeaders' => array(
- 'description' => 'Specifies which headers are allowed in a pre-flight OPTIONS request.',
'type' => 'array',
'sentAs' => 'AllowedHeader',
'data' => array(
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'AllowedHeader',
'type' => 'string',
'sentAs' => 'AllowedHeader',
),
),
- 'AllowedOrigins' => array(
- 'description' => 'One or more origins you want customers to be able to access the bucket from.',
+ 'AllowedMethods' => array(
'type' => 'array',
- 'sentAs' => 'AllowedOrigin',
+ 'sentAs' => 'AllowedMethod',
'data' => array(
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'AllowedMethod',
'type' => 'string',
- 'sentAs' => 'AllowedOrigin',
+ 'sentAs' => 'AllowedMethod',
),
),
- 'AllowedMethods' => array(
- 'description' => 'Identifies HTTP methods that the domain/origin specified in the rule is allowed to execute.',
+ 'AllowedOrigins' => array(
'type' => 'array',
- 'sentAs' => 'AllowedMethod',
+ 'sentAs' => 'AllowedOrigin',
'data' => array(
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'AllowedOrigin',
'type' => 'string',
- 'sentAs' => 'AllowedMethod',
+ 'sentAs' => 'AllowedOrigin',
),
),
- 'MaxAgeSeconds' => array(
- 'description' => 'The time in seconds that your browser is to cache the preflight response for the specified resource.',
- 'type' => 'numeric',
- ),
'ExposeHeaders' => array(
- 'description' => 'One or more headers in the response that you want customers to be able to access from their applications (for example, from a JavaScript XMLHttpRequest object).',
'type' => 'array',
'sentAs' => 'ExposeHeader',
'data' => array(
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'ExposeHeader',
'type' => 'string',
'sentAs' => 'ExposeHeader',
),
),
+ 'MaxAgeSeconds' => array(
+ 'type' => 'numeric',
+ ),
),
),
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'Rule',
'type' => 'object',
'sentAs' => 'Rule',
'properties' => array(
+ 'Expiration' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'Date' => array(
+ 'type' => 'string',
+ ),
+ 'Days' => array(
+ 'type' => 'numeric',
+ ),
+ ),
+ ),
'ID' => array(
- 'description' => 'Unique identifier for the rule. The value cannot be longer than 255 characters.',
'type' => 'string',
),
'Prefix' => array(
- 'description' => 'Prefix identifying one or more objects to which the rule applies.',
'type' => 'string',
),
'Status' => array(
- 'description' => 'If \'Enabled\', the rule is currently being applied. If \'Disabled\', the rule is not currently being applied.',
'type' => 'string',
),
'Transition' => array(
'type' => 'object',
'properties' => array(
- 'Days' => array(
- 'description' => 'Indicates the lifetime, in days, of the objects that are subject to the rule. The value must be a non-zero positive integer.',
- 'type' => 'numeric',
- ),
'Date' => array(
- 'description' => 'Indicates at what date the object is to be moved or deleted. Should be in GMT ISO 8601 Format.',
'type' => 'string',
),
+ 'Days' => array(
+ 'type' => 'numeric',
+ ),
'StorageClass' => array(
- 'description' => 'The class of storage used to store the object.',
'type' => 'string',
),
),
),
- 'Expiration' => array(
+ 'NoncurrentVersionTransition' => array(
'type' => 'object',
'properties' => array(
- 'Days' => array(
- 'description' => 'Indicates the lifetime, in days, of the objects that are subject to the rule. The value must be a non-zero positive integer.',
+ 'NoncurrentDays' => array(
'type' => 'numeric',
),
- 'Date' => array(
- 'description' => 'Indicates at what date the object is to be moved or deleted. Should be in GMT ISO 8601 Format.',
+ 'StorageClass' => array(
'type' => 'string',
),
),
),
+ 'NoncurrentVersionExpiration' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'NoncurrentDays' => array(
+ 'type' => 'numeric',
+ ),
+ ),
+ ),
),
),
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'location' => 'xml',
'properties' => array(
'TargetBucket' => array(
- 'description' => 'Specifies the bucket where you want Amazon S3 to store server access logs. You can have your logs delivered to any bucket that you own, including the same bucket that is being logged. You can also configure multiple buckets to deliver their logs to the same target bucket. In this case you should choose a different TargetPrefix for each source bucket so that the delivered log files can be distinguished by key.',
- 'type' => 'string',
- ),
- 'TargetPrefix' => array(
- 'description' => 'This element lets you specify a prefix for the keys that the log files will be stored under.',
'type' => 'string',
),
'TargetGrants' => array(
'Grantee' => array(
'type' => 'object',
'properties' => array(
- 'Type' => array(
- 'description' => 'Type of grantee',
+ 'DisplayName' => array(
'type' => 'string',
- 'sentAs' => 'xsi:type',
- 'data' => array(
- 'xmlAttribute' => true,
- 'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
- ),
),
- 'ID' => array(
- 'description' => 'The canonical user ID of the grantee.',
+ 'EmailAddress' => array(
'type' => 'string',
),
- 'DisplayName' => array(
- 'description' => 'Screen name of the grantee.',
+ 'ID' => array(
'type' => 'string',
),
- 'EmailAddress' => array(
- 'description' => 'Email address of the grantee.',
+ 'Type' => array(
'type' => 'string',
+ 'sentAs' => 'xsi:type',
+ 'data' => array(
+ 'xmlAttribute' => true,
+ 'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
+ ),
),
'URI' => array(
- 'description' => 'URI of the grantee group.',
'type' => 'string',
),
),
),
),
),
+ 'TargetPrefix' => array(
+ 'type' => 'string',
+ ),
),
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'type' => 'object',
'location' => 'xml',
'properties' => array(
- 'Topic' => array(
- 'description' => 'Amazon SNS topic to which Amazon S3 will publish a message to report the specified events for the bucket.',
+ 'Event' => array(
'type' => 'string',
),
- 'Event' => array(
- 'description' => 'Bucket event for which to send notifications.',
+ 'Topic' => array(
'type' => 'string',
),
),
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'Policy' => array(
- 'description' => 'The bucket policy as a JSON document.',
'type' => 'string',
'instanceOf' => 'Guzzle\\Http\\EntityBody',
'location' => 'body',
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'Payer' => array(
- 'description' => 'Specifies who pays for the download and request fees.',
'type' => 'string',
'location' => 'xml',
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'sentAs' => 'Tag',
'properties' => array(
'Key' => array(
- 'description' => 'Name of the tag.',
'type' => 'string',
),
'Value' => array(
- 'description' => 'Value of the tag.',
'type' => 'string',
),
),
),
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'Status' => array(
- 'description' => 'The versioning state of the bucket.',
'type' => 'string',
'location' => 'xml',
),
'MFADelete' => array(
- 'description' => 'Specifies whether MFA delete is enabled in the bucket versioning configuration. This element is only returned if the bucket has been configured with MFA delete. If the bucket has never been so configured, this element is not returned.',
'type' => 'string',
'location' => 'xml',
+ 'sentAs' => 'MfaDelete',
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'location' => 'xml',
'properties' => array(
'HostName' => array(
- 'description' => 'Name of the host where requests will be redirected.',
'type' => 'string',
),
'Protocol' => array(
- 'description' => 'Protocol to use (http, https) when redirecting requests. The default is the protocol that is used in the original request.',
'type' => 'string',
),
),
'location' => 'xml',
'properties' => array(
'Suffix' => array(
- 'description' => 'A suffix that is appended to a request that is for a directory on the website endpoint (e.g. if the suffix is index.html and you make a request to samplebucket/images/ the data that is returned will be for the object with the key name images/index.html) The suffix must not be empty and must not include a slash character.',
'type' => 'string',
),
),
'location' => 'xml',
'properties' => array(
'Key' => array(
- 'description' => 'The object key name to use when a 4XX class error occurs.',
'type' => 'string',
),
),
'sentAs' => 'RoutingRule',
'properties' => array(
'Condition' => array(
- 'description' => 'A container for describing a condition that must be met for the specified redirect to apply. For example, 1. If request is for pages in the /docs folder, redirect to the /documents folder. 2. If request results in HTTP error 4xx, redirect request to another host where you might process the error.',
'type' => 'object',
'properties' => array(
- 'KeyPrefixEquals' => array(
- 'description' => 'The object key name prefix when the redirect is applied. For example, to redirect requests for ExamplePage.html, the key prefix will be ExamplePage.html. To redirect request for all pages with the prefix docs/, the key prefix will be /docs, which identifies all objects in the docs/ folder. Required when the parent element Condition is specified and sibling HttpErrorCodeReturnedEquals is not specified. If both conditions are specified, both must be true for the redirect to be applied.',
+ 'HttpErrorCodeReturnedEquals' => array(
'type' => 'string',
),
- 'HttpErrorCodeReturnedEquals' => array(
- 'description' => 'The HTTP error code when the redirect is applied. In the event of an error, if the error code equals this value, then the specified redirect is applied. Required when parent element Condition is specified and sibling KeyPrefixEquals is not specified. If both are specified, then both must be true for the redirect to be applied.',
+ 'KeyPrefixEquals' => array(
'type' => 'string',
),
),
),
'Redirect' => array(
- 'description' => 'Container for redirect information. You can redirect requests to another host, to another page, or with another protocol. In the event of an error, you can can specify a different error code to return.',
'type' => 'object',
'properties' => array(
- 'ReplaceKeyPrefixWith' => array(
- 'description' => 'The object key prefix to use in the redirect request. For example, to redirect requests for all pages with prefix docs/ (objects in the docs/ folder) to documents/, you can set a condition block with KeyPrefixEquals set to docs/ and in the Redirect set ReplaceKeyPrefixWith to /documents. Not required if one of the siblings is present. Can be present only if ReplaceKeyWith is not provided.',
+ 'HostName' => array(
'type' => 'string',
),
- 'ReplaceKeyWith' => array(
- 'description' => 'The specific object key to use in the redirect request. For example, redirect request to error.html. Not required if one of the sibling is present. Can be present only if ReplaceKeyPrefixWith is not provided.',
+ 'HttpRedirectCode' => array(
'type' => 'string',
),
- 'HttpRedirectCode' => array(
- 'description' => 'The HTTP redirect code to use on the response. Not required if one of the siblings is present.',
+ 'Protocol' => array(
'type' => 'string',
),
- 'HostName' => array(
- 'description' => 'Name of the host where requests will be redirected.',
+ 'ReplaceKeyPrefixWith' => array(
'type' => 'string',
),
- 'Protocol' => array(
- 'description' => 'Protocol to use (http, https) when redirecting requests. The default is the protocol that is used in the original request.',
+ 'ReplaceKeyWith' => array(
'type' => 'string',
),
),
),
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'Body' => array(
- 'description' => 'Object data.',
'type' => 'string',
'instanceOf' => 'Guzzle\\Http\\EntityBody',
'location' => 'body',
),
'DeleteMarker' => array(
- 'description' => 'Specifies whether the object retrieved was (true) or was not (false) a Delete Marker. If false, this response header does not appear in the response.',
- 'type' => 'string',
+ 'type' => 'boolean',
'location' => 'header',
'sentAs' => 'x-amz-delete-marker',
),
'sentAs' => 'accept-ranges',
),
'Expiration' => array(
- 'description' => 'If the object expiration is configured (see PUT Bucket lifecycle), the response includes this header. It includes the expiry-date and rule-id key value pairs providing object expiration information. The value of the rule-id is URL encoded.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-expiration',
),
'Restore' => array(
- 'description' => 'Provides information about object restoration operation and expiration time of the restored object copy.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-restore',
),
'LastModified' => array(
- 'description' => 'Last modified date of the object',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Last-Modified',
),
'ContentLength' => array(
- 'description' => 'Size of the body in bytes.',
'type' => 'numeric',
'location' => 'header',
'sentAs' => 'Content-Length',
),
'ETag' => array(
- 'description' => 'An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL',
'type' => 'string',
'location' => 'header',
),
'MissingMeta' => array(
- 'description' => 'This is set to the number of metadata entries not returned in x-amz-meta headers. This can happen if you create metadata using an API like SOAP that supports more flexible metadata than the REST API. For example, using SOAP, you can create metadata whose values are not legal HTTP headers.',
'type' => 'numeric',
'location' => 'header',
'sentAs' => 'x-amz-missing-meta',
),
'VersionId' => array(
- 'description' => 'Version of the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-version-id',
),
'CacheControl' => array(
- 'description' => 'Specifies caching behavior along the request/reply chain.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Cache-Control',
),
'ContentDisposition' => array(
- 'description' => 'Specifies presentational information for the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Disposition',
),
'ContentEncoding' => array(
- 'description' => 'Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Encoding',
),
'ContentLanguage' => array(
- 'description' => 'The language the content is in.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Language',
),
'ContentType' => array(
- 'description' => 'A standard MIME type describing the format of the object data.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Type',
),
'Expires' => array(
- 'description' => 'The date and time at which the object is no longer cacheable.',
'type' => 'string',
'location' => 'header',
),
'WebsiteRedirectLocation' => array(
- 'description' => 'If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-website-redirect-location',
),
'ServerSideEncryption' => array(
- 'description' => 'The Server-side encryption algorithm used when storing this object in S3.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption',
),
'Metadata' => array(
- 'description' => 'A map of metadata to store with the object in S3.',
'type' => 'object',
'location' => 'header',
'sentAs' => 'x-amz-meta-',
'additionalProperties' => array(
- 'description' => 'The metadata value.',
'type' => 'string',
),
),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
+ ),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'type' => 'object',
'location' => 'xml',
'properties' => array(
- 'ID' => array(
+ 'DisplayName' => array(
'type' => 'string',
),
- 'DisplayName' => array(
+ 'ID' => array(
'type' => 'string',
),
),
),
'Grants' => array(
- 'description' => 'A list of grants.',
'type' => 'array',
'location' => 'xml',
'sentAs' => 'AccessControlList',
'Grantee' => array(
'type' => 'object',
'properties' => array(
- 'Type' => array(
- 'description' => 'Type of grantee',
+ 'DisplayName' => array(
'type' => 'string',
- 'sentAs' => 'xsi:type',
- 'data' => array(
- 'xmlAttribute' => true,
- 'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
- ),
),
- 'ID' => array(
- 'description' => 'The canonical user ID of the grantee.',
+ 'EmailAddress' => array(
'type' => 'string',
),
- 'DisplayName' => array(
- 'description' => 'Screen name of the grantee.',
+ 'ID' => array(
'type' => 'string',
),
- 'EmailAddress' => array(
- 'description' => 'Email address of the grantee.',
+ 'Type' => array(
'type' => 'string',
+ 'sentAs' => 'xsi:type',
+ 'data' => array(
+ 'xmlAttribute' => true,
+ 'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
+ ),
),
'URI' => array(
- 'description' => 'URI of the grantee group.',
'type' => 'string',
),
),
),
'Permission' => array(
- 'description' => 'Specifies the permission given to the grantee.',
'type' => 'string',
),
),
),
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'location' => 'body',
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'DeleteMarker' => array(
- 'description' => 'Specifies whether the object retrieved was (true) or was not (false) a Delete Marker. If false, this response header does not appear in the response.',
- 'type' => 'string',
+ 'type' => 'boolean',
'location' => 'header',
'sentAs' => 'x-amz-delete-marker',
),
'sentAs' => 'accept-ranges',
),
'Expiration' => array(
- 'description' => 'If the object expiration is configured (see PUT Bucket lifecycle), the response includes this header. It includes the expiry-date and rule-id key value pairs providing object expiration information. The value of the rule-id is URL encoded.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-expiration',
),
'Restore' => array(
- 'description' => 'Provides information about object restoration operation and expiration time of the restored object copy.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-restore',
),
'LastModified' => array(
- 'description' => 'Last modified date of the object',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Last-Modified',
),
'ContentLength' => array(
- 'description' => 'Size of the body in bytes.',
'type' => 'numeric',
'location' => 'header',
'sentAs' => 'Content-Length',
),
'ETag' => array(
- 'description' => 'An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL',
'type' => 'string',
'location' => 'header',
),
'MissingMeta' => array(
- 'description' => 'This is set to the number of metadata entries not returned in x-amz-meta headers. This can happen if you create metadata using an API like SOAP that supports more flexible metadata than the REST API. For example, using SOAP, you can create metadata whose values are not legal HTTP headers.',
'type' => 'numeric',
'location' => 'header',
'sentAs' => 'x-amz-missing-meta',
),
'VersionId' => array(
- 'description' => 'Version of the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-version-id',
),
'CacheControl' => array(
- 'description' => 'Specifies caching behavior along the request/reply chain.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Cache-Control',
),
'ContentDisposition' => array(
- 'description' => 'Specifies presentational information for the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Disposition',
),
'ContentEncoding' => array(
- 'description' => 'Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Encoding',
),
'ContentLanguage' => array(
- 'description' => 'The language the content is in.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Language',
),
'ContentType' => array(
- 'description' => 'A standard MIME type describing the format of the object data.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'Content-Type',
),
'Expires' => array(
- 'description' => 'The date and time at which the object is no longer cacheable.',
'type' => 'string',
'location' => 'header',
),
'WebsiteRedirectLocation' => array(
- 'description' => 'If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-website-redirect-location',
),
'ServerSideEncryption' => array(
- 'description' => 'The Server-side encryption algorithm used when storing this object in S3.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption',
),
'Metadata' => array(
- 'description' => 'A map of metadata to store with the object in S3.',
'type' => 'object',
'location' => 'header',
'sentAs' => 'x-amz-meta-',
'additionalProperties' => array(
- 'description' => 'The metadata value.',
'type' => 'string',
),
),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
+ ),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'sentAs' => 'Bucket',
'properties' => array(
'Name' => array(
- 'description' => 'The name of the bucket.',
'type' => 'string',
),
'CreationDate' => array(
- 'description' => 'Date the bucket was created.',
'type' => 'string',
),
),
'type' => 'object',
'location' => 'xml',
'properties' => array(
- 'ID' => array(
+ 'DisplayName' => array(
'type' => 'string',
),
- 'DisplayName' => array(
+ 'ID' => array(
'type' => 'string',
),
),
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'Bucket' => array(
- 'description' => 'Name of the bucket to which the multipart upload was initiated.',
'type' => 'string',
'location' => 'xml',
),
'KeyMarker' => array(
- 'description' => 'The key at or after which the listing began.',
'type' => 'string',
'location' => 'xml',
),
'UploadIdMarker' => array(
- 'description' => 'Upload ID after which listing began.',
'type' => 'string',
'location' => 'xml',
),
'NextKeyMarker' => array(
- 'description' => 'When a list is truncated, this element specifies the value that should be used for the key-marker request parameter in a subsequent request.',
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
+ 'Prefix' => array(
'type' => 'string',
'location' => 'xml',
),
'NextUploadIdMarker' => array(
- 'description' => 'When a list is truncated, this element specifies the value that should be used for the upload-id-marker request parameter in a subsequent request.',
'type' => 'string',
'location' => 'xml',
),
'MaxUploads' => array(
- 'description' => 'Maximum number of multipart uploads that could have been included in the response.',
'type' => 'numeric',
'location' => 'xml',
),
'IsTruncated' => array(
- 'description' => 'Indicates whether the returned list of multipart uploads is truncated. A value of true indicates that the list was truncated. The list can be truncated if the number of multipart uploads exceeds the limit allowed or specified by max uploads.',
'type' => 'boolean',
'location' => 'xml',
),
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'MultipartUpload',
'type' => 'object',
'sentAs' => 'Upload',
'properties' => array(
'UploadId' => array(
- 'description' => 'Upload ID that identifies the multipart upload.',
'type' => 'string',
),
'Key' => array(
- 'description' => 'Key of the object for which the multipart upload was initiated.',
'type' => 'string',
),
'Initiated' => array(
- 'description' => 'Date and time at which the multipart upload was initiated.',
'type' => 'string',
),
'StorageClass' => array(
- 'description' => 'The class of storage used to store the object.',
'type' => 'string',
),
'Owner' => array(
'type' => 'object',
'properties' => array(
- 'ID' => array(
+ 'DisplayName' => array(
'type' => 'string',
),
- 'DisplayName' => array(
+ 'ID' => array(
'type' => 'string',
),
),
),
'Initiator' => array(
- 'description' => 'Identifies who initiated the multipart upload.',
'type' => 'object',
'properties' => array(
'ID' => array(
- 'description' => 'If the principal is an AWS account, it provides the Canonical User ID. If the principal is an IAM User, it provides a user ARN value.',
'type' => 'string',
),
'DisplayName' => array(
- 'description' => 'Name of the Principal.',
'type' => 'string',
),
),
),
),
),
+ 'CommonPrefixes' => array(
+ 'type' => 'array',
+ 'location' => 'xml',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'CommonPrefix',
+ 'type' => 'object',
+ 'properties' => array(
+ 'Prefix' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'IsTruncated' => array(
- 'description' => 'A flag that indicates whether or not Amazon S3 returned all of the results that satisfied the search criteria. If your results were truncated, you can make a follow-up paginated request using the NextKeyMarker and NextVersionIdMarker response parameters as a starting place in another request to return the rest of the results.',
'type' => 'boolean',
'location' => 'xml',
),
'KeyMarker' => array(
- 'description' => 'Marks the last Key returned in a truncated response.',
'type' => 'string',
'location' => 'xml',
),
'location' => 'xml',
),
'NextKeyMarker' => array(
- 'description' => 'Use this value for the key marker request parameter in a subsequent request.',
'type' => 'string',
'location' => 'xml',
),
'NextVersionIdMarker' => array(
- 'description' => 'Use this value for the next version id marker parameter in a subsequent request.',
'type' => 'string',
'location' => 'xml',
),
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'ObjectVersion',
'type' => 'object',
'sentAs' => 'Version',
'properties' => array(
'type' => 'string',
),
'Size' => array(
- 'description' => 'Size in bytes of the object.',
- 'type' => 'string',
+ 'type' => 'numeric',
),
'StorageClass' => array(
- 'description' => 'The class of storage used to store the object.',
'type' => 'string',
),
'Key' => array(
- 'description' => 'The object key.',
'type' => 'string',
),
'VersionId' => array(
- 'description' => 'Version ID of an object.',
'type' => 'string',
),
'IsLatest' => array(
- 'description' => 'Specifies whether the object is (true) or is not (false) the latest version of an object.',
'type' => 'boolean',
),
'LastModified' => array(
- 'description' => 'Date and time the object was last modified.',
'type' => 'string',
),
'Owner' => array(
'type' => 'object',
'properties' => array(
- 'ID' => array(
+ 'DisplayName' => array(
'type' => 'string',
),
- 'DisplayName' => array(
+ 'ID' => array(
'type' => 'string',
),
),
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'DeleteMarkerEntry',
'type' => 'object',
'sentAs' => 'DeleteMarker',
'properties' => array(
'Owner' => array(
'type' => 'object',
'properties' => array(
- 'ID' => array(
+ 'DisplayName' => array(
'type' => 'string',
),
- 'DisplayName' => array(
+ 'ID' => array(
'type' => 'string',
),
),
),
'Key' => array(
- 'description' => 'The object key.',
'type' => 'string',
),
'VersionId' => array(
- 'description' => 'Version ID of an object.',
'type' => 'string',
),
'IsLatest' => array(
- 'description' => 'Specifies whether the object is (true) or is not (false) the latest version of an object.',
'type' => 'boolean',
),
'LastModified' => array(
- 'description' => 'Date and time the object was last modified.',
'type' => 'string',
),
),
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'CommonPrefix',
'type' => 'object',
'properties' => array(
'Prefix' => array(
),
),
),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'IsTruncated' => array(
- 'description' => 'A flag that indicates whether or not Amazon S3 returned all of the results that satisfied the search criteria.',
'type' => 'boolean',
'location' => 'xml',
),
'type' => 'string',
'location' => 'xml',
),
+ 'NextMarker' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
'Contents' => array(
'type' => 'array',
'location' => 'xml',
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'Object',
'type' => 'object',
'properties' => array(
'Key' => array(
'type' => 'numeric',
),
'StorageClass' => array(
- 'description' => 'The class of storage used to store the object.',
'type' => 'string',
),
'Owner' => array(
'type' => 'object',
'properties' => array(
- 'ID' => array(
+ 'DisplayName' => array(
'type' => 'string',
),
- 'DisplayName' => array(
+ 'ID' => array(
'type' => 'string',
),
),
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'CommonPrefix',
'type' => 'object',
'properties' => array(
'Prefix' => array(
),
),
),
+ 'EncodingType' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'Bucket' => array(
- 'description' => 'Name of the bucket to which the multipart upload was initiated.',
'type' => 'string',
'location' => 'xml',
),
'Key' => array(
- 'description' => 'Object key for which the multipart upload was initiated.',
'type' => 'string',
'location' => 'xml',
),
'UploadId' => array(
- 'description' => 'Upload ID identifying the multipart upload whose parts are being listed.',
'type' => 'string',
'location' => 'xml',
),
'PartNumberMarker' => array(
- 'description' => 'Part number after which listing begins.',
'type' => 'numeric',
'location' => 'xml',
),
'NextPartNumberMarker' => array(
- 'description' => 'When a list is truncated, this element specifies the last part in the list, as well as the value to use for the part-number-marker request parameter in a subsequent request.',
'type' => 'numeric',
'location' => 'xml',
),
'MaxParts' => array(
- 'description' => 'Maximum number of parts that were allowed in the response.',
'type' => 'numeric',
'location' => 'xml',
),
'IsTruncated' => array(
- 'description' => 'Indicates whether the returned list of parts is truncated.',
'type' => 'boolean',
'location' => 'xml',
),
'xmlFlattened' => true,
),
'items' => array(
+ 'name' => 'Part',
'type' => 'object',
'sentAs' => 'Part',
'properties' => array(
'PartNumber' => array(
- 'description' => 'Part number identifying the part.',
'type' => 'numeric',
),
'LastModified' => array(
- 'description' => 'Date and time at which the part was uploaded.',
'type' => 'string',
),
'ETag' => array(
- 'description' => 'Entity tag returned when the part was uploaded.',
'type' => 'string',
),
'Size' => array(
- 'description' => 'Size of the uploaded part data.',
'type' => 'numeric',
),
),
),
),
'Initiator' => array(
- 'description' => 'Identifies who initiated the multipart upload.',
'type' => 'object',
'location' => 'xml',
'properties' => array(
'ID' => array(
- 'description' => 'If the principal is an AWS account, it provides the Canonical User ID. If the principal is an IAM User, it provides a user ARN value.',
'type' => 'string',
),
'DisplayName' => array(
- 'description' => 'Name of the Principal.',
'type' => 'string',
),
),
'type' => 'object',
'location' => 'xml',
'properties' => array(
- 'ID' => array(
+ 'DisplayName' => array(
'type' => 'string',
),
- 'DisplayName' => array(
+ 'ID' => array(
'type' => 'string',
),
),
),
'StorageClass' => array(
- 'description' => 'The class of storage used to store the object.',
'type' => 'string',
'location' => 'xml',
),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'Expiration' => array(
- 'description' => 'If the object expiration is configured, this will contain the expiration date (expiry-date) and rule ID (rule-id). The value of rule-id is URL encoded.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-expiration',
),
'ETag' => array(
- 'description' => 'Entity tag for the uploaded object.',
'type' => 'string',
'location' => 'header',
),
'ServerSideEncryption' => array(
- 'description' => 'The Server-side encryption algorithm used when storing this object in S3.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption',
),
'VersionId' => array(
- 'description' => 'Version of the object.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-version-id',
),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
+ ),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'ObjectURL' => array(
- 'description' => 'URL of the uploaded object',
),
),
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'ServerSideEncryption' => array(
- 'description' => 'The Server-side encryption algorithm used when storing this object in S3.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption',
),
'ETag' => array(
- 'description' => 'Entity tag for the uploaded object.',
'type' => 'string',
'location' => 'header',
),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
+ ),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
'additionalProperties' => true,
'properties' => array(
'CopySourceVersionId' => array(
- 'description' => 'The version of the source object that was copied, if you have enabled versioning on the source bucket.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-copy-source-version-id',
),
'ETag' => array(
- 'description' => 'Entity tag of the object.',
'type' => 'string',
'location' => 'xml',
),
'LastModified' => array(
- 'description' => 'Date and time at which the object was uploaded.',
'type' => 'string',
'location' => 'xml',
),
'ServerSideEncryption' => array(
- 'description' => 'The Server-side encryption algorithm used when storing this object in S3.',
'type' => 'string',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption',
),
+ 'SSECustomerAlgorithm' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
+ ),
+ 'SSECustomerKeyMD5' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
+ ),
'RequestId' => array(
- 'description' => 'Request ID of the operation',
'location' => 'header',
'sentAs' => 'x-amz-request-id',
),
),
),
),
+ 'iterators' => array(
+ 'ListBuckets' => array(
+ 'result_key' => 'Buckets',
+ ),
+ 'ListMultipartUploads' => array(
+ 'limit_key' => 'MaxUploads',
+ 'more_results' => 'IsTruncated',
+ 'output_token' => array(
+ 'NextKeyMarker',
+ 'NextUploadIdMarker',
+ ),
+ 'input_token' => array(
+ 'KeyMarker',
+ 'UploadIdMarker',
+ ),
+ 'result_key' => array(
+ 'Uploads',
+ 'CommonPrefixes',
+ ),
+ ),
+ 'ListObjectVersions' => array(
+ 'more_results' => 'IsTruncated',
+ 'limit_key' => 'MaxKeys',
+ 'output_token' => array(
+ 'NextKeyMarker',
+ 'NextVersionIdMarker',
+ ),
+ 'input_token' => array(
+ 'KeyMarker',
+ 'VersionIdMarker',
+ ),
+ 'result_key' => array(
+ 'Versions',
+ 'DeleteMarkers',
+ 'CommonPrefixes',
+ ),
+ ),
+ 'ListObjects' => array(
+ 'more_results' => 'IsTruncated',
+ 'limit_key' => 'MaxKeys',
+ 'output_token' => 'NextMarker',
+ 'input_token' => 'Marker',
+ 'result_key' => array(
+ 'Contents',
+ 'CommonPrefixes',
+ ),
+ ),
+ 'ListParts' => array(
+ 'more_results' => 'IsTruncated',
+ 'limit_key' => 'MaxParts',
+ 'output_token' => 'NextPartNumberMarker',
+ 'input_token' => 'PartNumberMarker',
+ 'result_key' => 'Parts',
+ ),
+ ),
'waiters' => array(
'__default__' => array(
'interval' => 5,
),
'BucketExists' => array(
'operation' => 'HeadBucket',
- 'description' => 'Wait until a bucket exists.',
'success.type' => 'output',
'ignore_errors' => array(
'NoSuchBucket',
),
'BucketNotExists' => array(
'operation' => 'HeadBucket',
- 'description' => 'Wait until a bucket does not exist.',
'success.type' => 'error',
'success.value' => 'NoSuchBucket',
),
'ObjectExists' => array(
'operation' => 'HeadObject',
- 'description' => 'Wait until an object exists.',
'success.type' => 'output',
'ignore_errors' => array(
'NoSuchKey',
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;
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;
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;
/**
* 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}
* @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
{
'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())
{
// 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'
))
// 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(
}
/**
- * 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.
*
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;
}
. '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);
}
/**
*/
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)
--- /dev/null
+<?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);
+ }
+ }
+ }
+}
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);
// 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);
);
}
- /**
- * {@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";
*
* @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') {
}
}
- if (empty($headers)) {
+ if (!$headers) {
return '';
- } else {
- ksort($headers);
-
- return implode("\n", $headers) . "\n";
}
+
+ ksort($headers);
+
+ return implode("\n", $headers) . "\n";
}
/**
*
* @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())
$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}";
}
}
*
* @return string
*/
- protected function parseBucketName(RequestInterface $request)
+ private function parseBucketName(RequestInterface $request)
{
$baseUrl = Url::factory($request->getClient()->getBaseUrl());
$baseHost = $baseUrl->getHost();
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 {}
--- /dev/null
+<?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(), '/');
+ }
+}
&& $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;
}
}
--- /dev/null
+<?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));
+ }
+ }
+}
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;
* 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
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;
}
}
// 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) {
$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;
$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']) {
* @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
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);
}
/**
$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());
}
/**
* 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.
*
$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'];
$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;
*/
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);
- }
+ // Skip empty result keys
+ if (!$this->objectIterator->valid()) {
+ return false;
+ }
- // Cache the object data for quick url_stat lookups used with RecursiveDirectoryIterator
- self::$nextStat = array($key => $stat);
- $this->objectIterator->next();
+ $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;
}
$params = $this->getOptions();
unset($params['seekable']);
- unset($params['throw_exceptions']);
return array(
'Bucket' => $parts[0],
*/
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;
}
);
$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;
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';
+ }
}
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;
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;
*/
public function setKeyPrefix($keyPrefix)
{
- $this->keyPrefix = $keyPrefix;
+ // Removing leading slash
+ $this->keyPrefix = ltrim($keyPrefix, '/');
return $this;
}
// 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(),
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);
- }
}
}
);
{
// 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) : ''), '/');
+ $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' => ''))));
+ $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 . '/')));
+ return $this->filterIterator(new \NoRewindIterator(new OpendirIterator($dh, $dir)));
}
}
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
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;
}
*/
protected function getTargetData($key)
{
+ $key = $this->cleanKey($key);
+
if (isset($this->cache[$key])) {
$result = $this->cache[$key];
unset($this->cache[$key]);
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, '/');
+ }
}
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
{
$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
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;
*/
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;
}
}
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;
*/
class UploadSync extends AbstractSync
{
+ const BEFORE_MULTIPART_BUILD = 's3.sync.before_multipart_build';
+
protected function init()
{
if (null == $this->options['multipart_upload_size']) {
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
}
// 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(
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;
*/
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
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();
$size = $command['Body']->getContentLength();
$percentage = number_format(($progress / $totalSize) * 100, 2);
fwrite($resource, "- Part {$command['PartNumber']} ({$size} bytes, {$percentage}%)\n");
- $progress .= $size;
+ $progress += $size;
}
);
});
+++ /dev/null
-<?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'],
- );
- }
-}
+++ /dev/null
-<?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;
- }
-}
+++ /dev/null
-<?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();
-}
+++ /dev/null
-<?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();
-}
+++ /dev/null
-<?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'],
- );
- }
-
-}
+++ /dev/null
-<?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;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?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);
- }
-}
\ No newline at end of file
+++ /dev/null
-<?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'],
- );
- }
-}
+++ /dev/null
-<?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'],
- );
- }
-}
+++ /dev/null
-<?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);
- }
-}
+++ /dev/null
-<?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
- );
- }
-}
+++ /dev/null
-<?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'],
- );
- }
-}
+++ /dev/null
-<?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'],
- );
- }
-}
+++ /dev/null
-<?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;
- }
-}
--- /dev/null
+{
+ "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"
+ }
+ }
+}
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;
--- /dev/null
+{
+ "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"
+ }
+ }
+}
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)
/** @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
*
*/
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;
}
{
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);
+ }
}
*
* @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());
*/
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
--- /dev/null
+{
+ "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"
+ }
+ }
+}
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;
*/
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;
}
*/
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)
} 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) {
} 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 {
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())
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;
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
*
*/
protected function initSsl()
{
- if ('system' == ($authority = $this->config[self::SSL_CERT_AUTHORITY])) {
+ $authority = $this->config[self::SSL_CERT_AUTHORITY];
+
+ if ($authority === 'system') {
return;
}
}
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);
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;
+ }
}
$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,
$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;
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
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) {
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;
$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
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;
}
}
+ 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
*
*/
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) {
++$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));
}
$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;
}
}
{
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();
}
}
);
}
}
+
+ /**
+ * @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;
+ }
}
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.
}
// All are claimed, so create one
- $handle = new CurlMulti();
+ $handle = new CurlMulti($this->selectTimeout);
$handle->setEventDispatcher($this->getEventDispatcher());
$this->handles[] = $handle;
));
}
- 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;
+ }
}
/**
*/
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 '';
+ }
+
+ $read = (string) $body->read($length);
+ if ($this->emitIo) {
+ $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read));
}
- return !$read ? '' : $read;
+ return $read;
}
}
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;
+ }
}
/**
} else {
$label = 'Unsuccessful response';
$class = __CLASS__;
- $e = new self();
}
$message = $label . PHP_EOL . implode(PHP_EOL, array(
{
protected $successfulRequests = array();
protected $failedRequests = array();
+ protected $exceptionForRequest = array();
/**
* Get all of the requests in the transfer
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
*
public function dispatch($eventName, array $context = array())
{
- $this->getEventDispatcher()->dispatch($eventName, new Event($context));
+ return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
}
/**
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)
// 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) {
return $this;
}
- public function addPostFile($field, $filename = null, $contentType = null)
+ public function addPostFile($field, $filename = null, $contentType = null, $postname = null)
{
$data = null;
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) {
* @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
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]);
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;
namespace Guzzle\Http\Message\Header;
-use Guzzle\Common\Collection;
use Guzzle\Common\ToArrayInterface;
/**
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();
}
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;
// 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;
}
*/
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
*
*/
public function getFilename();
+ /**
+ * Get the post name of the file
+ *
+ * @return string
+ */
+ public function getPostname();
+
/**
* Set the Content-Type of the file
*
// 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());
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)
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
*
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.
{
$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) {
} 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
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());
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;
}
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
}
}
{
$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);
+ }
+ }
}
* 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
}
/**
- * 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;
*/
public function fromExtension($extension)
{
+ $extension = strtolower($extension);
+
return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null;
}
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;
protected $aggregator;
/** @var array Cached PHP aggregator */
- protected static $defaultAggregator = null;
+ private static $defaultAggregator = null;
/**
* Parse a query string into a QueryString object
public static function fromString($query)
{
$q = new static();
+ if ($query === '') {
+ return $q;
+ }
- if ($query || $query === '0') {
- if ($query[0] == '?') {
- $query = substr($query, 1);
- }
- foreach (explode('&', $query) as $kvp) {
- $parts = explode('=', $kvp, 2);
- $key = rawurldecode($parts[0]);
-
- if ($paramIsPhpStyleArray = substr($key, -2) == '[]') {
- $key = substr($key, 0, -2);
- }
+ $foundDuplicates = $foundPhpStyle = false;
- if (isset($parts[1])) {
- $value = rawurldecode(str_replace('+', '%20', $parts[1]));
- if ($paramIsPhpStyleArray && !$q->hasKey($key)) {
- $value = array($value);
- }
+ 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);
+ }
+ 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;
}
* Convert the query string parameters to a query string string
*
* @return string
+ * @throws RuntimeException
*/
public function __toString()
{
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);
}
/**
$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);
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);
+ }
}
{
parent::__construct($body);
$this->setLimit($limit)->setOffset($offset);
- $this->body->seek($offset);
}
/**
public function isConsumed()
{
- return (($this->offset + $this->limit) - $this->body->ftell()) <= 0;
+ return $this->body->isConsumed() ||
+ ($this->body->ftell() >= $this->offset + $this->limit);
}
/**
// 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
$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;
$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);
/**
* 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
*
##
## ca-bundle.crt -- Bundle of CA Root Certificates
##
-## Certificate data from Mozilla as of: Sat Dec 29 20:03:40 2012
+## Certificate data from Mozilla as of: Tue Apr 22 08:29:31 2014
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
## file (certdata.txt). This file can be found in the mozilla source tree:
-## http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1
+## http://mxr.mozilla.org/mozilla-release/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1
##
## It contains the certificates in PEM format and therefore
## can be directly used with curl / libcurl / php_curl, or with
## Just configure this file as the SSLCACertificateFile.
##
-# @(#) $RCSfile: certdata.txt,v $ $Revision: 1.87 $ $Date: 2012/12/29 16:32:45 $
GTE CyberTrust Global Root
==========================
70+sB3c4
-----END CERTIFICATE-----
-Digital Signature Trust Co. Global CA 1
-=======================================
------BEGIN CERTIFICATE-----
-MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE
-ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMTAeFw05ODEy
-MTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs
-IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUA
-A4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJE
-NySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2i
-o74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo
-BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0
-dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw
-IoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQY
-MBaAFGp5fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAM
-BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB
-ACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lNQseSJqBcNJo4cvj9axY+IO6CizEq
-kzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4
-RbyhkwS7hp86W0N6w4pl
------END CERTIFICATE-----
-
-Digital Signature Trust Co. Global CA 3
-=======================================
------BEGIN CERTIFICATE-----
-MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE
-ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMjAeFw05ODEy
-MDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs
-IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUA
-A4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGOD
-VvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JS
-xhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo
-BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0
-dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw
-IoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQY
-MBaAFB6CTShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAM
-BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB
-AEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHRxdf0CiUPPXiBng+xZ8SQTGPdXqfi
-up/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1
-mPnHfxsb1gYgAlihw6ID
------END CERTIFICATE-----
-
Verisign Class 3 Public Primary Certification Authority
=======================================================
-----BEGIN CERTIFICATE-----
Tqj/ZA1k
-----END CERTIFICATE-----
-Verisign Class 1 Public Primary Certification Authority - G2
-============================================================
------BEGIN CERTIFICATE-----
-MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT
-MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFy
-eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
-biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
-dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT
-MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFy
-eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln
-biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
-dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgd
-k4xWArzZbxpvUjZudVYKVdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIq
-WpDBucSmFc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQIDAQAB
-MA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0Jh9ZrbWB85a7FkCMM
-XErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2uluIncrKTdcu1OofdPvAbT6shkdHvC
-lUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68DzFc6PLZ
------END CERTIFICATE-----
-
-Verisign Class 2 Public Primary Certification Authority - G2
-============================================================
------BEGIN CERTIFICATE-----
-MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQswCQYDVQQGEwJV
-UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1h
-cnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNp
-Z24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1
-c3QgTmV0d29yazAeFw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJV
-UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1h
-cnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNp
-Z24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1
-c3QgTmV0d29yazCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjx
-nNuX6Zr8wgQGE75fUsjMHiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRC
-wiNPStjwDqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cCAwEA
-ATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9jinb3/7aHmZuovCfTK
-1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAXrXfMSTWqz9iP0b63GJZHc2pUIjRk
-LbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnInjBJ7xUS0rg==
------END CERTIFICATE-----
-
Verisign Class 3 Public Primary Certification Authority - G2
============================================================
-----BEGIN CERTIFICATE-----
on+jjBXu
-----END CERTIFICATE-----
-Verisign Class 1 Public Primary Certification Authority - G3
-============================================================
------BEGIN CERTIFICATE-----
-MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV
-UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
-cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
-IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh
-dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw
-CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
-dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv
-cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkg
-Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
-ggEBAN2E1Lm0+afY8wR4nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/E
-bRrsC+MO8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjVojYJ
-rKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjbPG7PoBMAGrgnoeS+
-Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP26KbqxzcSXKMpHgLZ2x87tNcPVkeB
-FQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vrn5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
-q2aN17O6x5q25lXQBfGfMY1aqtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/N
-y9Sn2WCVhDr4wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3
-ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrspSCAaWihT37h
-a88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4E1Z5T21Q6huwtVexN2ZYI/Pc
-D98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g==
------END CERTIFICATE-----
-
-Verisign Class 2 Public Primary Certification Authority - G3
-============================================================
------BEGIN CERTIFICATE-----
-MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJBgNVBAYTAlVT
-MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29y
-azE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ug
-b25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0
-aW9uIEF1dGhvcml0eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJ
-BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1
-c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y
-aXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBD
-ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEArwoNwtUs22e5LeWUJ92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6
-tW8UvxDOJxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUYwZF7
-C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9okoqQHgiBVrKtaaNS
-0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjNqWm6o+sdDZykIKbBoMXRRkwXbdKs
-Zj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/ESrg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0
-JhU8wI1NQ0kdvekhktdmnLfexbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf
-0xwLRtxyID+u7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU
-sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RIsH/7NiXaldDx
-JBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTPcjnhsUPgKM+351psE2tJs//j
-GHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q
------END CERTIFICATE-----
-
Verisign Class 3 Public Primary Certification Authority - G3
============================================================
-----BEGIN CERTIFICATE-----
Entrust.net Premium 2048 Secure Server CA
=========================================
-----BEGIN CERTIFICATE-----
-MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u
+MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u
ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp
bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV
BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx
-NzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3
+NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3
d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u
ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr
hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW
nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi
-VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo3QwcjARBglghkgBhvhC
-AQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdER
-gL7YibkIozH5oSQJFrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B
-AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo
-oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQh7A6tcOdBTcS
-o8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z
-2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjX
-OP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ==
+VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ
+KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy
+T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf
+zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT
+J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e
+nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE=
-----END CERTIFICATE-----
Baltimore CyberTrust Root
KpYrtWKmpj29f5JZzVoqgrI3eQ==
-----END CERTIFICATE-----
-Equifax Secure eBusiness CA 2
-=============================
------BEGIN CERTIFICATE-----
-MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEXMBUGA1UE
-ChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0y
-MB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoT
-DkVxdWlmYXggU2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCB
-nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn
-2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5
-BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAG
-A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUx
-JjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoG
-A1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9e
-uSBIplBqy/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMB
-Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAAyGgq3oThr1
-jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia
-78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUm
-V+GRMOrN
------END CERTIFICATE-----
-
AddTrust Low-Value Services Root
================================
-----BEGIN CERTIFICATE-----
X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
-----END CERTIFICATE-----
-UTN-USER First-Network Applications
-===================================
------BEGIN CERTIFICATE-----
-MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUFADCBozELMAkGA1UE
-BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
-IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzAp
-BgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0ODM5
-WhcNMTkwNzA5MTg1NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5T
-YWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
-dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBB
-cHBsaWNhdGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz+5Gh5DZVhawGNFug
-mliy+LUPBXeDrjKxdpJo7CNKyXY/45y2N3kDuatpjQclthln5LAbGHNhSuh+zdMvZOOmfAz6F4Cj
-DUeJT1FxL+78P/m4FoCHiZMlIJpDgmkkdihZNaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXu
-Ozr0hAReYFmnjDRy7rh4xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1axwi
-P8vv/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6gyN7igEL66S/ozjIE
-j3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8w
-HQYDVR0OBBYEFPqGydvguul49Uuo1hXf8NPhahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9j
-cmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0G
-CSqGSIb3DQEBBQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXhi6r/fWRRzwr/vH3Y
-IWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUqf9FuVSTiuwL7MT++6LzsQCv4AdRWOOTK
-RIK1YSAhZ2X28AvnNPilwpyjXEAfhZOVBt5P1CeptqX8Fs1zMT+4ZSfP1FMa8Kxun08FDAOBp4Qp
-xFq9ZFdyrTvPNximmMatBrTcCKME1SmklpoSZ0qMYEWd8SOasACcaLWYUNPvji6SZbFIPiG+FTAq
-DbUMo2s/rn9X9R+WfN9v3YIwLGUbQErNaLly7HF27FSOH4UMAWr6pjisH8SE
------END CERTIFICATE-----
-
America Online Root Certification Authority 1
=============================================
-----BEGIN CERTIFICATE-----
FL39vmwLAw==
-----END CERTIFICATE-----
-Sonera Class 1 Root CA
-======================
------BEGIN CERTIFICATE-----
-MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG
-U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAxMDQwNjEwNDkxM1oXDTIxMDQw
-NjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh
-IENsYXNzMSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H88
-7dF+2rDNbS82rDTG29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9
-EJUkoVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk3w0LBUXl
-0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBLqdReLjVQCfOAl/QMF645
-2F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIINnvmLVz5MxxftLItyM19yejhW1ebZrgUa
-HXVFsculJRwSVzb9IjcCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZT
-iFIwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE9
-28Jj2VuXZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0HDjxV
-yhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VOTzF2nBBhjrZTOqMR
-vq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2UvkVrCqIexVmiUefkl98HVrhq4uz2P
-qYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4wzMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9Z
-IRlXvVWa
------END CERTIFICATE-----
-
Sonera Class 2 Root CA
======================
-----BEGIN CERTIFICATE-----
Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l
-----END CERTIFICATE-----
-TDC OCES Root CA
-================
------BEGIN CERTIFICATE-----
-MIIFGTCCBAGgAwIBAgIEPki9xDANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQGEwJESzEMMAoGA1UE
-ChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTAeFw0wMzAyMTEwODM5MzBaFw0zNzAyMTEwOTA5
-MzBaMDExCzAJBgNVBAYTAkRLMQwwCgYDVQQKEwNUREMxFDASBgNVBAMTC1REQyBPQ0VTIENBMIIB
-IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArGL2YSCyz8DGhdfjeebM7fI5kqSXLmSjhFuH
-nEz9pPPEXyG9VhDr2y5h7JNp46PMvZnDBfwGuMo2HP6QjklMxFaaL1a8z3sM8W9Hpg1DTeLpHTk0
-zY0s2RKY+ePhwUp8hjjEqcRhiNJerxomTdXkoCJHhNlktxmW/OwZ5LKXJk5KTMuPJItUGBxIYXvV
-iGjaXbXqzRowwYCDdlCqT9HU3Tjw7xb04QxQBr/q+3pJoSgrHPb8FTKjdGqPqcNiKXEx5TukYBde
-dObaE+3pHx8b0bJoc8YQNHVGEBDjkAB2QMuLt0MJIf+rTpPGWOmlgtt3xDqZsXKVSQTwtyv6e1mO
-3QIDAQABo4ICNzCCAjMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwgewGA1UdIASB
-5DCB4TCB3gYIKoFQgSkBAQEwgdEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2VydGlmaWthdC5k
-ay9yZXBvc2l0b3J5MIGdBggrBgEFBQcCAjCBkDAKFgNUREMwAwIBARqBgUNlcnRpZmlrYXRlciBm
-cmEgZGVubmUgQ0EgdWRzdGVkZXMgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4xLiBDZXJ0aWZp
-Y2F0ZXMgZnJvbSB0aGlzIENBIGFyZSBpc3N1ZWQgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4x
-LjARBglghkgBhvhCAQEEBAMCAAcwgYEGA1UdHwR6MHgwSKBGoESkQjBAMQswCQYDVQQGEwJESzEM
-MAoGA1UEChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTENMAsGA1UEAxMEQ1JMMTAsoCqgKIYm
-aHR0cDovL2NybC5vY2VzLmNlcnRpZmlrYXQuZGsvb2Nlcy5jcmwwKwYDVR0QBCQwIoAPMjAwMzAy
-MTEwODM5MzBagQ8yMDM3MDIxMTA5MDkzMFowHwYDVR0jBBgwFoAUYLWF7FZkfhIZJ2cdUBVLc647
-+RIwHQYDVR0OBBYEFGC1hexWZH4SGSdnHVAVS3OuO/kSMB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6
-NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEACromJkbTc6gJ82sLMJn9iuFXehHTuJTXCRBuo7E4
-A9G28kNBKWKnctj7fAXmMXAnVBhOinxO5dHKjHiIzxvTkIvmI/gLDjNDfZziChmPyQE+dF10yYsc
-A+UYyAFMP8uXBV2YcaaYb7Z8vTd/vuGTJW1v8AqtFxjhA7wHKcitJuj4YfD9IQl+mo6paH1IYnK9
-AOoBmbgGglGBTvH1tJFUuSN6AJqfXY3gPGS5GhKSKseCRHI53OI8xthV9RVOyAUO28bQYqbsFbS1
-AoLbrIyigfCbmTH1ICCoiGEKB5+U/NDXG8wuF/MEJ3Zn61SD/aSQfgY9BKNDLdr8C2LqL19iUw==
------END CERTIFICATE-----
-
UTN DATACorp SGC Root CA
========================
-----BEGIN CERTIFICATE-----
DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI
-----END CERTIFICATE-----
-UTN USERFirst Email Root CA
-===========================
------BEGIN CERTIFICATE-----
-MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCBrjELMAkGA1UE
-BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
-IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0
-BgNVBAMTLVVUTi1VU0VSRmlyc3QtQ2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05
-OTA3MDkxNzI4NTBaFw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQx
-FzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsx
-ITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UEAxMtVVROLVVTRVJGaXJz
-dC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWlsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
-MIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3BYHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIx
-B8dOtINknS4p1aJkxIW9hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8
-om+rWV6lL8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLmSGHG
-TPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM1tZUOt4KpLoDd7Nl
-yP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws6wIDAQABo4G5MIG2MAsGA1UdDwQE
-AwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNV
-HR8EUTBPME2gS6BJhkdodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGll
-bnRBdXRoZW50aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
-AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u7mFVbwQ+zzne
-xRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0xtcgBEXkzYABurorbs6q15L+
-5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQrfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarV
-NZ1yQAOJujEdxRBoUp7fooXFXAimeOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZ
-w7JHpsIyYdfHb0gkUSeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ=
------END CERTIFICATE-----
-
UTN USERFirst Hardware Root CA
==============================
-----BEGIN CERTIFICATE-----
nfhmqA==
-----END CERTIFICATE-----
-UTN USERFirst Object Root CA
-============================
------BEGIN CERTIFICATE-----
-MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UE
-BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
-IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAb
-BgNVBAMTFFVUTi1VU0VSRmlyc3QtT2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAz
-NlowgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkx
-HjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3dy51c2Vy
-dHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCCASIwDQYJKoZIhvcNAQEB
-BQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicPHxzfOpuCaDDASmEd8S8O+r5596Uj71VR
-loTN2+O5bj4x2AogZ8f02b+U60cEPgLOKqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQ
-w5ujm9M89RKZd7G3CeBo5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vu
-lBe3/IW+pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehbkkj7
-RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUCAwEAAaOBrzCBrDAL
-BgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU2u1kdBScFDyr3ZmpvVsoTYs8
-ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmly
-c3QtT2JqZWN0LmNybDApBgNVHSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQw
-DQYJKoZIhvcNAQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujw
-NTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXBmMiKVl0+7kNO
-PmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU4U3GDZlDAQ0Slox4nb9QorFE
-qmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK581OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCG
-hU3IfdeLA/5u1fedFqySLKAj5ZyRUh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g=
------END CERTIFICATE-----
-
Camerfirma Chambers of Commerce Root
====================================
-----BEGIN CERTIFICATE-----
t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
-----END CERTIFICATE-----
-NetLock Qualified (Class QA) Root
-=================================
------BEGIN CERTIFICATE-----
-MIIG0TCCBbmgAwIBAgIBezANBgkqhkiG9w0BAQUFADCByTELMAkGA1UEBhMCSFUxETAPBgNVBAcT
-CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV
-BAsTEVRhbnVzaXR2YW55a2lhZG9rMUIwQAYDVQQDEzlOZXRMb2NrIE1pbm9zaXRldHQgS296amVn
-eXpvaSAoQ2xhc3MgUUEpIFRhbnVzaXR2YW55a2lhZG8xHjAcBgkqhkiG9w0BCQEWD2luZm9AbmV0
-bG9jay5odTAeFw0wMzAzMzAwMTQ3MTFaFw0yMjEyMTUwMTQ3MTFaMIHJMQswCQYDVQQGEwJIVTER
-MA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNhZ2kgS2Z0
-LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxQjBABgNVBAMTOU5ldExvY2sgTWlub3NpdGV0
-dCBLb3pqZWd5em9pIChDbGFzcyBRQSkgVGFudXNpdHZhbnlraWFkbzEeMBwGCSqGSIb3DQEJARYP
-aW5mb0BuZXRsb2NrLmh1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx1Ilstg91IRV
-CacbvWy5FPSKAtt2/GoqeKvld/Bu4IwjZ9ulZJm53QE+b+8tmjwi8F3JV6BVQX/yQ15YglMxZc4e
-8ia6AFQer7C8HORSjKAyr7c3sVNnaHRnUPYtLmTeriZ539+Zhqurf4XsoPuAzPS4DB6TRWO53Lhb
-m+1bOdRfYrCnjnxmOCyqsQhjF2d9zL2z8cM/z1A57dEZgxXbhxInlrfa6uWdvLrqOU+L73Sa58XQ
-0uqGURzk/mQIKAR5BevKxXEOC++r6uwSEaEYBTJp0QwsGj0lmT+1fMptsK6ZmfoIYOcZwvK9UdPM
-0wKswREMgM6r3JSda6M5UzrWhQIDAMV9o4ICwDCCArwwEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV
-HQ8BAf8EBAMCAQYwggJ1BglghkgBhvhCAQ0EggJmFoICYkZJR1lFTEVNISBFemVuIHRhbnVzaXR2
-YW55IGEgTmV0TG9jayBLZnQuIE1pbm9zaXRldHQgU3pvbGdhbHRhdGFzaSBTemFiYWx5emF0YWJh
-biBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBBIG1pbm9zaXRldHQgZWxla3Ryb25p
-a3VzIGFsYWlyYXMgam9naGF0YXMgZXJ2ZW55ZXN1bGVzZW5laywgdmFsYW1pbnQgZWxmb2dhZGFz
-YW5hayBmZWx0ZXRlbGUgYSBNaW5vc2l0ZXR0IFN6b2xnYWx0YXRhc2kgU3phYmFseXphdGJhbiwg
-YXogQWx0YWxhbm9zIFN6ZXJ6b2Rlc2kgRmVsdGV0ZWxla2JlbiBlbG9pcnQgZWxsZW5vcnplc2kg
-ZWxqYXJhcyBtZWd0ZXRlbGUuIEEgZG9rdW1lbnR1bW9rIG1lZ3RhbGFsaGF0b2sgYSBodHRwczov
-L3d3dy5uZXRsb2NrLmh1L2RvY3MvIGNpbWVuIHZhZ3kga2VyaGV0b2sgYXogaW5mb0BuZXRsb2Nr
-Lm5ldCBlLW1haWwgY2ltZW4uIFdBUk5JTkchIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0
-aGlzIGNlcnRpZmljYXRlIGFyZSBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIFF1YWxpZmllZCBDUFMg
-YXZhaWxhYmxlIGF0IGh0dHBzOi8vd3d3Lm5ldGxvY2suaHUvZG9jcy8gb3IgYnkgZS1tYWlsIGF0
-IGluZm9AbmV0bG9jay5uZXQwHQYDVR0OBBYEFAlqYhaSsFq7VQ7LdTI6MuWyIckoMA0GCSqGSIb3
-DQEBBQUAA4IBAQCRalCc23iBmz+LQuM7/KbD7kPgz/PigDVJRXYC4uMvBcXxKufAQTPGtpvQMznN
-wNuhrWw3AkxYQTvyl5LGSKjN5Yo5iWH5Upfpvfb5lHTocQ68d4bDBsxafEp+NFAwLvt/MpqNPfMg
-W/hqyobzMUwsWYACff44yTB1HLdV47yfuqhthCgFdbOLDcCRVCHnpgu0mfVRQdzNo0ci2ccBgcTc
-R08m6h/t280NmPSjnLRzMkqWmf68f8glWPhY83ZmiVSkpj7EUFy6iRiCdUgh0k8T6GB+B3bbELVR
-5qq5aKrN9p2QdRLqOBrKROi3macqaJVmlaut74nLYKkGEsaUR+ko
------END CERTIFICATE-----
-
NetLock Notary (Class A) Root
=============================
-----BEGIN CERTIFICATE-----
+fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS
-----END CERTIFICATE-----
-Firmaprofesional Root CA
-========================
------BEGIN CERTIFICATE-----
-MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMxIjAgBgNVBAcT
-GUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1dG9yaWRhZCBkZSBDZXJ0aWZp
-Y2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FA
-ZmlybWFwcm9mZXNpb25hbC5jb20wHhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTEL
-MAkGA1UEBhMCRVMxIjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMT
-OUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2
-ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20wggEiMA0GCSqGSIb3DQEB
-AQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5uCp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5V
-j1H5WuretXDE7aTt/6MNbg9kUDGvASdYrv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJH
-lShbz++AbOCQl4oBPB3zhxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf
-3H5idPayBQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcLiam8
-NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcbAgMBAAGjgZ8wgZww
-KgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lvbmFsLmNvbTASBgNVHRMBAf8ECDAG
-AQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1Ud
-DwEB/wQEAwIBBjAdBgNVHQ4EFgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQAD
-ggEBAEdz/o0nVPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq
-u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36mhoEyIwOdyPdf
-wUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzflZKG+TQyTmAyX9odtsz/ny4Cm
-7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBpQWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YG
-VM+h4k0460tQtcsm9MracEpqoeJ5quGnM/b9Sh/22WA=
------END CERTIFICATE-----
-
-Wells Fargo Root CA
-===================
------BEGIN CERTIFICATE-----
-MIID5TCCAs2gAwIBAgIEOeSXnjANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UEBhMCVVMxFDASBgNV
-BAoTC1dlbGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhv
-cml0eTEvMC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
-MDAxMDExMTY0MTI4WhcNMjEwMTE0MTY0MTI4WjCBgjELMAkGA1UEBhMCVVMxFDASBgNVBAoTC1dl
-bGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEv
-MC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVqDM7Jvk0/82bfuUER84A4n135zHCLielTWi5MbqNQ1mX
-x3Oqfz1cQJ4F5aHiidlMuD+b+Qy0yGIZLEWukR5zcUHESxP9cMIlrCL1dQu3U+SlK93OvRw6esP3
-E48mVJwWa2uv+9iWsWCaSOAlIiR5NM4OJgALTqv9i86C1y8IcGjBqAr5dE8Hq6T54oN+J3N0Prj5
-OEL8pahbSCOz6+MlsoCultQKnMJ4msZoGK43YjdeUXWoWGPAUe5AeH6orxqg4bB4nVCMe+ez/I4j
-sNtlAHCEAQgAFG5Uhpq6zPk3EPbg3oQtnaSFN9OH4xXQwReQfhkhahKpdv0SAulPIV4XAgMBAAGj
-YTBfMA8GA1UdEwEB/wQFMAMBAf8wTAYDVR0gBEUwQzBBBgtghkgBhvt7hwcBCzAyMDAGCCsGAQUF
-BwIBFiRodHRwOi8vd3d3LndlbGxzZmFyZ28uY29tL2NlcnRwb2xpY3kwDQYJKoZIhvcNAQEFBQAD
-ggEBANIn3ZwKdyu7IvICtUpKkfnRLb7kuxpo7w6kAOnu5+/u9vnldKTC2FJYxHT7zmu1Oyl5GFrv
-m+0fazbuSCUlFLZWohDo7qd/0D+j0MNdJu4HzMPBJCGHHt8qElNvQRbn7a6U+oxy+hNH8Dx+rn0R
-OhPs7fpvcmR7nX1/Jv16+yWt6j4pf0zjAFcysLPp7VMX2YuyFA4w6OXVE8Zkr8QA1dhYJPz1j+zx
-x32l2w8n0cbyQIjmH/ZhqPRCyLk306m+LFZ4wnKbWV01QIroTmMatukgalHizqSQ33ZwmVxwQ023
-tqcZZE6St8WRPH9IFmV7Fv3L/PvZ1dZPIWU7Sn9Ho/s=
------END CERTIFICATE-----
-
Swisscom Root CA 1
==================
-----BEGIN CERTIFICATE-----
UrbnBEI=
-----END CERTIFICATE-----
-SwissSign Platinum CA - G2
-==========================
------BEGIN CERTIFICATE-----
-MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UEBhMCQ0gxFTAT
-BgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWduIFBsYXRpbnVtIENBIC0gRzIw
-HhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAwWjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMM
-U3dpc3NTaWduIEFHMSMwIQYDVQQDExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJ
-KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu
-669yIIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2HtnIuJpX+UF
-eNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+6ixuEFGSzH7VozPY1kne
-WCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5objM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIo
-j5+saCB9bzuohTEJfwvH6GXp43gOCWcwizSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/6
-8++QHkwFix7qepF6w9fl+zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34T
-aNhxKFrYzt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaPpZjy
-domyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtFKwH3HBqi7Ri6Cr2D
-+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuWae5ogObnmLo2t/5u7Su9IPhlGdpV
-CX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMBAAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
-EwEB/wQFMAMBAf8wHQYDVR0OBBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCv
-zAeHFUdvOMW0ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW
-IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUAA4ICAQAIhab1
-Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0uMoI3LQwnkAHFmtllXcBrqS3
-NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4
-U99REJNi54Av4tHgvI42Rncz7Lj7jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8
-KV2LwUvJ4ooTHbG/u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl
-9x8DYSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1puEa+S1B
-aYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXaicYwu+uPyyIIoK6q8QNs
-OktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbGDI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSY
-Mdp08YSTcU1f+2BY0fvEwW2JorsgH51xkcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAci
-IfNAChs0B0QTwoRqjt8ZWr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g==
------END CERTIFICATE-----
-
SwissSign Gold CA - G2
======================
-----BEGIN CERTIFICATE-----
okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0=
-----END CERTIFICATE-----
-S-TRUST Authentication and Encryption Root CA 2005 PN
-=====================================================
------BEGIN CERTIFICATE-----
-MIIEezCCA2OgAwIBAgIQNxkY5lNUfBq1uMtZWts1tzANBgkqhkiG9w0BAQUFADCBrjELMAkGA1UE
-BhMCREUxIDAeBgNVBAgTF0JhZGVuLVd1ZXJ0dGVtYmVyZyAoQlcpMRIwEAYDVQQHEwlTdHV0dGdh
-cnQxKTAnBgNVBAoTIERldXRzY2hlciBTcGFya2Fzc2VuIFZlcmxhZyBHbWJIMT4wPAYDVQQDEzVT
-LVRSVVNUIEF1dGhlbnRpY2F0aW9uIGFuZCBFbmNyeXB0aW9uIFJvb3QgQ0EgMjAwNTpQTjAeFw0w
-NTA2MjIwMDAwMDBaFw0zMDA2MjEyMzU5NTlaMIGuMQswCQYDVQQGEwJERTEgMB4GA1UECBMXQmFk
-ZW4tV3VlcnR0ZW1iZXJnIChCVykxEjAQBgNVBAcTCVN0dXR0Z2FydDEpMCcGA1UEChMgRGV1dHNj
-aGVyIFNwYXJrYXNzZW4gVmVybGFnIEdtYkgxPjA8BgNVBAMTNVMtVFJVU1QgQXV0aGVudGljYXRp
-b24gYW5kIEVuY3J5cHRpb24gUm9vdCBDQSAyMDA1OlBOMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
-MIIBCgKCAQEA2bVKwdMz6tNGs9HiTNL1toPQb9UY6ZOvJ44TzbUlNlA0EmQpoVXhOmCTnijJ4/Ob
-4QSwI7+Vio5bG0F/WsPoTUzVJBY+h0jUJ67m91MduwwA7z5hca2/OnpYH5Q9XIHV1W/fuJvS9eXL
-g3KSwlOyggLrra1fFi2SU3bxibYs9cEv4KdKb6AwajLrmnQDaHgTncovmwsdvs91DSaXm8f1Xgqf
-eN+zvOyauu9VjxuapgdjKRdZYgkqeQd3peDRF2npW932kKvimAoA0SVtnteFhy+S8dF2g08LOlk3
-KC8zpxdQ1iALCvQm+Z845y2kuJuJja2tyWp9iRe79n+Ag3rm7QIDAQABo4GSMIGPMBIGA1UdEwEB
-/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTVFJv
-bmxpbmUxLTIwNDgtNTAdBgNVHQ4EFgQUD8oeXHngovMpttKFswtKtWXsa1IwHwYDVR0jBBgwFoAU
-D8oeXHngovMpttKFswtKtWXsa1IwDQYJKoZIhvcNAQEFBQADggEBAK8B8O0ZPCjoTVy7pWMciDMD
-pwCHpB8gq9Yc4wYfl35UvbfRssnV2oDsF9eK9XvCAPbpEW+EoFolMeKJ+aQAPzFoLtU96G7m1R08
-P7K9n3frndOMusDXtk3sU5wPBG7qNWdX4wple5A64U8+wwCSersFiXOMy6ZNwPv2AtawB6MDwidA
-nwzkhYItr5pCHdDHjfhA7p0GVxzZotiAFP7hYy0yh9WUUpY6RsZxlj33mA6ykaqP2vROJAA5Veit
-F7nTNCtKqUDMFypVZUF0Qn71wK/Ik63yGFs9iQzbRzkk+OBM8h+wPQrKBU6JIRrjKpms/H+h8Q8b
-Hz2eBIPdltkdOpQ=
------END CERTIFICATE-----
-
Microsec e-Szigno Root CA
=========================
-----BEGIN CERTIFICATE-----
Cm26OWMohpLzGITY+9HPBVZkVw==
-----END CERTIFICATE-----
-ComSign CA
-==========
------BEGIN CERTIFICATE-----
-MIIDkzCCAnugAwIBAgIQFBOWgxRVjOp7Y+X8NId3RDANBgkqhkiG9w0BAQUFADA0MRMwEQYDVQQD
-EwpDb21TaWduIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTMy
-MThaFw0yOTAzMTkxNTAyMThaMDQxEzARBgNVBAMTCkNvbVNpZ24gQ0ExEDAOBgNVBAoTB0NvbVNp
-Z24xCzAJBgNVBAYTAklMMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8ORUaSvTx49q
-ROR+WCf4C9DklBKK8Rs4OC8fMZwG1Cyn3gsqrhqg455qv588x26i+YtkbDqthVVRVKU4VbirgwTy
-P2Q298CNQ0NqZtH3FyrV7zb6MBBC11PN+fozc0yz6YQgitZBJzXkOPqUm7h65HkfM/sb2CEJKHxN
-GGleZIp6GZPKfuzzcuc3B1hZKKxC+cX/zT/npfo4sdAMx9lSGlPWgcxCejVb7Us6eva1jsz/D3zk
-YDaHL63woSV9/9JLEYhwVKZBqGdTUkJe5DSe5L6j7KpiXd3DTKaCQeQzC6zJMw9kglcq/QytNuEM
-rkvF7zuZ2SOzW120V+x0cAwqTwIDAQABo4GgMIGdMAwGA1UdEwQFMAMBAf8wPQYDVR0fBDYwNDAy
-oDCgLoYsaHR0cDovL2ZlZGlyLmNvbXNpZ24uY28uaWwvY3JsL0NvbVNpZ25DQS5jcmwwDgYDVR0P
-AQH/BAQDAgGGMB8GA1UdIwQYMBaAFEsBmz5WGmU2dst7l6qSBe4y5ygxMB0GA1UdDgQWBBRLAZs+
-VhplNnbLe5eqkgXuMucoMTANBgkqhkiG9w0BAQUFAAOCAQEA0Nmlfv4pYEWdfoPPbrxHbvUanlR2
-QnG0PFg/LUAlQvaBnPGJEMgOqnhPOAlXsDzACPw1jvFIUY0McXS6hMTXcpuEfDhOZAYnKuGntewI
-mbQKDdSFc8gS4TXt8QUxHXOZDOuWyt3T5oWq8Ir7dcHyCTxlZWTzTNity4hp8+SDtwy9F1qWF8pb
-/627HOkthIDYIb6FUtnUdLlphbpN7Sgy6/lhSuTENh4Z3G+EER+V9YMoGKgzkkMn3V0TBEVPh9VG
-zT2ouvDzuFYkRes3x+F2T3I5GN9+dHLHcy056mDmrRGiVod7w2ia/viMcKjfZTL0pECMocJEAw6U
-AGegcQCCSA==
------END CERTIFICATE-----
-
ComSign Secured CA
==================
-----BEGIN CERTIFICATE-----
tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA==
-----END CERTIFICATE-----
-Verisign Class 1 Public Primary Certification Authority
-=======================================================
------BEGIN CERTIFICATE-----
-MIICPDCCAaUCED9pHoGc8JpK83P/uUii5N0wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx
-FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5
-IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow
-XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAx
-IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
-A4GNADCBiQKBgQDlGb9to1ZhLZlIcfZn3rmN67eehoAKkQ76OCWvRoiC5XOooJskXQ0fzGVuDLDQ
-VoQYh5oGmxChc9+0WDlrbsH2FdWoqD+qEgaNMax/sDTXjzRniAnNFBHiTkVWaR94AoDa3EeRKbs2
-yWNcxeDXLYd7obcysHswuiovMaruo2fa2wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFgVKTk8d6Pa
-XCUDfGD67gmZPCcQcMgMCeazh88K4hiWNWLMv5sneYlfycQJ9M61Hd8qveXbhpxoJeUwfLaJFf5n
-0a3hUKw8fGJLj7qE1xIVGx/KXQ/BUpQqEZnae88MNhPVNdwQGVnqlMEAv3WP2fr9dgTbYruQagPZ
-RjXZ+Hxb
------END CERTIFICATE-----
-
Verisign Class 3 Public Primary Certification Authority
=======================================================
-----BEGIN CERTIFICATE-----
kpeDMdmztcpHWD9f
-----END CERTIFICATE-----
-TC TrustCenter Universal CA III
-===============================
------BEGIN CERTIFICATE-----
-MIID4TCCAsmgAwIBAgIOYyUAAQACFI0zFQLkbPQwDQYJKoZIhvcNAQEFBQAwezELMAkGA1UEBhMC
-REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy
-IFVuaXZlcnNhbCBDQTEoMCYGA1UEAxMfVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIElJSTAe
-Fw0wOTA5MDkwODE1MjdaFw0yOTEyMzEyMzU5NTlaMHsxCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNU
-QyBUcnVzdENlbnRlciBHbWJIMSQwIgYDVQQLExtUQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0Ex
-KDAmBgNVBAMTH1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQSBJSUkwggEiMA0GCSqGSIb3DQEB
-AQUAA4IBDwAwggEKAoIBAQDC2pxisLlxErALyBpXsq6DFJmzNEubkKLF5+cvAqBNLaT6hdqbJYUt
-QCggbergvbFIgyIpRJ9Og+41URNzdNW88jBmlFPAQDYvDIRlzg9uwliT6CwLOunBjvvya8o84pxO
-juT5fdMnnxvVZ3iHLX8LR7PH6MlIfK8vzArZQe+f/prhsq75U7Xl6UafYOPfjdN/+5Z+s7Vy+Eut
-CHnNaYlAJ/Uqwa1D7KRTyGG299J5KmcYdkhtWyUB0SbFt1dpIxVbYYqt8Bst2a9c8SaQaanVDED1
-M4BDj5yjdipFtK+/fz6HP3bFzSreIMUWWMv5G/UPyw0RUmS40nZid4PxWJ//AgMBAAGjYzBhMB8G
-A1UdIwQYMBaAFFbn4VslQ4Dg9ozhcbyO5YAvxEjiMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
-BAQDAgEGMB0GA1UdDgQWBBRW5+FbJUOA4PaM4XG8juWAL8RI4jANBgkqhkiG9w0BAQUFAAOCAQEA
-g8ev6n9NCjw5sWi+e22JLumzCecYV42FmhfzdkJQEw/HkG8zrcVJYCtsSVgZ1OK+t7+rSbyUyKu+
-KGwWaODIl0YgoGhnYIg5IFHYaAERzqf2EQf27OysGh+yZm5WZ2B6dF7AbZc2rrUNXWZzwCUyRdhK
-BgePxLcHsU0GDeGl6/R1yrqc0L2z0zIkTO5+4nYES0lT2PLpVDP85XEfPRRclkvxOvIAu2y0+pZV
-CIgJwcyRGSmwIC3/yzikQOEXvnlhgP8HA4ZMTnsGnxGGjYnuJ8Tb4rwZjgvDwxPHLQNjO9Po5KIq
-woIIlBZU8O8fJ5AluA0OKBtHd0e9HKgl8ZS0Zg==
------END CERTIFICATE-----
-
Autoridad de Certificacion Firmaprofesional CIF A62634068
=========================================================
-----BEGIN CERTIFICATE-----
3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM
dcGWxZ0=
-----END CERTIFICATE-----
+
+TURKTRUST Certificate Services Provider Root 2007
+=================================================
+-----BEGIN CERTIFICATE-----
+MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF
+bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP
+MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg
+QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X
+DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl
+a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN
+BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp
+bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N
+YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv
+KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya
+KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT
+rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC
+AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s
+Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I
+aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO
+Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb
+BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK
+poRq0Tl9
+-----END CERTIFICATE-----
+
+D-TRUST Root Class 3 CA 2 2009
+==============================
+-----BEGIN CERTIFICATE-----
+MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe
+Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE
+LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD
+ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA
+BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv
+KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z
+p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC
+AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ
+4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y
+eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw
+MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G
+PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw
+OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm
+2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
+o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV
+dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph
+X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I=
+-----END CERTIFICATE-----
+
+D-TRUST Root Class 3 CA 2 EV 2009
+=================================
+-----BEGIN CERTIFICATE-----
+MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
+OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
+OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS
+egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh
+zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T
+7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60
+sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35
+11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv
+cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v
+ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El
+MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp
+b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh
+c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+
+PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
+nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX
+ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA
+NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv
+w9y4AyHqnxbxLFS1
+-----END CERTIFICATE-----
+
+PSCProcert
+==========
+-----BEGIN CERTIFICATE-----
+MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk
+ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ
+MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz
+dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl
+cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw
+IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw
+MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w
+DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD
+ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp
+Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC
+wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA
+3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh
+RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO
+EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2
+0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH
+0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU
+td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw
+Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp
+r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/
+AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz
+Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId
+xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp
+ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH
+EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h
+Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k
+ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG
+9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG
+MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG
+LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52
+ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy
+YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v
+Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o
+dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq
+T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN
+g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q
+uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1
+n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn
+FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo
+5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq
+3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5
+poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y
+eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km
+-----END CERTIFICATE-----
+
+China Internet Network Information Center EV Certificates Root
+==============================================================
+-----BEGIN CERTIFICATE-----
+MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV
+BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D
+aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg
+Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG
+A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM
+PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl
+cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y
+jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV
+98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H
+klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23
+KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC
+7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD
+glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5
+0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM
+7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws
+ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0
+5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8=
+-----END CERTIFICATE-----
+
+Swisscom Root CA 2
+==================
+-----BEGIN CERTIFICATE-----
+MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQG
+EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
+dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2
+MjUwNzM4MTRaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
+aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIIC
+IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvErjw0DzpPM
+LgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r0rk0X2s682Q2zsKwzxNo
+ysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJ
+wDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVPACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpH
+Wrumnf2U5NGKpV+GY3aFy6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1a
+SgJA/MTAtukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL6yxS
+NLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0uPoTXGiTOmekl9Ab
+mbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrALacywlKinh/LTSlDcX3KwFnUey7QY
+Ypqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velhk6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3
+qPyZ7iVNTA6z00yPhOgpD/0QVAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
+HQYDVR0hBBYwFDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O
+BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqhb97iEoHF8Twu
+MA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4RfbgZPnm3qKhyN2abGu2sEzsO
+v2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ
+82YqZh6NM4OKb3xuqFp1mrjX2lhIREeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLz
+o9v/tdhZsnPdTSpxsrpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcs
+a0vvaGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciATwoCqISxx
+OQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99nBjx8Oto0QuFmtEYE3saW
+mA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5Wt6NlUe07qxS/TFED6F+KBZvuim6c779o
++sjaC+NCydAXFJy3SuCvkychVSa1ZC+N8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TC
+rvJcwhbtkj6EPnNgiLx29CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX
+5OfNeOI5wSsSnqaeG8XmDtkx2Q==
+-----END CERTIFICATE-----
+
+Swisscom Root EV CA 2
+=====================
+-----BEGIN CERTIFICATE-----
+MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UE
+BhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdpdGFsIENlcnRpZmljYXRlIFNl
+cnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcN
+MzEwNjI1MDg0NTA4WjBnMQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsT
+HERpZ2l0YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYg
+Q0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7BxUglgRCgz
+o3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD1ycfMQ4jFrclyxy0uYAy
+Xhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPHoCE2G3pXKSinLr9xJZDzRINpUKTk4Rti
+GZQJo/PDvO/0vezbE53PnUgJUmfANykRHvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8Li
+qG12W0OfvrSdsyaGOx9/5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaH
+Za0zKcQvidm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHLOdAG
+alNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaCNYGu+HuB5ur+rPQa
+m3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f46Fq9mDU5zXNysRojddxyNMkM3Ox
+bPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCBUWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDi
+xzgHcgplwLa7JSnaFp6LNYth7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/
+BAQDAgGGMB0GA1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED
+MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWBbj2ITY1x0kbB
+bkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6xXCX5145v9Ydkn+0UjrgEjihL
+j6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98TPLr+flaYC/NUn81ETm484T4VvwYmneTwkLbU
+wp4wLh/vx3rEUMfqe9pQy3omywC0Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7
+XwgiG/W9mR4U9s70WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH
+59yLGn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm7JFe3VE/
+23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4Snr8PyQUQ3nqjsTzyP6Wq
+J3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VNvBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyA
+HmBR3NdUIR7KYndP+tiPsys6DXhyyWhBWkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/gi
+uMod89a2GQ+fYWVq6nTIfI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuW
+l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc=
+-----END CERTIFICATE-----
+
+CA Disig Root R1
+================
+-----BEGIN CERTIFICATE-----
+MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw
+EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
+ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx
+EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
+c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy
+3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8
+u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2
+m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk
+CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa
+YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6
+vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL
+LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX
+ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is
+XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ
+04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR
+xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B
+LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM
+CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb
+VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85
+YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS
+ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix
+lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N
+UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ
+a7+h89n07eLw4+1knj0vllJPgFOL
+-----END CERTIFICATE-----
+
+CA Disig Root R2
+================
+-----BEGIN CERTIFICATE-----
+MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw
+EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
+ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx
+EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
+c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC
+w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia
+xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7
+A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S
+GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV
+g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa
+5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE
+koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A
+Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i
+Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u
+Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM
+tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV
+sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je
+dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8
+1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx
+mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01
+utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0
+sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg
+UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV
+7+ZtsH8tZ/3zbBt1RqPlShfppNcL
+-----END CERTIFICATE-----
+
+ACCVRAIZ1
+=========
+-----BEGIN CERTIFICATE-----
+MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB
+SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1
+MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH
+UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM
+jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0
+RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD
+aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ
+0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG
+WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7
+8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR
+5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J
+9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK
+Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw
+Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu
+Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
+VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM
+Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA
+QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh
+AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA
+YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj
+AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA
+IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk
+aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0
+dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2
+MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI
+hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E
+R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN
+YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49
+nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ
+TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3
+sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
+I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg
+Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd
+3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p
+EfbRD0tVNEYqi4Y7
+-----END CERTIFICATE-----
+
+TWCA Global Root CA
+===================
+-----BEGIN CERTIFICATE-----
+MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT
+CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD
+QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK
+EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg
+Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C
+nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV
+r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR
+Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV
+tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W
+KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99
+sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p
+yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn
+kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI
+zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC
+AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g
+cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn
+LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M
+8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg
+/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg
+lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP
+A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m
+i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8
+EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3
+zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0=
+-----END CERTIFICATE-----
+
+TeliaSonera Root CA v1
+======================
+-----BEGIN CERTIFICATE-----
+MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE
+CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4
+MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW
+VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+
+6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA
+3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k
+B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn
+Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH
+oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3
+F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ
+oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7
+gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc
+TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB
+AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW
+DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm
+zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
+0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW
+pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV
+G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc
+c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT
+JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2
+qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6
+Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems
+WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
+-----END CERTIFICATE-----
+
+E-Tugra Certification Authority
+===============================
+-----BEGIN CERTIFICATE-----
+MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w
+DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls
+ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
+ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw
+NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx
+QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl
+cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD
+DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd
+hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K
+CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g
+ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ
+BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0
+E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz
+rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq
+jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
+rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5
+dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB
+/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG
+MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK
+kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO
+XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807
+VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo
+a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc
+dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV
+KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT
+Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0
+8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G
+C7TbO6Orb1wdtn7os4I07QZcJA==
+-----END CERTIFICATE-----
+
+T-TeleSec GlobalRoot Class 2
+============================
+-----BEGIN CERTIFICATE-----
+MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
+IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
+cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx
+MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
+dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
+ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ
+SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F
+vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970
+2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV
+WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy
+YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4
+r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf
+vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR
+3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
+9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg==
+-----END CERTIFICATE-----
+
+Atos TrustedRoot 2011
+=====================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU
+cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4
+MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG
+A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV
+hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr
+54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+
+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320
+HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR
+z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R
+l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ
+bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
+CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h
+k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh
+TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9
+61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G
+3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
+-----END CERTIFICATE-----
+++ /dev/null
-47961e7ef15667c93cd99be01b51f00a
namespace Guzzle\Http;
use Guzzle\Common\Exception\InvalidArgumentException;
-use Guzzle\Parser\ParserRegistry;
/**
* Parses and generates URLs based on URL parts. In favor of performance, URL parts are not validated.
* @param string $url Full URL used to create a Url object
*
* @return Url
+ * @throws InvalidArgumentException
*/
public static function factory($url)
{
static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null,
'user' => null, 'pass' => null, 'fragment' => null);
- $parts = parse_url($url) + $defaults;
+ if (false === ($parts = parse_url($url))) {
+ throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url);
+ }
+
+ $parts += $defaults;
// Convert the query string into a QueryString object
if ($parts['query'] || 0 !== strlen($parts['query'])) {
$parts['query'] = QueryString::fromString($parts['query']);
}
- return new self($parts['scheme'], $parts['host'], $parts['user'],
+ return new static($parts['scheme'], $parts['host'], $parts['user'],
$parts['pass'], $parts['port'], $parts['path'], $parts['query'],
$parts['fragment']);
}
*/
public function getParts()
{
+ $query = (string) $this->query;
+
return array(
'scheme' => $this->scheme,
'user' => $this->username,
'host' => $this->host,
'port' => $this->port,
'path' => $this->getPath(),
- 'query' => (string) $this->query ?: null,
+ 'query' => $query !== '' ? $query : null,
'fragment' => $this->fragment,
);
}
*/
public function setScheme($scheme)
{
+ if ($this->scheme == 'http' && $this->port == 80) {
+ $this->port = null;
+ } elseif ($this->scheme == 'https' && $this->port == 443) {
+ $this->port = null;
+ }
+
$this->scheme = $scheme;
return $this;
*/
public function setPath($path)
{
+ static $pathReplace = array(' ' => '%20', '?' => '%3F');
if (is_array($path)) {
- $this->path = '/' . implode('/', $path);
- } else {
- $this->path = (string) $path;
+ $path = '/' . implode('/', $path);
}
+ $this->path = strtr($path, $pathReplace);
+
return $this;
}
return $this;
}
- // Replace // and /./ with /
- $this->path = str_replace(array('/./', '//'), '/', $this->path);
-
- // Remove dot segments
- if (strpos($this->path, '..') !== false) {
-
- // Remove trailing relative paths if possible
- $segments = $this->getPathSegments();
- $last = end($segments);
- $trailingSlash = false;
- if ($last === '') {
- array_pop($segments);
- $trailingSlash = true;
+ $results = array();
+ $segments = $this->getPathSegments();
+ foreach ($segments as $segment) {
+ if ($segment == '..') {
+ array_pop($results);
+ } elseif ($segment != '.' && $segment != '') {
+ $results[] = $segment;
}
+ }
- while ($last == '..' || $last == '.') {
- if ($last == '..') {
- array_pop($segments);
- $last = array_pop($segments);
- }
- if ($last == '.' || $last == '') {
- $last = array_pop($segments);
- }
- }
+ // Combine the normalized parts and add the leading slash if needed
+ $this->path = ($this->path[0] == '/' ? '/' : '') . implode('/', $results);
- $this->path = implode('/', $segments);
- if ($trailingSlash) {
- $this->path .= '/';
- }
+ // Add the trailing slash if necessary
+ if ($this->path != '/' && end($segments) == '') {
+ $this->path .= '/';
}
return $this;
}
/**
- * Add a relative path to the currently set path
+ * Add a relative path to the currently set path.
*
* @param string $relativePath Relative path to add
*
*/
public function addPath($relativePath)
{
- if (!$relativePath || $relativePath == '/') {
- return $this;
- }
-
- // Add a leading slash if needed
- if ($relativePath[0] != '/') {
- $relativePath = '/' . $relativePath;
+ if ($relativePath != '/' && is_string($relativePath) && strlen($relativePath) > 0) {
+ // Add a leading slash if needed
+ if ($relativePath[0] != '/') {
+ $relativePath = '/' . $relativePath;
+ }
+ $this->setPath(str_replace('//', '/', $this->path . $relativePath));
}
- return $this->setPath(str_replace('//', '/', $this->getPath() . $relativePath));
+ return $this;
}
/**
/**
* Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4.
*
- * @param string $url Relative URL to combine with
- *
+ * @param string $url Relative URL to combine with
+ * @param bool $strictRfc3986 Set to true to use strict RFC 3986 compliance when merging paths. When first
+ * released, Guzzle used an incorrect algorithm for combining relative URL paths. In
+ * order to not break users, we introduced this flag to allow the merging of URLs based
+ * on strict RFC 3986 section 5.4.1. This means that "http://a.com/foo/baz" merged with
+ * "bar" would become "http://a.com/foo/bar". When this value is set to false, it would
+ * become "http://a.com/foo/baz/bar".
* @return Url
* @throws InvalidArgumentException
* @link http://tools.ietf.org/html/rfc3986#section-5.4
*/
- public function combine($url)
+ public function combine($url, $strictRfc3986 = false)
{
$url = self::factory($url);
$this->username = $url->getUsername();
$this->password = $url->getPassword();
$this->path = $url->getPath();
+ $this->query = $url->getQuery();
$this->fragment = $url->getFragment();
return $this;
}
if (!$path) {
if (count($query)) {
- $this->query = $query;
+ $this->addQuery($query, $strictRfc3986);
}
} else {
if ($path[0] == '/') {
$this->path = $path;
+ } elseif ($strictRfc3986) {
+ $this->path .= '/../' . $path;
} else {
$this->path .= '/' . $path;
}
$this->normalizePath();
- $this->query = $query;
+ $this->addQuery($query, $strictRfc3986);
}
$this->fragment = $url->getFragment();
return $this;
}
+
+ private function addQuery(QueryString $new, $strictRfc386)
+ {
+ if (!$strictRfc386) {
+ $new->merge($this->query);
+ }
+
+ $this->query = $new;
+ }
}
--- /dev/null
+{
+ "name": "guzzle/http",
+ "description": "HTTP libraries used by Guzzle",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["http client", "http", "client", "Guzzle", "curl"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/common": "self.version",
+ "guzzle/parser": "self.version",
+ "guzzle/stream": "self.version"
+ },
+ "suggest": {
+ "ext-curl": "*"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Http": "" }
+ },
+ "target-dir": "Guzzle/Http",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
--- /dev/null
+{
+ "name": "guzzle/inflection",
+ "description": "Guzzle inflection component",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["inflection", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Inflection": "" }
+ },
+ "target-dir": "Guzzle/Inflection",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
/**
* @param \Traversable $iterator Traversable iterator
* @param int $chunkSize Size to make each chunk
+ * @throws \InvalidArgumentException
*/
public function __construct(\Traversable $iterator, $chunkSize)
{
+ $chunkSize = (int) $chunkSize;
+ if ($chunkSize < 0 ) {
+ throw new \InvalidArgumentException("The chunk size must be equal or greater than zero; $chunkSize given");
+ }
+
parent::__construct($iterator);
$this->chunkSize = $chunkSize;
}
public function rewind()
{
+ parent::rewind();
$this->next();
}
public function next()
{
$this->chunk = array();
- $inner = $this->getInnerIterator();
- for ($i = 0; $i < $this->chunkSize && $inner->valid(); $i++) {
- $this->chunk[] = $inner->current();
- $inner->next();
+ for ($i = 0; $i < $this->chunkSize && parent::valid(); $i++) {
+ $this->chunk[] = parent::current();
+ parent::next();
}
}
public function valid()
{
- return !empty($this->chunk);
+ return (bool) $this->chunk;
}
}
protected $callback;
/**
- * @param \Traversable $iterator Traversable iterator
+ * @param \Iterator $iterator Traversable iterator
* @param array|\Closure $callback Callback used for filtering. Return true to keep or false to filter.
*
* @throws InvalidArgumentException if the callback if not callable
*/
- public function __construct(\Traversable $iterator, $callback)
+ public function __construct(\Iterator $iterator, $callback)
{
parent::__construct($iterator);
if (!is_callable($callback)) {
--- /dev/null
+Guzzle Iterator
+===============
+
+Provides useful Iterators and Iterator decorators
+
+- ChunkedIterator: Pulls out chunks from an inner iterator and yields the chunks as arrays
+- FilterIterator: Used when PHP 5.4's CallbackFilterIterator is not available
+- MapIterator: Maps values before yielding
+- MethodProxyIterator: Proxies missing method calls to the innermost iterator
+
+### Installing via Composer
+
+```bash
+# Install Composer
+curl -sS https://getcomposer.org/installer | php
+
+# Add Guzzle as a dependency
+php composer.phar require guzzle/iterator:~3.0
+```
+
+After installing, you need to require Composer's autoloader:
+
+```php
+require 'vendor/autoload.php';
+```
--- /dev/null
+{
+ "name": "guzzle/iterator",
+ "description": "Provides helpful iterators and iterator decorators",
+ "keywords": ["iterator", "guzzle"],
+ "homepage": "http://guzzlephp.org/",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/common": ">=2.8.0"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Iterator": "/" }
+ },
+ "target-dir": "Guzzle/Iterator",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
public function log($message, $priority = LOG_INFO, $extras = array())
{
- $this->log->addRecord(self::$mapping[$priority], $message);
+ $this->log->addRecord(self::$mapping[$priority], $message, $extras);
}
}
--- /dev/null
+{
+ "name": "guzzle/log",
+ "description": "Guzzle log adapter component",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["log", "adapter", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Log": "" }
+ },
+ "suggest": {
+ "guzzle/http": "self.version"
+ },
+ "target-dir": "Guzzle/Log",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
$data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array(
'cookies' => array(),
'data' => array(),
- 'path' => $path ?: '/',
+ 'path' => null,
'http_only' => false,
'discard' => false,
'domain' => $host
$data['expires'] = time() + (int) $data['max_age'];
}
+ // Check path attribute according RFC6265 http://tools.ietf.org/search/rfc6265#section-5.2.4
+ // "If the attribute-value is empty or if the first character of the
+ // attribute-value is not %x2F ("/"):
+ // Let cookie-path be the default-path.
+ // Otherwise:
+ // Let cookie-path be the attribute-value."
+ if (!$data['path'] || substr($data['path'], 0, 1) !== '/') {
+ $data['path'] = $this->getDefaultPath($path);
+ }
+
return $data;
}
+
+ /**
+ * Get default cookie path according to RFC 6265
+ * http://tools.ietf.org/search/rfc6265#section-5.1.4 Paths and Path-Match
+ *
+ * @param string $path Request uri-path
+ *
+ * @return string
+ */
+ protected function getDefaultPath($path) {
+ // "The user agent MUST use an algorithm equivalent to the following algorithm
+ // to compute the default-path of a cookie:"
+
+ // "2. If the uri-path is empty or if the first character of the uri-path is not
+ // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps.
+ if (empty($path) || substr($path, 0, 1) !== '/') {
+ return '/';
+ }
+
+ // "3. If the uri-path contains no more than one %x2F ("/") character, output
+ // %x2F ("/") and skip the remaining step."
+ if ($path === "/") {
+ return $path;
+ }
+
+ $rightSlashPos = strrpos($path, '/');
+ if ($rightSlashPos === 0) {
+ return "/";
+ }
+
+ // "4. Output the characters of the uri-path from the first character up to,
+ // but not including, the right-most %x2F ("/")."
+ return substr($path, 0, $rightSlashPos);
+
+ }
}
*/
class UriTemplate implements UriTemplateInterface
{
+ const DEFAULT_PATTERN = '/\{([^\}]+)\}/';
+
/** @var string URI template */
private $template;
private $variables;
/** @var string Regex used to parse expressions */
- private static $regex = '/\{([^\}]+)\}/';
+ private $regex = self::DEFAULT_PATTERN;
/** @var array Hash for quick operator lookups */
private static $operatorHash = array(
public function expand($template, array $variables)
{
+ if ($this->regex == self::DEFAULT_PATTERN && false === strpos($template, '{')) {
+ return $template;
+ }
+
$this->template = $template;
$this->variables = $variables;
- // Check to ensure that the preg_* function is needed
- if (false === strpos($this->template, '{')) {
- return $this->template;
- }
+ return preg_replace_callback($this->regex, array($this, 'expandMatch'), $this->template);
+ }
- return preg_replace_callback(self::$regex, array($this, 'expandMatch'), $this->template);
+ /**
+ * Set the regex patten used to expand URI templates
+ *
+ * @param string $regexPattern
+ */
+ public function setRegex($regexPattern)
+ {
+ $this->regex = $regexPattern;
}
/**
--- /dev/null
+{
+ "name": "guzzle/parser",
+ "homepage": "http://guzzlephp.org/",
+ "description": "Interchangeable parsers used by Guzzle",
+ "keywords": ["HTTP", "message", "cookie", "URL", "URI Template"],
+ "license": "MIT",
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Parser": "" }
+ },
+ "target-dir": "Guzzle/Parser",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
public function onCurlProgress(Event $event)
{
if ($event['handle'] &&
- ($event['downloaded'] || ($event['uploaded'] && $event['upload_size'] === $event['uploaded']))
+ ($event['downloaded'] || (isset($event['uploaded']) && $event['upload_size'] === $event['uploaded']))
) {
// Timeout after 1ms
curl_setopt($event['handle'], CURLOPT_TIMEOUT_MS, 1);
- // Even if the response is quick, tell curl not to download the body
- curl_setopt($event['handle'], CURLOPT_NOBODY, true);
+ // Even if the response is quick, tell curl not to download the body.
+ // - Note that we can only perform this shortcut if the request transmitted a body so as to ensure that the
+ // request method is not converted to a HEAD request before the request was sent via curl.
+ if ($event['uploaded']) {
+ curl_setopt($event['handle'], CURLOPT_NOBODY, true);
+ }
}
}
--- /dev/null
+{
+ "name": "guzzle/plugin-async",
+ "description": "Guzzle async request plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Async": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Async",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestInterface;
-use Guzzle\Http\Message\Response;
use Guzzle\Http\Curl\CurlMultiInterface;
+use Guzzle\Http\Exception\CurlException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
$this->dispatch(self::RETRY_EVENT, array(
'request' => $request,
'response' => $response,
- 'handle' => $exception ? $exception->getCurlHandle() : null,
+ 'handle' => ($exception && $exception instanceof CurlException) ? $exception->getCurlHandle() : null,
'retries' => $retries,
'delay' => $delay
));
{
/** @var array Default cURL errors to retry */
protected static $defaultErrorCodes = array(
- CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_CONNECT, CURLE_WRITE_ERROR, CURLE_READ_ERROR,
+ CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_CONNECT, CURLE_PARTIAL_FILE, CURLE_WRITE_ERROR, CURLE_READ_ERROR,
CURLE_OPERATION_TIMEOUTED, CURLE_SSL_CONNECT_ERROR, CURLE_HTTP_PORT_FAILED, CURLE_GOT_NOTHING,
CURLE_SEND_ERROR, CURLE_RECV_ERROR
);
--- /dev/null
+{
+ "name": "guzzle/plugin-backoff",
+ "description": "Guzzle backoff retry plugins",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version",
+ "guzzle/log": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Backoff": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Backoff",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
{
return $this->requestCallback
? call_user_func($this->requestCallback, $request)
- : parent::canCache($request);
+ : parent::canCacheRequest($request);
}
public function canCacheResponse(Response $response)
($resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate')));
// Use the strong ETag validator if available and the response contains no Cache-Control directive
- if (!$revalidate && !$reqCache && $response->hasHeader('ETag')) {
+ if (!$revalidate && !$resCache && $response->hasHeader('ETag')) {
$revalidate = true;
}
protected function createRevalidationRequest(RequestInterface $request, Response $response)
{
$revalidate = clone $request;
- $revalidate->removeHeader('Pragma')
- ->removeHeader('Cache-Control')
- ->setHeader('If-Modified-Since', $response->getLastModified() ?: $response->getDate());
+ $revalidate->removeHeader('Pragma')->removeHeader('Cache-Control');
+
+ if ($response->getLastModified()) {
+ $revalidate->setHeader('If-Modified-Since', $response->getLastModified());
+ }
if ($response->getEtag()) {
- $revalidate->setHeader('If-None-Match', '"' . $response->getEtag() . '"');
+ $revalidate->setHeader('If-None-Match', $response->getEtag());
}
// Remove any cache plugins that might be on the request to prevent infinite recursive revalidations
$dispatcher = $revalidate->getEventDispatcher();
foreach ($dispatcher->getListeners() as $eventName => $listeners) {
foreach ($listeners as $listener) {
- if ($listener[0] instanceof CachePlugin) {
+ if (is_array($listener) && $listener[0] instanceof CachePlugin) {
$dispatcher->removeListener($eventName, $listener);
}
}
--- /dev/null
+{
+ "name": "guzzle/plugin-cache",
+ "description": "Guzzle HTTP cache plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version",
+ "guzzle/cache": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Cache": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Cache",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
*/
public function matchesPath($path)
{
- return !$this->getPath() || 0 === stripos($path, $this->getPath());
+ // RFC6265 http://tools.ietf.org/search/rfc6265#section-5.1.4
+ // A request-path path-matches a given cookie-path if at least one of
+ // the following conditions holds:
+
+ // o The cookie-path and the request-path are identical.
+ if ($path == $this->getPath()) {
+ return true;
+ }
+
+ $pos = stripos($path, $this->getPath());
+ if ($pos === 0) {
+ // o The cookie-path is a prefix of the request-path, and the last
+ // character of the cookie-path is %x2F ("/").
+ if (substr($this->getPath(), -1, 1) === "/") {
+ return true;
+ }
+
+ // o The cookie-path is a prefix of the request-path, and the first
+ // character of the request-path that is not included in the cookie-
+ // path is a %x2F ("/") character.
+ if (substr($path, strlen($this->getPath()), 1) === "/") {
+ return true;
+ }
+ }
+
+ return false;
}
/**
*/
public function matchesDomain($domain)
{
- $cookieDomain = $this->getDomain();
+ // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3
+ $cookieDomain = ltrim($this->getDomain(), '.');
// Domain not set or exact match.
if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
return true;
}
- // . prefix match.
- if (strpos($cookieDomain, '.') === 0) {
- $realDomain = substr($cookieDomain, 1);
-
- // Root domains don't match except for .local.
- if (!substr_count($realDomain, '.') && strcasecmp($realDomain, 'local')) {
- return false;
- }
-
- if (substr($domain, -strlen($realDomain)) === $realDomain) {
- // Match exact or 1 deep subdomain.
- return !strcasecmp($domain, $realDomain) ||
- substr_count(substr($domain, 0, -strlen($realDomain)), '.') === 1;
- }
+ // Matching the subdomain according to RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.1.3
+ if (filter_var($domain, FILTER_VALIDATE_IP)) {
+ return false;
}
- return false;
+ return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/i', $domain);
}
/**
if ($this->strictMode) {
throw new InvalidCookieException($result);
} else {
+ $this->removeCookieIfEmpty($cookie);
return false;
}
}
return $cookies;
}
+
+ /**
+ * If a cookie already exists and the server asks to set it again with a null value, the
+ * cookie must be deleted.
+ *
+ * @param \Guzzle\Plugin\Cookie\Cookie $cookie
+ */
+ private function removeCookieIfEmpty(Cookie $cookie)
+ {
+ $cookieValue = $cookie->getValue();
+ if ($cookieValue === null || $cookieValue === '') {
+ $this->remove($cookie->getDomain(), $cookie->getPath(), $cookie->getName());
+ }
+ }
}
--- /dev/null
+{
+ "name": "guzzle/plugin-cookie",
+ "description": "Guzzle cookie plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Cookie": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Cookie",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
--- /dev/null
+{
+ "name": "guzzle/plugin-curlauth",
+ "description": "Guzzle cURL authorization plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "curl", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\CurlAuth": "" }
+ },
+ "target-dir": "Guzzle/Plugin/CurlAuth",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
$className = $error['class'];
$errorClassInterface = __NAMESPACE__ . '\\ErrorResponseExceptionInterface';
if (!class_exists($className)) {
- throw new ErrorResponseException("{$className} does not exist");;
- } elseif (!is_subclass_of($className, $errorClassInterface)) {
+ throw new ErrorResponseException("{$className} does not exist");
+ } elseif (!(in_array($errorClassInterface, class_implements($className)))) {
throw new ErrorResponseException("{$className} must implement {$errorClassInterface}");
}
throw $className::fromCommand($command, $response);
--- /dev/null
+{
+ "name": "guzzle/plugin-error-response",
+ "description": "Guzzle errorResponse plugin for creating error exceptions based on a service description",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/service": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\ErrorResponse": "" }
+ },
+ "target-dir": "Guzzle/Plugin/ErrorResponse",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
--- /dev/null
+{
+ "name": "guzzle/plugin-history",
+ "description": "Guzzle history plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\History": "" }
+ },
+ "target-dir": "Guzzle/Plugin/History",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
use Guzzle\Log\ClosureLogAdapter;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
-use Guzzle\Http\Message\Response;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
class LogPlugin implements EventSubscriberInterface
{
/** @var LogAdapterInterface Adapter responsible for writing log data */
- private $logAdapter;
+ protected $logAdapter;
/** @var MessageFormatter Formatter used to format messages before logging */
protected $formatter;
--- /dev/null
+{
+ "name": "guzzle/plugin-log",
+ "description": "Guzzle log plugin for over the wire logging",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "log", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version",
+ "guzzle/log": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Log": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Log",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
&& $command->getOperation()->hasParam($this->contentMd5Param)) {
// Check if an MD5 checksum value should be passed along to the request
if ($command[$this->contentMd5Param] === true) {
- $request->setHeader('Content-MD5', $request->getBody()->getContentMd5(true, true));
+ if (false !== ($md5 = $request->getBody()->getContentMd5(true, true))) {
+ $request->setHeader('Content-MD5', $md5);
+ }
}
}
use Guzzle\Common\Event;
use Guzzle\Common\Exception\UnexpectedValueException;
-use Guzzle\Http\Message\Response;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
--- /dev/null
+{
+ "name": "guzzle/plugin-md5",
+ "description": "Guzzle MD5 plugins",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Md5": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Md5",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
* Called when a request is about to be sent
*
* @param Event $event
+ * @throws \OutOfBoundsException When queue is empty
*/
public function onRequestBeforeSend(Event $event)
{
- if ($this->queue) {
- $request = $event['request'];
- $this->received[] = $request;
- // Detach the filter from the client so it's a one-time use
- if ($this->temporary && count($this->queue) == 1 && $request->getClient()) {
- $request->getClient()->getEventDispatcher()->removeSubscriber($this);
- }
- $this->dequeue($request);
+ if (!$this->queue) {
+ throw new \OutOfBoundsException('Mock queue is empty');
+ }
+
+ $request = $event['request'];
+ $this->received[] = $request;
+ // Detach the filter from the client so it's a one-time use
+ if ($this->temporary && count($this->queue) == 1 && $request->getClient()) {
+ $request->getClient()->getEventDispatcher()->removeSubscriber($this);
}
+ $this->dequeue($request);
}
}
--- /dev/null
+{
+ "name": "guzzle/plugin-mock",
+ "description": "Guzzle Mock plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["mock", "plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Mock": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Mock",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
*/
class OauthPlugin implements EventSubscriberInterface
{
+ /**
+ * Consumer request method constants. See http://oauth.net/core/1.0/#consumer_req_param
+ */
+ const REQUEST_METHOD_HEADER = 'header';
+ const REQUEST_METHOD_QUERY = 'query';
+
/** @var Collection Configuration settings */
protected $config;
* Create a new OAuth 1.0 plugin
*
* @param array $config Configuration array containing these parameters:
+ * - string 'request_method' Consumer request method. Use the class constants.
* - string 'callback' OAuth callback
* - string 'consumer_key' Consumer key
* - string 'consumer_secret' Consumer secret
{
$this->config = Collection::fromConfig($config, array(
'version' => '1.0',
+ 'request_method' => self::REQUEST_METHOD_HEADER,
'consumer_key' => 'anonymous',
'consumer_secret' => 'anonymous',
'signature_method' => 'HMAC-SHA1',
*
* @param Event $event Event received
* @return array
+ * @throws \InvalidArgumentException
*/
public function onRequestBeforeSend(Event $event)
{
$timestamp = $this->getTimestamp($event);
$request = $event['request'];
$nonce = $this->generateNonce($request);
+ $authorizationParams = $this->getOauthParams($timestamp, $nonce);
+ $authorizationParams['oauth_signature'] = $this->getSignature($request, $timestamp, $nonce);
- $authorizationParams = array(
- 'oauth_callback' => $this->config['callback'],
- 'oauth_consumer_key' => $this->config['consumer_key'],
- 'oauth_nonce' => $nonce,
- 'oauth_signature' => $this->getSignature($request, $timestamp, $nonce),
- 'oauth_signature_method' => $this->config['signature_method'],
- 'oauth_timestamp' => $timestamp,
- 'oauth_token' => $this->config['token'],
- 'oauth_verifier' => $this->config['verifier'],
- 'oauth_version' => $this->config['version'],
- );
-
- $request->setHeader(
- 'Authorization',
- $this->buildAuthorizationHeader($authorizationParams)
- );
+ switch ($this->config['request_method']) {
+ case self::REQUEST_METHOD_HEADER:
+ $request->setHeader(
+ 'Authorization',
+ $this->buildAuthorizationHeader($authorizationParams)
+ );
+ break;
+ case self::REQUEST_METHOD_QUERY:
+ foreach ($authorizationParams as $key => $value) {
+ $request->getQuery()->set($key, $value);
+ }
+ break;
+ default:
+ throw new \InvalidArgumentException(sprintf(
+ 'Invalid consumer method "%s"',
+ $this->config['request_method']
+ ));
+ }
return $authorizationParams;
}
$params = $this->prepareParameters($params);
// Build signing string from combined params
- $parameterString = new QueryString($params);
+ $parameterString = clone $request->getQuery();
+ $parameterString->replace($params);
$url = Url::factory($request->getUrl())->setQuery('')->setFragment(null);
}
/**
- * Parameters sorted and filtered in order to properly sign a request
+ * Get the oauth parameters as named by the oauth spec
*
- * @param RequestInterface $request Request to generate a signature for
- * @param integer $timestamp Timestamp to use for nonce
- * @param string $nonce
- *
- * @return array
+ * @param $timestamp
+ * @param $nonce
+ * @return Collection
*/
- public function getParamsToSign(RequestInterface $request, $timestamp, $nonce)
+ protected function getOauthParams($timestamp, $nonce)
{
$params = new Collection(array(
- 'oauth_callback' => $this->config['callback'],
'oauth_consumer_key' => $this->config['consumer_key'],
'oauth_nonce' => $nonce,
'oauth_signature_method' => $this->config['signature_method'],
'oauth_timestamp' => $timestamp,
- 'oauth_token' => $this->config['token'],
- 'oauth_verifier' => $this->config['verifier'],
- 'oauth_version' => $this->config['version']
));
+ // Optional parameters should not be set if they have not been set in the config as
+ // the parameter may be considered invalid by the Oauth service.
+ $optionalParams = array(
+ 'callback' => 'oauth_callback',
+ 'token' => 'oauth_token',
+ 'verifier' => 'oauth_verifier',
+ 'version' => 'oauth_version'
+ );
+
+ foreach ($optionalParams as $optionName => $oauthName) {
+ if (isset($this->config[$optionName]) == true) {
+ $params[$oauthName] = $this->config[$optionName];
+ }
+ }
+
+ return $params;
+ }
+
+ /**
+ * Get all of the parameters required to sign a request including:
+ * * The oauth params
+ * * The request GET params
+ * * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded)
+ *
+ * @param RequestInterface $request Request to generate a signature for
+ * @param integer $timestamp Timestamp to use for nonce
+ * @param string $nonce
+ *
+ * @return array
+ */
+ public function getParamsToSign(RequestInterface $request, $timestamp, $nonce)
+ {
+ $params = $this->getOauthParams($timestamp, $nonce);
+
// Add query string parameters
$params->merge($request->getQuery());
// Sort params
$params = $params->toArray();
- ksort($params);
+ uksort($params, 'strcmp');
return $params;
}
--- /dev/null
+{
+ "name": "guzzle/plugin-oauth",
+ "description": "Guzzle OAuth plugin",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["oauth", "plugin", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin\\Oauth": "" }
+ },
+ "target-dir": "Guzzle/Plugin/Oauth",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
--- /dev/null
+{
+ "name": "guzzle/plugin",
+ "description": "Guzzle plugin component containing all Guzzle HTTP plugins",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["http", "client", "plugin", "extension", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/http": "self.version"
+ },
+ "suggest": {
+ "guzzle/cache": "self.version",
+ "guzzle/log": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Plugin": "" }
+ },
+ "target-dir": "Guzzle/Plugin",
+ "replace": {
+ "guzzle/plugin-async": "self.version",
+ "guzzle/plugin-backoff": "self.version",
+ "guzzle/plugin-cache": "self.version",
+ "guzzle/plugin-cookie": "self.version",
+ "guzzle/plugin-curlauth": "self.version",
+ "guzzle/plugin-error-response": "self.version",
+ "guzzle/plugin-history": "self.version",
+ "guzzle/plugin-log": "self.version",
+ "guzzle/plugin-md5": "self.version",
+ "guzzle/plugin-mock": "self.version",
+ "guzzle/plugin-oauth": "self.version"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
'command.before_prepare',
'command.after_prepare',
'command.before_send',
- 'command.after_send'
+ 'command.after_send',
+ 'command.parse_response'
));
}
{
$this->serviceDescription = $service;
+ if ($this->getCommandFactory() && $this->getCommandFactory() instanceof CompositeFactory) {
+ $this->commandFactory->add(new Command\Factory\ServiceDescriptionFactory($service));
+ }
+
// If a baseUrl was set on the description, then update the client
if ($baseUrl = $service->getBaseUrl()) {
$this->setBaseUrl($baseUrl);
use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
-use Guzzle\Http\Message\Response;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Curl\CurlHandle;
use Guzzle\Service\Client;
return $this->validator;
}
+
+ /**
+ * Get array of any validation errors
+ * If no validator has been set then return false
+ */
+ public function getValidationErrors()
+ {
+ if (!$this->validator) {
+ return false;
+ }
+
+ return $this->validator->getErrors();
+ }
}
--- /dev/null
+<?php
+
+namespace Guzzle\Service\Command;
+
+use Guzzle\Common\Event;
+
+/**
+ * Event class emitted with the operation.parse_class event
+ */
+class CreateResponseClassEvent extends Event
+{
+ /**
+ * Set the result of the object creation
+ *
+ * @param mixed $result Result value to set
+ */
+ public function setResult($result)
+ {
+ $this['result'] = $result;
+ $this->stopPropagation();
+ }
+
+ /**
+ * Get the created object
+ *
+ * @return mixed
+ */
+ public function getResult()
+ {
+ return $this['result'];
+ }
+}
namespace Guzzle\Service\Command;
use Guzzle\Http\Message\RequestInterface;
-use Guzzle\Http\Url;
use Guzzle\Service\Command\LocationVisitor\Request\RequestVisitorInterface;
use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight;
use Guzzle\Service\Description\OperationInterface;
if ($result->getBody()) {
if (stripos($contentType, 'json') !== false) {
$result = $result->json();
- } if (stripos($contentType, 'xml') !== false) {
+ } elseif (stripos($contentType, 'xml') !== false) {
$result = $result->xml();
}
}
public function after(CommandInterface $command, RequestInterface $request)
{
if (isset($this->data[$command])) {
- $request->setBody(json_encode($this->data[$command]));
- unset($this->data[$command]);
// Don't overwrite the Content-Type if one is set
if ($this->jsonContentType && !$request->hasHeader('Content-Type')) {
$request->setHeader('Content-Type', $this->jsonContentType);
}
+
+ $request->setBody(json_encode($this->data[$command]));
+ unset($this->data[$command]);
}
}
}
? $this->data[$command]
: $this->createRootElement($param->getParent());
$this->addXml($xml, $param, $value);
+
$this->data[$command] = $xml;
}
// If data was found that needs to be serialized, then do so
if (isset($this->data[$command])) {
- $xml = $this->data[$command]->asXML();
+ $xml = $this->finishDocument($this->data[$command]);
unset($this->data[$command]);
} else {
// Check if XML should always be sent for the command
$operation = $command->getOperation();
if ($operation->getData('xmlAllowEmpty')) {
- $xml = $this->createRootElement($operation)->asXML();
+ $xmlWriter = $this->createRootElement($operation);
+ $xml = $this->finishDocument($xmlWriter);
}
}
if ($xml) {
- $request->setBody($xml);
// Don't overwrite the Content-Type if one is set
if ($this->contentType && !$request->hasHeader('Content-Type')) {
$request->setHeader('Content-Type', $this->contentType);
}
+ $request->setBody($xml);
}
}
*
* @param Operation $operation Operation object
*
- * @return \SimpleXMLElement
+ * @return \XMLWriter
*/
protected function createRootElement(Operation $operation)
{
static $defaultRoot = array('name' => 'Request');
// If no root element was specified, then just wrap the XML in 'Request'
$root = $operation->getData('xmlRoot') ?: $defaultRoot;
-
// Allow the XML declaration to be customized with xmlEncoding
- $declaration = '<?xml version="1.0"';
- if ($encoding = $operation->getData('xmlEncoding')) {
- $declaration .= ' encoding="' . $encoding . '"';
- }
- $declaration .= "?>";
+ $encoding = $operation->getData('xmlEncoding');
+
+ $xmlWriter = $this->startDocument($encoding);
+ $xmlWriter->startElement($root['name']);
// Create the wrapping element with no namespaces if no namespaces were present
- if (empty($root['namespaces'])) {
- return new \SimpleXMLElement("{$declaration}\n<{$root['name']}/>");
- } else {
+ if (!empty($root['namespaces'])) {
// Create the wrapping element with an array of one or more namespaces
- $xml = "{$declaration}\n<{$root['name']} ";
foreach ((array) $root['namespaces'] as $prefix => $uri) {
- $xml .= is_numeric($prefix) ? "xmlns=\"{$uri}\" " : "xmlns:{$prefix}=\"{$uri}\" ";
+ $nsLabel = 'xmlns';
+ if (!is_numeric($prefix)) {
+ $nsLabel .= ':'.$prefix;
+ }
+ $xmlWriter->writeAttribute($nsLabel, $uri);
}
- return new \SimpleXMLElement($xml . "/>");
}
+ return $xmlWriter;
}
/**
* Recursively build the XML body
*
- * @param \SimpleXMLElement $xml XML to modify
- * @param Parameter $param API Parameter
- * @param mixed $value Value to add
+ * @param \XMLWriter $xmlWriter XML to modify
+ * @param Parameter $param API Parameter
+ * @param mixed $value Value to add
*/
- protected function addXml(\SimpleXMLElement $xml, Parameter $param, $value)
+ protected function addXml(\XMLWriter $xmlWriter, Parameter $param, $value)
{
if ($value === null) {
return;
$value = $param->filter($value);
$type = $param->getType();
+ $name = $param->getWireName();
+ $prefix = null;
+ $namespace = $param->getData('xmlNamespace');
+ if (false !== strpos($name, ':')) {
+ list($prefix, $name) = explode(':', $name, 2);
+ }
if ($type == 'object' || $type == 'array') {
- $ele = $param->getData('xmlFlattened') ? $xml : $xml->addChild($param->getWireName());
+ if (!$param->getData('xmlFlattened')) {
+ $xmlWriter->startElementNS(null, $name, $namespace);
+ }
if ($param->getType() == 'array') {
- $this->addXmlArray($ele, $param, $value, $param->getData('xmlNamespace'));
+ $this->addXmlArray($xmlWriter, $param, $value);
} elseif ($param->getType() == 'object') {
- $this->addXmlObject($ele, $param, $value);
+ $this->addXmlObject($xmlWriter, $param, $value);
}
- } elseif ($param->getData('xmlAttribute')) {
- $xml->addAttribute($param->getWireName(), $value, $param->getData('xmlNamespace'));
+ if (!$param->getData('xmlFlattened')) {
+ $xmlWriter->endElement();
+ }
+ return;
+ }
+ if ($param->getData('xmlAttribute')) {
+ $this->writeAttribute($xmlWriter, $prefix, $name, $namespace, $value);
} else {
- $xml->addChild($param->getWireName(), $value, $param->getData('xmlNamespace'));
+ $this->writeElement($xmlWriter, $prefix, $name, $namespace, $value);
}
}
+ /**
+ * Write an attribute with namespace if used
+ *
+ * @param \XMLWriter $xmlWriter XMLWriter instance
+ * @param string $prefix Namespace prefix if any
+ * @param string $name Attribute name
+ * @param string $namespace The uri of the namespace
+ * @param string $value The attribute content
+ */
+ protected function writeAttribute($xmlWriter, $prefix, $name, $namespace, $value)
+ {
+ if (empty($namespace)) {
+ $xmlWriter->writeAttribute($name, $value);
+ } else {
+ $xmlWriter->writeAttributeNS($prefix, $name, $namespace, $value);
+ }
+ }
+
+ /**
+ * Write an element with namespace if used
+ *
+ * @param \XMLWriter $xmlWriter XML writer resource
+ * @param string $prefix Namespace prefix if any
+ * @param string $name Element name
+ * @param string $namespace The uri of the namespace
+ * @param string $value The element content
+ */
+ protected function writeElement(\XMLWriter $xmlWriter, $prefix, $name, $namespace, $value)
+ {
+ $xmlWriter->startElementNS($prefix, $name, $namespace);
+ if (strpbrk($value, '<>&')) {
+ $xmlWriter->writeCData($value);
+ } else {
+ $xmlWriter->writeRaw($value);
+ }
+ $xmlWriter->endElement();
+ }
+
+ /**
+ * Create a new xml writer and start a document
+ *
+ * @param string $encoding document encoding
+ *
+ * @return \XMLWriter the writer resource
+ */
+ protected function startDocument($encoding)
+ {
+ $xmlWriter = new \XMLWriter();
+ $xmlWriter->openMemory();
+ $xmlWriter->startDocument('1.0', $encoding);
+
+ return $xmlWriter;
+ }
+
+ /**
+ * End the document and return the output
+ *
+ * @param \XMLWriter $xmlWriter
+ *
+ * @return \string the writer resource
+ */
+ protected function finishDocument($xmlWriter)
+ {
+ $xmlWriter->endDocument();
+
+ return $xmlWriter->outputMemory();
+ }
+
/**
* Add an array to the XML
*/
- protected function addXmlArray(\SimpleXMLElement $xml, Parameter $param, &$value)
+ protected function addXmlArray(\XMLWriter $xmlWriter, Parameter $param, &$value)
{
if ($items = $param->getItems()) {
foreach ($value as $v) {
- $this->addXml($xml, $items, $v);
+ $this->addXml($xmlWriter, $items, $v);
}
}
}
/**
* Add an object to the XML
*/
- protected function addXmlObject(\SimpleXMLElement $xml, Parameter $param, &$value)
+ protected function addXmlObject(\XMLWriter $xmlWriter, Parameter $param, &$value)
{
+ $noAttributes = array();
+ // add values which have attributes
foreach ($value as $name => $v) {
if ($property = $param->getProperty($name)) {
- $this->addXml($xml, $property, $v);
+ if ($property->getData('xmlAttribute')) {
+ $this->addXml($xmlWriter, $property, $v);
+ } else {
+ $noAttributes[] = array('value' => $v, 'property' => $property);
+ }
}
}
+ // now add values with no attributes
+ foreach ($noAttributes as $element) {
+ $this->addXml($xmlWriter, $element['property'], $element['value']);
+ }
}
}
}
} elseif ($type == 'object' && !isset($value[0])) {
// On the above line, we ensure that the array is associative and not numerically indexed
+ $knownProperties = array();
if ($properties = $param->getProperties()) {
foreach ($properties as $property) {
$name = $property->getName();
$key = $property->getWireName();
+ $knownProperties[$name] = 1;
if (isset($value[$key])) {
$this->recursiveProcess($property, $value[$key]);
if ($key != $name) {
}
}
}
+
+ // Remove any unknown and potentially unsafe properties
+ if ($param->getAdditionalProperties() === false) {
+ $value = array_intersect_key($value, $knownProperties);
+ } elseif (($additional = $param->getAdditionalProperties()) !== true) {
+ // Validate and filter additional properties
+ foreach ($value as &$v) {
+ $this->recursiveProcess($additional, $v);
+ }
+ }
}
}
$this->processObject($param, $value);
} elseif ($type == 'array') {
$this->processArray($param, $value);
+ } elseif ($type == 'string' && gettype($value) == 'array') {
+ $value = '';
}
if ($value !== null) {
{
// Ensure that the array is associative and not numerically indexed
if (!isset($value[0]) && ($properties = $param->getProperties())) {
+ $knownProperties = array();
foreach ($properties as $property) {
$name = $property->getName();
$sentAs = $property->getWireName();
+ $knownProperties[$name] = 1;
if ($property->getData('xmlAttribute')) {
$this->processXmlAttribute($property, $value);
} elseif (isset($value[$sentAs])) {
}
}
}
+
+ // Remove any unknown and potentially unsafe properties
+ if ($param->getAdditionalProperties() === false) {
+ $value = array_intersect_key($value, $knownProperties);
+ }
}
}
/** @var self */
protected static $instance;
+ /** @var bool */
+ private $schemaInModels;
+
/**
* @return self
* @codeCoverageIgnore
}
/**
- * @param VisitorFlyweight $factory Factory to use when creating visitors
+ * @param VisitorFlyweight $factory Factory to use when creating visitors
+ * @param bool $schemaInModels Set to true to inject schemas into models
*/
- public function __construct(VisitorFlyweight $factory)
+ public function __construct(VisitorFlyweight $factory, $schemaInModels = false)
{
$this->factory = $factory;
+ $this->schemaInModels = $schemaInModels;
}
/**
if ($type == OperationInterface::TYPE_MODEL) {
$model = $operation->getServiceDescription()->getModel($operation->getResponseClass());
} elseif ($type == OperationInterface::TYPE_CLASS) {
- $responseClassInterface = __NAMESPACE__ . '\ResponseClassInterface';
- $className = $operation->getResponseClass();
- if (!class_exists($className)) {
- throw new ResponseClassException("{$className} does not exist");
- } elseif (!method_exists($className, 'fromCommand')) {
- throw new ResponseClassException("{$className} must implement {$responseClassInterface}");
- }
- return $className::fromCommand($command);
+ return $this->parseClass($command);
}
if (!$model) {
return parent::handleParsing($command, $response, $contentType);
} elseif ($command[AbstractCommand::RESPONSE_PROCESSING] != AbstractCommand::TYPE_MODEL) {
// Returns a model with no visiting if the command response processing is not model
- return new Model(parent::handleParsing($command, $response, $contentType), $model);
+ return new Model(parent::handleParsing($command, $response, $contentType));
} else {
- return new Model($this->visitResult($model, $command, $response), $model);
+ // Only inject the schema into the model if "schemaInModel" is true
+ return new Model($this->visitResult($model, $command, $response), $this->schemaInModels ? $model : null);
}
}
+ /**
+ * Parse a class object
+ *
+ * @param CommandInterface $command Command to parse into an object
+ *
+ * @return mixed
+ * @throws ResponseClassException
+ */
+ protected function parseClass(CommandInterface $command)
+ {
+ // Emit the operation.parse_class event. If a listener injects a 'result' property, then that will be the result
+ $event = new CreateResponseClassEvent(array('command' => $command));
+ $command->getClient()->getEventDispatcher()->dispatch('command.parse_response', $event);
+ if ($result = $event->getResult()) {
+ return $result;
+ }
+
+ $className = $command->getOperation()->getResponseClass();
+ if (!method_exists($className, 'fromCommand')) {
+ throw new ResponseClassException("{$className} must exist and implement a static fromCommand() method");
+ }
+
+ return $className::fromCommand($command);
+ }
+
/**
* Perform transformations on the result array
*
*/
protected function visitResult(Parameter $model, CommandInterface $command, Response $response)
{
- $foundVisitors = $result = array();
+ $foundVisitors = $result = $knownProps = array();
$props = $model->getProperties();
foreach ($props as $schema) {
}
// Visit additional properties when it is an actual schema
- if ($additional = $model->getAdditionalProperties()) {
- if ($additional instanceof Parameter) {
- // Only visit when a location is specified
- if ($location = $additional->getLocation()) {
- if (!isset($foundVisitors[$location])) {
- $foundVisitors[$location] = $this->factory->getResponseVisitor($location);
- $foundVisitors[$location]->before($command, $result);
- }
- // Only traverse if an array was parsed from the before() visitors
- if (is_array($result)) {
- // Find each additional property
- foreach (array_keys($result) as $key) {
- // Check if the model actually knows this property. If so, then it is not additional
- if (!$model->getProperty($key)) {
- // Set the name to the key so that we can parse it with each visitor
- $additional->setName($key);
- $foundVisitors[$location]->visit($command, $response, $additional, $result);
- }
- }
- // Reset the additionalProperties name to null
- $additional->setName(null);
- }
- }
- }
+ if (($additional = $model->getAdditionalProperties()) instanceof Parameter) {
+ $this->visitAdditionalProperties($model, $command, $response, $additional, $result, $foundVisitors);
}
// Apply the parameter value with the location visitor
foreach ($props as $schema) {
+ $knownProps[$schema->getName()] = 1;
if ($location = $schema->getLocation()) {
$foundVisitors[$location]->visit($command, $response, $schema, $result);
}
}
+ // Remove any unknown and potentially unsafe top-level properties
+ if ($additional === false) {
+ $result = array_intersect_key($result, $knownProps);
+ }
+
// Call the after() method of each found visitor
foreach ($foundVisitors as $visitor) {
$visitor->after($command);
return $result;
}
+
+ protected function visitAdditionalProperties(
+ Parameter $model,
+ CommandInterface $command,
+ Response $response,
+ Parameter $additional,
+ &$result,
+ array &$foundVisitors
+ ) {
+ // Only visit when a location is specified
+ if ($location = $additional->getLocation()) {
+ if (!isset($foundVisitors[$location])) {
+ $foundVisitors[$location] = $this->factory->getResponseVisitor($location);
+ $foundVisitors[$location]->before($command, $result);
+ }
+ // Only traverse if an array was parsed from the before() visitors
+ if (is_array($result)) {
+ // Find each additional property
+ foreach (array_keys($result) as $key) {
+ // Check if the model actually knows this property. If so, then it is not additional
+ if (!$model->getProperty($key)) {
+ // Set the name to the key so that we can parse it with each visitor
+ $additional->setName($key);
+ $foundVisitors[$location]->visit($command, $response, $additional, $result);
+ }
+ }
+ // Reset the additionalProperties name to null
+ $additional->setName(null);
+ }
+ }
+ }
}
/**
* Get the additionalParameters of the operation
*
- * @return Paramter|null
+ * @return Parameter|null
*/
public function getAdditionalParameters()
{
*/
protected function inferResponseType()
{
- if (!$this->responseClass || $this->responseClass == 'array' || $this->responseClass == 'string'
- || $this->responseClass == 'boolean' || $this->responseClass == 'integer'
- ) {
+ static $primitives = array('array' => 1, 'boolean' => 1, 'string' => 1, 'integer' => 1, '' => 1);
+ if (isset($primitives[$this->responseClass])) {
$this->responseType = self::TYPE_PRIMITIVE;
} elseif ($this->description && $this->description->hasModel($this->responseClass)) {
$this->responseType = self::TYPE_MODEL;
- } elseif (strpos($this->responseClass, '\\') !== false) {
- $this->responseType = self::TYPE_CLASS;
} else {
- $this->responseType = self::TYPE_PRIMITIVE;
+ $this->responseType = self::TYPE_CLASS;
}
}
}
if ($description) {
if (isset($data['$ref'])) {
if ($model = $description->getModel($data['$ref'])) {
- // The name of the original parameter should override the ref name if one is available
- $name = empty($data['name']) ? null : $data['name'];
- $data = $model->toArray();
- if ($name) {
- $data['name'] = $name;
- }
+ $data = $model->toArray() + $data;
}
} elseif (isset($data['extends'])) {
// If this parameter extends from another parameter then start with the actual data
*/
public function toArray()
{
- $result = array();
- $checks = array('required', 'description', 'static', 'type', 'format', 'instanceOf', 'location', 'sentAs',
+ static $checks = array('required', 'description', 'static', 'type', 'format', 'instanceOf', 'location', 'sentAs',
'pattern', 'minimum', 'maximum', 'minItems', 'maxItems', 'minLength', 'maxLength', 'data', 'enum',
'filters');
+ $result = array();
+
// Anything that is in the `Items` attribute of an array *must* include it's name if available
if ($this->parent instanceof self && $this->parent->getType() == 'array' && isset($this->name)) {
$result['name'] = $this->name;
*/
public function getValue($value)
{
- return $this->static || ($this->default !== null && !$value && ($this->type != 'boolean' || $value !== false))
- ? $this->default
- : $value;
+ if ($this->static || ($this->default !== null && $value === null)) {
+ return $this->default;
+ }
+
+ return $value;
}
/**
*/
public static function formatTimestamp($value)
{
- return self::dateFormatter($value, 'U');
+ return (int) self::dateFormatter($value, 'U');
}
/**
$current = null;
$this->recursiveProcess($property, $current, $path, $depth + 1);
// Only set the value if it was populated with something
- if ($current) {
+ if (null !== $current) {
$value[$name] = $current;
}
}
}
} else {
// if additionalProperties is set to false and there are additionalProperties in the values, then fail
- $keys = array_keys($value);
- $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, reset($keys));
+ foreach ($diff as $prop) {
+ $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, $prop);
+ }
}
}
}
public static function fromMultiTransferException(MultiTransferException $e)
{
$ce = new self($e->getMessage(), $e->getCode(), $e->getPrevious());
+ $ce->setSuccessfulRequests($e->getSuccessfulRequests());
- return $ce->setExceptions($e->getIterator()->getArrayCopy())
- ->setSuccessfulRequests($e->getSuccessfulRequests())
- ->setFailedRequests($e->getFailedRequests());
+ $alreadyAddedExceptions = array();
+ foreach ($e->getFailedRequests() as $request) {
+ if ($re = $e->getExceptionForFailedRequest($request)) {
+ $alreadyAddedExceptions[] = $re;
+ $ce->addFailedRequestWithException($request, $re);
+ } else {
+ $ce->addFailedRequest($request);
+ }
+ }
+
+ // Add any exceptions that did not map to a request
+ if (count($alreadyAddedExceptions) < count($e)) {
+ foreach ($e as $ex) {
+ if (!in_array($ex, $alreadyAddedExceptions)) {
+ $ce->add($ex);
+ }
+ }
+ }
+
+ return $ce;
}
/**
{
return $this->failedCommands;
}
+
+ /**
+ * Get the Exception that caused the given $command to fail
+ *
+ * @param CommandInterface $command Failed command
+ *
+ * @return \Exception|null
+ */
+ public function getExceptionForFailedCommand(CommandInterface $command)
+ {
+ return $this->getExceptionForFailedRequest($command->getRequest());
+ }
}
public function __construct(array $data = array(), Parameter $structure = null)
{
$this->data = $data;
- $this->structure = $structure ?: new Parameter();
+ $this->structure = $structure;
}
/**
*/
public function getStructure()
{
- return $this->structure;
+ return $this->structure ?: new Parameter();
}
/**
*/
public function __toString()
{
- $output = 'Debug output of ' . ($this->structure->getName() ?: ' the model');
+ $output = 'Debug output of ';
+ if ($this->structure) {
+ $output .= $this->structure->getName() . ' ';
+ }
+ $output .= 'model';
$output = str_repeat('=', strlen($output)) . "\n" . $output . "\n" . str_repeat('=', strlen($output)) . "\n\n";
$output .= "Model data\n-----------\n\n";
$output .= "This data can be retrieved from the model object using the get() method of the model "
. "(e.g. \$model->get(\$key)) or accessing the model like an associative array (e.g. \$model['key']).\n\n";
$lines = array_slice(explode("\n", trim(print_r($this->toArray(), true))), 2, -1);
- $output .= implode("\n", $lines) . "\n\n";
- $output .= "Model structure\n---------------\n\n";
- $output .= "The following JSON document defines how the model was parsed from an HTTP response into the "
- . "associative array strucure you see above.\n\n";
- $output .= ' ' . json_encode($this->structure->toArray()) . "\n\n";
+ $output .= implode("\n", $lines);
- return $output;
+ if ($this->structure) {
+ $output .= "\n\nModel structure\n---------------\n\n";
+ $output .= "The following JSON document defines how the model was parsed from an HTTP response into the "
+ . "associative array structure you see above.\n\n";
+ $output .= ' ' . json_encode($this->structure->toArray()) . "\n\n";
+ }
+
+ return $output . "\n";
}
}
--- /dev/null
+{
+ "name": "guzzle/service",
+ "description": "Guzzle service component for abstracting RESTful web services",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["web service", "webservice", "REST", "guzzle"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.2",
+ "guzzle/cache": "self.version",
+ "guzzle/http": "self.version",
+ "guzzle/inflection": "self.version"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Service": "" }
+ },
+ "target-dir": "Guzzle/Service",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
throw new InvalidArgumentException('$context must be an array or resource');
}
- $this->setUrl($request);
- $this->addDefaultContextOptions($request);
- $this->addSslOptions($request);
- $this->addBodyOptions($request);
- $this->addProxyOptions($request);
-
// Dispatch the before send event
$request->dispatch('request.before_send', array(
'request' => $request,
'context_options' => $this->contextOptions
));
+ $this->setUrl($request);
+ $this->addDefaultContextOptions($request);
+ $this->addSslOptions($request);
+ $this->addBodyOptions($request);
+ $this->addProxyOptions($request);
+
// Create the file handle but silence errors
return $this->createStream($params)
->setCustomData('request', $request)
protected function addDefaultContextOptions(RequestInterface $request)
{
$this->setContextValue('http', 'method', $request->getMethod());
- $this->setContextValue('http', 'header', $request->getHeaderLines());
- // Force 1.0 for now until PHP fully support chunked transfer-encoding decoding
- $this->setContextValue('http', 'protocol_version', '1.0');
+ $headers = $request->getHeaderLines();
+
+ // "Connection: close" is required to get streams to work in HTTP 1.1
+ if (!$request->hasHeader('Connection')) {
+ $headers[] = 'Connection: close';
+ }
+
+ $this->setContextValue('http', 'header', $headers);
+ $this->setContextValue('http', 'protocol_version', $request->getProtocolVersion());
$this->setContextValue('http', 'ignore_errors', true);
}
*/
protected function addSslOptions(RequestInterface $request)
{
- if ($verify = $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) {
+ if ($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) {
$this->setContextValue('ssl', 'verify_peer', true, true);
if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) {
$this->setContextValue('ssl', 'cafile', $cafile, true);
{
// Set the size on the stream if it was returned in the response
foreach ($this->lastResponseHeaders as $header) {
- if (($pos = stripos($header, 'Content-Length:')) === 0) {
+ if ((stripos($header, 'Content-Length:')) === 0) {
$stream->setSize(trim(substr($header, 15)));
}
}
}
$ctx = hash_init($algo);
- while ($data = $stream->read(8192)) {
- hash_update($ctx, $data);
+ while (!$stream->feof()) {
+ hash_update($ctx, $stream->read(8192));
}
$out = hash_final($ctx, (bool) $rawOutput);
public function read($length)
{
- return $this->cache[self::IS_READABLE] ? fread($this->stream, $length) : false;
+ return fread($this->stream, $length);
}
public function write($string)
{
- if (!$this->cache[self::IS_WRITABLE]) {
- return 0;
- }
-
- $bytes = fwrite($this->stream, $string);
// We can't know the size after writing anything
$this->size = null;
- return $bytes;
+ return fwrite($this->stream, $string);
}
public function ftell()
--- /dev/null
+{
+ "name": "guzzle/stream",
+ "description": "Guzzle stream wrapper component",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": ["stream", "component", "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"
+ },
+ "suggest": {
+ "guzzle/http": "To convert Guzzle request objects to PHP streams"
+ },
+ "autoload": {
+ "psr-0": { "Guzzle\\Stream": "" }
+ },
+ "target-dir": "Guzzle/Stream",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7-dev"
+ }
+ }
+}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Formatter;
-
-use Monolog\Logger;
-
-/**
- * Formats a log message according to the ChromePHP array format
- *
- * @author Christophe Coevoet <stof@notk.org>
- */
-class ChromePHPFormatter implements FormatterInterface
-{
- /**
- * Translates Monolog log levels to Wildfire levels.
- */
- private $logLevels = array(
- Logger::DEBUG => 'log',
- Logger::INFO => 'info',
- Logger::NOTICE => 'info',
- Logger::WARNING => 'warn',
- Logger::ERROR => 'error',
- Logger::CRITICAL => 'error',
- Logger::ALERT => 'error',
- Logger::EMERGENCY => 'error',
- );
-
- /**
- * {@inheritdoc}
- */
- public function format(array $record)
- {
- // Retrieve the line and file if set and remove them from the formatted extra
- $backtrace = 'unknown';
- if (isset($record['extra']['file']) && isset($record['extra']['line'])) {
- $backtrace = $record['extra']['file'].' : '.$record['extra']['line'];
- unset($record['extra']['file']);
- unset($record['extra']['line']);
- }
-
- $message = array('message' => $record['message']);
- if ($record['context']) {
- $message['context'] = $record['context'];
- }
- if ($record['extra']) {
- $message['extra'] = $record['extra'];
- }
- if (count($message) === 1) {
- $message = reset($message);
- }
-
- return array(
- $record['channel'],
- $message,
- $backtrace,
- $this->logLevels[$record['level']],
- );
- }
-
- public function formatBatch(array $records)
- {
- $formatted = array();
-
- foreach ($records as $record) {
- $formatted[] = $this->format($record);
- }
-
- return $formatted;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Formatter;
-
-/**
- * Interface for formatters
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-interface FormatterInterface
-{
- /**
- * Formats a log record.
- *
- * @param array $record A record to format
- * @return mixed The formatted record
- */
- public function format(array $record);
-
- /**
- * Formats a set of log records.
- *
- * @param array $records A set of records to format
- * @return mixed The formatted set of records
- */
- public function formatBatch(array $records);
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Formatter;
-
-use Monolog\Logger;
-use Gelf\Message;
-
-/**
- * Serializes a log message to GELF
- * @see http://www.graylog2.org/about/gelf
- *
- * @author Matt Lehner <mlehner@gmail.com>
- */
-class GelfMessageFormatter extends NormalizerFormatter
-{
- /**
- * @var string the name of the system for the Gelf log message
- */
- protected $systemName;
-
- /**
- * @var string a prefix for 'extra' fields from the Monolog record (optional)
- */
- protected $extraPrefix;
-
- /**
- * @var string a prefix for 'context' fields from the Monolog record (optional)
- */
- protected $contextPrefix;
-
- /**
- * Translates Monolog log levels to Graylog2 log priorities.
- */
- private $logLevels = array(
- Logger::DEBUG => 7,
- Logger::INFO => 6,
- Logger::NOTICE => 5,
- Logger::WARNING => 4,
- Logger::ERROR => 3,
- Logger::CRITICAL => 2,
- Logger::ALERT => 1,
- Logger::EMERGENCY => 0,
- );
-
- public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_')
- {
- parent::__construct('U.u');
-
- $this->systemName = $systemName ?: gethostname();
-
- $this->extraPrefix = $extraPrefix;
- $this->contextPrefix = $contextPrefix;
- }
-
- /**
- * {@inheritdoc}
- */
- public function format(array $record)
- {
- $record = parent::format($record);
- $message = new Message();
- $message
- ->setTimestamp($record['datetime'])
- ->setShortMessage((string) $record['message'])
- ->setFacility($record['channel'])
- ->setHost($this->systemName)
- ->setLine(isset($record['extra']['line']) ? $record['extra']['line'] : null)
- ->setFile(isset($record['extra']['file']) ? $record['extra']['file'] : null)
- ->setLevel($this->logLevels[$record['level']]);
-
- // Do not duplicate these values in the additional fields
- unset($record['extra']['line']);
- unset($record['extra']['file']);
-
- foreach ($record['extra'] as $key => $val) {
- $message->setAdditional($this->extraPrefix . $key, is_scalar($val) ? $val : $this->toJson($val));
- }
-
- foreach ($record['context'] as $key => $val) {
- $message->setAdditional($this->contextPrefix . $key, is_scalar($val) ? $val : $this->toJson($val));
- }
-
- return $message;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Formatter;
-
-/**
- * Encodes whatever record data is passed to it as json
- *
- * This can be useful to log to databases or remote APIs
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-class JsonFormatter implements FormatterInterface
-{
- /**
- * {@inheritdoc}
- */
- public function format(array $record)
- {
- return json_encode($record);
- }
-
- /**
- * {@inheritdoc}
- */
- public function formatBatch(array $records)
- {
- return json_encode($records);
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Formatter;
-
-/**
- * Formats incoming records into a one-line string
- *
- * This is especially useful for logging to files
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- * @author Christophe Coevoet <stof@notk.org>
- */
-class LineFormatter extends NormalizerFormatter
-{
- const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
-
- protected $format;
-
- /**
- * @param string $format The format of the message
- * @param string $dateFormat The format of the timestamp: one supported by DateTime::format
- */
- public function __construct($format = null, $dateFormat = null)
- {
- $this->format = $format ?: static::SIMPLE_FORMAT;
- parent::__construct($dateFormat);
- }
-
- /**
- * {@inheritdoc}
- */
- public function format(array $record)
- {
- $vars = parent::format($record);
-
- $output = $this->format;
- foreach ($vars['extra'] as $var => $val) {
- if (false !== strpos($output, '%extra.'.$var.'%')) {
- $output = str_replace('%extra.'.$var.'%', $this->convertToString($val), $output);
- unset($vars['extra'][$var]);
- }
- }
- foreach ($vars as $var => $val) {
- $output = str_replace('%'.$var.'%', $this->convertToString($val), $output);
- }
-
- return $output;
- }
-
- public function formatBatch(array $records)
- {
- $message = '';
- foreach ($records as $record) {
- $message .= $this->format($record);
- }
-
- return $message;
- }
-
- protected function normalize($data)
- {
- if (is_bool($data) || is_null($data)) {
- return var_export($data, true);
- }
-
- if ($data instanceof \Exception) {
- return '[object] ('.get_class($data).': '.$data->getMessage().' at '.$data->getFile().':'.$data->getLine().')';
- }
-
- return parent::normalize($data);
- }
-
- protected function convertToString($data)
- {
- if (null === $data || is_scalar($data)) {
- return (string) $data;
- }
-
- $data = $this->normalize($data);
- if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
- return $this->toJson($data);
- }
-
- return str_replace('\\/', '/', json_encode($data));
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Formatter;
-
-/**
- * Serializes a log message to Logstash Event Format
- *
- * @see http://logstash.net/
- * @see https://github.com/logstash/logstash/blob/master/lib/logstash/event.rb
- *
- * @author Tim Mower <timothy.mower@gmail.com>
- */
-class LogstashFormatter extends NormalizerFormatter
-{
- /**
- * @var string the name of the system for the Logstash log message, used to fill the @source field
- */
- protected $systemName;
-
- /**
- * @var string an application name for the Logstash log message, used to fill the @type field
- */
- protected $applicationName;
-
- /**
- * @var string a prefix for 'extra' fields from the Monolog record (optional)
- */
- protected $extraPrefix;
-
- /**
- * @var string a prefix for 'context' fields from the Monolog record (optional)
- */
- protected $contextPrefix;
-
- /**
- * @param string $applicationName the application that sends the data, used as the "type" field of logstash
- * @param string $systemName the system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine
- * @param string $extraPrefix prefix for extra keys inside logstash "fields"
- * @param string $contextPrefix prefix for context keys inside logstash "fields", defaults to ctxt_
- */
- public function __construct($applicationName, $systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_')
- {
- //log stash requires a ISO 8601 format date
- parent::__construct('c');
-
- $this->systemName = $systemName ?: gethostname();
- $this->applicationName = $applicationName;
-
- $this->extraPrefix = $extraPrefix;
- $this->contextPrefix = $contextPrefix;
- }
-
- /**
- * {@inheritdoc}
- */
- public function format(array $record)
- {
- $record = parent::format($record);
- $message = array(
- '@timestamp' => $record['datetime'],
- '@message' => $record['message'],
- '@tags' => array($record['channel']),
- '@source' => $this->systemName
- );
-
- if ($this->applicationName) {
- $message['@type'] = $this->applicationName;
- }
- $message['@fields'] = array();
- $message['@fields']['channel'] = $record['channel'];
- $message['@fields']['level'] = $record['level'];
-
- if (isset($record['extra']['server'])) {
- $message['@source_host'] = $record['extra']['server'];
- }
- if (isset($record['extra']['url'])) {
- $message['@source_path'] = $record['extra']['url'];
- }
- foreach ($record['extra'] as $key => $val) {
- $message['@fields'][$this->extraPrefix . $key] = $val;
- }
-
- foreach ($record['context'] as $key => $val) {
- $message['@fields'][$this->contextPrefix . $key] = $val;
- }
-
- return json_encode($message) . "\n";
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Formatter;
-
-/**
- * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-class NormalizerFormatter implements FormatterInterface
-{
- const SIMPLE_DATE = "Y-m-d H:i:s";
-
- protected $dateFormat;
-
- /**
- * @param string $dateFormat The format of the timestamp: one supported by DateTime::format
- */
- public function __construct($dateFormat = null)
- {
- $this->dateFormat = $dateFormat ?: static::SIMPLE_DATE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function format(array $record)
- {
- return $this->normalize($record);
- }
-
- /**
- * {@inheritdoc}
- */
- public function formatBatch(array $records)
- {
- foreach ($records as $key => $record) {
- $records[$key] = $this->format($record);
- }
-
- return $records;
- }
-
- protected function normalize($data)
- {
- if (null === $data || is_scalar($data)) {
- return $data;
- }
-
- if (is_array($data) || $data instanceof \Traversable) {
- $normalized = array();
-
- foreach ($data as $key => $value) {
- $normalized[$key] = $this->normalize($value);
- }
-
- return $normalized;
- }
-
- if ($data instanceof \DateTime) {
- return $data->format($this->dateFormat);
- }
-
- if (is_object($data)) {
- return sprintf("[object] (%s: %s)", get_class($data), $this->toJson($data, true));
- }
-
- if (is_resource($data)) {
- return '[resource]';
- }
-
- return '[unknown('.gettype($data).')]';
- }
-
- protected function toJson($data, $ignoreErrors = false)
- {
- // suppress json_encode errors since it's twitchy with some inputs
- if ($ignoreErrors) {
- if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
- return @json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
- }
-
- return @json_encode($data);
- }
-
- if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
- return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
- }
-
- return json_encode($data);
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Formatter;
-
-use Monolog\Logger;
-
-/**
- * Serializes a log message according to Wildfire's header requirements
- *
- * @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
- * @author Christophe Coevoet <stof@notk.org>
- * @author Kirill chEbba Chebunin <iam@chebba.org>
- */
-class WildfireFormatter extends NormalizerFormatter
-{
- /**
- * Translates Monolog log levels to Wildfire levels.
- */
- private $logLevels = array(
- Logger::DEBUG => 'LOG',
- Logger::INFO => 'INFO',
- Logger::NOTICE => 'INFO',
- Logger::WARNING => 'WARN',
- Logger::ERROR => 'ERROR',
- Logger::CRITICAL => 'ERROR',
- Logger::ALERT => 'ERROR',
- Logger::EMERGENCY => 'ERROR',
- );
-
- /**
- * {@inheritdoc}
- */
- public function format(array $record)
- {
- // Retrieve the line and file if set and remove them from the formatted extra
- $file = $line = '';
- if (isset($record['extra']['file'])) {
- $file = $record['extra']['file'];
- unset($record['extra']['file']);
- }
- if (isset($record['extra']['line'])) {
- $line = $record['extra']['line'];
- unset($record['extra']['line']);
- }
-
- $record = $this->normalize($record);
- $message = array('message' => $record['message']);
- $handleError = false;
- if ($record['context']) {
- $message['context'] = $record['context'];
- $handleError = true;
- }
- if ($record['extra']) {
- $message['extra'] = $record['extra'];
- $handleError = true;
- }
- if (count($message) === 1) {
- $message = reset($message);
- }
-
- // Create JSON object describing the appearance of the message in the console
- $json = $this->toJson(array(
- array(
- 'Type' => $this->logLevels[$record['level']],
- 'File' => $file,
- 'Line' => $line,
- 'Label' => $record['channel'],
- ),
- $message,
- ), $handleError);
-
- // The message itself is a serialization of the above JSON object + it's length
- return sprintf(
- '%s|%s|',
- strlen($json),
- $json
- );
- }
-
- public function formatBatch(array $records)
- {
- throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter');
- }
-
- protected function normalize($data)
- {
- if (is_object($data) && !$data instanceof \DateTime) {
- return $data;
- }
-
- return parent::normalize($data);
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-use Monolog\Formatter\FormatterInterface;
-use Monolog\Formatter\LineFormatter;
-
-/**
- * Base Handler class providing the Handler structure
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-abstract class AbstractHandler implements HandlerInterface
-{
- protected $level = Logger::DEBUG;
- protected $bubble = false;
-
- /**
- * @var FormatterInterface
- */
- protected $formatter;
- protected $processors = array();
-
- /**
- * @param integer $level The minimum logging level at which this handler will be triggered
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- */
- public function __construct($level = Logger::DEBUG, $bubble = true)
- {
- $this->level = $level;
- $this->bubble = $bubble;
- }
-
- /**
- * {@inheritdoc}
- */
- public function isHandling(array $record)
- {
- return $record['level'] >= $this->level;
- }
-
- /**
- * {@inheritdoc}
- */
- public function handleBatch(array $records)
- {
- foreach ($records as $record) {
- $this->handle($record);
- }
- }
-
- /**
- * Closes the handler.
- *
- * This will be called automatically when the object is destroyed
- */
- public function close()
- {
- }
-
- /**
- * {@inheritdoc}
- */
- public function pushProcessor($callback)
- {
- if (!is_callable($callback)) {
- throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given');
- }
- array_unshift($this->processors, $callback);
- }
-
- /**
- * {@inheritdoc}
- */
- public function popProcessor()
- {
- if (!$this->processors) {
- throw new \LogicException('You tried to pop from an empty processor stack.');
- }
-
- return array_shift($this->processors);
- }
-
- /**
- * {@inheritdoc}
- */
- public function setFormatter(FormatterInterface $formatter)
- {
- $this->formatter = $formatter;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFormatter()
- {
- if (!$this->formatter) {
- $this->formatter = $this->getDefaultFormatter();
- }
-
- return $this->formatter;
- }
-
- /**
- * Sets minimum logging level at which this handler will be triggered.
- *
- * @param integer $level
- */
- public function setLevel($level)
- {
- $this->level = $level;
- }
-
- /**
- * Gets minimum logging level at which this handler will be triggered.
- *
- * @return integer
- */
- public function getLevel()
- {
- return $this->level;
- }
-
- /**
- * Sets the bubbling behavior.
- *
- * @param Boolean $bubble True means that bubbling is not permitted.
- * False means that this handler allows bubbling.
- */
- public function setBubble($bubble)
- {
- $this->bubble = $bubble;
- }
-
- /**
- * Gets the bubbling behavior.
- *
- * @return Boolean True means that bubbling is not permitted.
- * False means that this handler allows bubbling.
- */
- public function getBubble()
- {
- return $this->bubble;
- }
-
- public function __destruct()
- {
- try {
- $this->close();
- } catch (\Exception $e) {
- // do nothing
- }
- }
-
- /**
- * Gets the default formatter.
- *
- * @return FormatterInterface
- */
- protected function getDefaultFormatter()
- {
- return new LineFormatter();
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-/**
- * Base Handler class providing the Handler structure
- *
- * Classes extending it should (in most cases) only implement write($record)
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- * @author Christophe Coevoet <stof@notk.org>
- */
-abstract class AbstractProcessingHandler extends AbstractHandler
-{
- /**
- * {@inheritdoc}
- */
- public function handle(array $record)
- {
- if ($record['level'] < $this->level) {
- return false;
- }
-
- $record = $this->processRecord($record);
-
- $record['formatted'] = $this->getFormatter()->format($record);
-
- $this->write($record);
-
- return false === $this->bubble;
- }
-
- /**
- * Writes the record down to the log of the implementing handler
- *
- * @param array $record
- * @return void
- */
- abstract protected function write(array $record);
-
- /**
- * Processes a record.
- *
- * @param array $record
- * @return array
- */
- protected function processRecord(array $record)
- {
- if ($this->processors) {
- foreach ($this->processors as $processor) {
- $record = call_user_func($processor, $record);
- }
- }
-
- return $record;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-use Monolog\Formatter\JsonFormatter;
-
-class AmqpHandler extends AbstractProcessingHandler
-{
- /**
- * @var \AMQPExchange $exchange
- */
- protected $exchange;
-
- /**
- * @param \AMQPExchange $exchange AMQP exchange, ready for use
- * @param string $exchangeName
- * @param int $level
- * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
- */
- public function __construct(\AMQPExchange $exchange, $exchangeName = 'log', $level = Logger::DEBUG, $bubble = true)
- {
- $this->exchange = $exchange;
- $this->exchange->setName($exchangeName);
-
- parent::__construct($level, $bubble);
- }
-
- /**
- * {@inheritDoc}
- */
- protected function write(array $record)
- {
- $data = $record["formatted"];
-
- $routingKey = sprintf(
- '%s.%s',
- substr($record['level_name'], 0, 4),
- $record['channel']
- );
-
- $this->exchange->publish(
- $data,
- strtolower($routingKey),
- 0,
- array(
- 'delivery_mode' => 2,
- 'Content-type' => 'application/json'
- )
- );
- }
-
- /**
- * {@inheritDoc}
- */
- protected function getDefaultFormatter()
- {
- return new JsonFormatter();
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-
-/**
- * Buffers all records until closing the handler and then pass them as batch.
- *
- * This is useful for a MailHandler to send only one mail per request instead of
- * sending one per log message.
- *
- * @author Christophe Coevoet <stof@notk.org>
- */
-class BufferHandler extends AbstractHandler
-{
- protected $handler;
- protected $bufferSize = 0;
- protected $bufferLimit;
- protected $flushOnOverflow;
- protected $buffer = array();
-
- /**
- * @param HandlerInterface $handler Handler.
- * @param integer $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
- * @param integer $level The minimum logging level at which this handler will be triggered
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- * @param Boolean $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded
- */
- public function __construct(HandlerInterface $handler, $bufferSize = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false)
- {
- parent::__construct($level, $bubble);
- $this->handler = $handler;
- $this->bufferLimit = (int) $bufferSize;
- $this->flushOnOverflow = $flushOnOverflow;
-
- // __destructor() doesn't get called on Fatal errors
- register_shutdown_function(array($this, 'close'));
- }
-
- /**
- * {@inheritdoc}
- */
- public function handle(array $record)
- {
- if ($record['level'] < $this->level) {
- return false;
- }
-
- if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) {
- if ($this->flushOnOverflow) {
- $this->flush();
- } else {
- array_shift($this->buffer);
- $this->bufferSize--;
- }
- }
-
- if ($this->processors) {
- foreach ($this->processors as $processor) {
- $record = call_user_func($processor, $record);
- }
- }
-
- $this->buffer[] = $record;
- $this->bufferSize++;
-
- return false === $this->bubble;
- }
-
- public function flush()
- {
- if ($this->bufferSize === 0) {
- return;
- }
-
- $this->handler->handleBatch($this->buffer);
- $this->bufferSize = 0;
- $this->buffer = array();
- }
-
- /**
- * {@inheritdoc}
- */
- public function close()
- {
- $this->flush();
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Formatter\ChromePHPFormatter;
-
-/**
- * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
- *
- * @author Christophe Coevoet <stof@notk.org>
- */
-class ChromePHPHandler extends AbstractProcessingHandler
-{
- /**
- * Version of the extension
- */
- const VERSION = '3.0';
-
- /**
- * Header name
- */
- const HEADER_NAME = 'X-ChromePhp-Data';
-
- protected static $initialized = false;
-
- protected static $json = array(
- 'version' => self::VERSION,
- 'columns' => array('label', 'log', 'backtrace', 'type'),
- 'rows' => array(),
- );
-
- protected static $sendHeaders = true;
-
- /**
- * {@inheritdoc}
- */
- public function handleBatch(array $records)
- {
- $messages = array();
-
- foreach ($records as $record) {
- if ($record['level'] < $this->level) {
- continue;
- }
- $messages[] = $this->processRecord($record);
- }
-
- if (!empty($messages)) {
- $messages = $this->getFormatter()->formatBatch($messages);
- self::$json['rows'] = array_merge(self::$json['rows'], $messages);
- $this->send();
- }
- }
-
- /**
- * {@inheritDoc}
- */
- protected function getDefaultFormatter()
- {
- return new ChromePHPFormatter();
- }
-
- /**
- * Creates & sends header for a record
- *
- * @see sendHeader()
- * @see send()
- * @param array $record
- */
- protected function write(array $record)
- {
- self::$json['rows'][] = $record['formatted'];
-
- $this->send();
- }
-
- /**
- * Sends the log header
- *
- * @see sendHeader()
- */
- protected function send()
- {
- if (!self::$initialized) {
- self::$sendHeaders = $this->headersAccepted();
- self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
-
- self::$initialized = true;
- }
-
- $json = @json_encode(self::$json);
- $this->sendHeader(self::HEADER_NAME, base64_encode(utf8_encode($json)));
- }
-
- /**
- * Send header string to the client
- *
- * @param string $header
- * @param string $content
- */
- protected function sendHeader($header, $content)
- {
- if (!headers_sent() && self::$sendHeaders) {
- header(sprintf('%s: %s', $header, $content));
- }
- }
-
- /**
- * Verifies if the headers are accepted by the current user agent
- *
- * @return Boolean
- */
- protected function headersAccepted()
- {
- return !isset($_SERVER['HTTP_USER_AGENT'])
- || preg_match('{\bChrome/\d+[\.\d+]*\b}', $_SERVER['HTTP_USER_AGENT']);
- }
-
- /**
- * BC getter for the sendHeaders property that has been made static
- */
- public function __get($property)
- {
- if ('sendHeaders' !== $property) {
- throw new \InvalidArgumentException('Undefined property '.$property);
- }
-
- return static::$sendHeaders;
- }
-
- /**
- * BC setter for the sendHeaders property that has been made static
- */
- public function __set($property, $value)
- {
- if ('sendHeaders' !== $property) {
- throw new \InvalidArgumentException('Undefined property '.$property);
- }
-
- static::$sendHeaders = $value;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Formatter\JsonFormatter;
-use Monolog\Logger;
-
-/**
- * CouchDB handler
- *
- * @author Markus Bachmann <markus.bachmann@bachi.biz>
- */
-class CouchDBHandler extends AbstractProcessingHandler
-{
- private $options;
-
- public function __construct(array $options = array(), $level = Logger::DEBUG, $bubble = true)
- {
- $this->options = array_merge(array(
- 'host' => 'localhost',
- 'port' => 5984,
- 'dbname' => 'logger',
- 'username' => null,
- 'password' => null,
- ), $options);
-
- parent::__construct($level, $bubble);
- }
-
- /**
- * {@inheritDoc}
- */
- protected function write(array $record)
- {
- $basicAuth = null;
- if ($this->options['username']) {
- $basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']);
- }
-
- $url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname'];
- $context = stream_context_create(array(
- 'http' => array(
- 'method' => 'POST',
- 'content' => $record['formatted'],
- 'ignore_errors' => true,
- 'max_redirects' => 0,
- 'header' => 'Content-type: application/json',
- )
- ));
-
- if (false === @file_get_contents($url, null, $context)) {
- throw new \RuntimeException(sprintf('Could not connect to %s', $url));
- }
- }
-
- /**
- * {@inheritDoc}
- */
- protected function getDefaultFormatter()
- {
- return new JsonFormatter();
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-
-/**
- * Logs to Cube.
- *
- * @link http://square.github.com/cube/
- * @author Wan Chen <kami@kamisama.me>
- */
-class CubeHandler extends AbstractProcessingHandler
-{
- private $udpConnection = null;
- private $httpConnection = null;
- private $scheme = null;
- private $host = null;
- private $port = null;
- private $acceptedSchemes = array('http', 'udp');
-
- /**
- * Create a Cube handler
- *
- * @throws UnexpectedValueException when given url is not a valid url.
- * A valid url must consists of three parts : protocol://host:port
- * Only valid protocol used by Cube are http and udp
- */
- public function __construct($url, $level = Logger::DEBUG, $bubble = true)
- {
- $urlInfos = parse_url($url);
-
- if (!isset($urlInfos['scheme']) || !isset($urlInfos['host']) || !isset($urlInfos['port'])) {
- throw new \UnexpectedValueException('URL "'.$url.'" is not valid');
- }
-
- if (!in_array($urlInfos['scheme'], $this->acceptedSchemes)) {
- throw new \UnexpectedValueException(
- 'Invalid protocol (' . $urlInfos['scheme'] . ').'
- . ' Valid options are ' . implode(', ', $this->acceptedSchemes));
- }
-
- $this->scheme = $urlInfos['scheme'];
- $this->host = $urlInfos['host'];
- $this->port = $urlInfos['port'];
-
- parent::__construct($level, $bubble);
- }
-
- /**
- * Establish a connection to an UDP socket
- *
- * @throws LogicException when unable to connect to the socket
- */
- protected function connectUdp()
- {
- if (!extension_loaded('sockets')) {
- throw new \LogicException('The sockets extension is needed to use udp URLs with the CubeHandler');
- }
-
- $this->udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0);
- if (!$this->udpConnection) {
- throw new \LogicException('Unable to create a socket');
- }
-
- if (!socket_connect($this->udpConnection, $this->host, $this->port)) {
- throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port);
- }
- }
-
- /**
- * Establish a connection to a http server
- */
- protected function connectHttp()
- {
- if (!extension_loaded('curl')) {
- throw new \LogicException('The curl extension is needed to use http URLs with the CubeHandler');
- }
-
- $this->httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put');
-
- if (!$this->httpConnection) {
- throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port);
- }
-
- curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST");
- curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true);
- }
-
- /**
- * {@inheritdoc}
- */
- protected function write(array $record)
- {
- $date = $record['datetime'];
-
- $data = array('time' => $date->format('Y-m-d\TH:i:s.u'));
- unset($record['datetime']);
-
- if (isset($record['context']['type'])) {
- $data['type'] = $record['context']['type'];
- unset($record['context']['type']);
- } else {
- $data['type'] = $record['channel'];
- }
-
- $data['data'] = $record['context'];
- $data['data']['level'] = $record['level'];
-
- $this->{'write'.$this->scheme}(json_encode($data));
- }
-
- private function writeUdp($data)
- {
- if (!$this->udpConnection) {
- $this->connectUdp();
- }
-
- socket_send($this->udpConnection, $data, strlen($data), 0);
- }
-
- private function writeHttp($data)
- {
- if (!$this->httpConnection) {
- $this->connectHttp();
- }
-
- curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']');
- curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, array(
- 'Content-Type: application/json',
- 'Content-Length: ' . strlen('['.$data.']'))
- );
-
- return curl_exec($this->httpConnection);
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-use Monolog\Formatter\NormalizerFormatter;
-use Doctrine\CouchDB\CouchDBClient;
-
-/**
- * CouchDB handler for Doctrine CouchDB ODM
- *
- * @author Markus Bachmann <markus.bachmann@bachi.biz>
- */
-class DoctrineCouchDBHandler extends AbstractProcessingHandler
-{
- private $client;
-
- public function __construct(CouchDBClient $client, $level = Logger::DEBUG, $bubble = true)
- {
- $this->client = $client;
- parent::__construct($level, $bubble);
- }
-
- /**
- * {@inheritDoc}
- */
- protected function write(array $record)
- {
- $this->client->postDocument($record['formatted']);
- }
-
- protected function getDefaultFormatter()
- {
- return new NormalizerFormatter;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler\FingersCrossed;
-
-/**
- * Interface for activation strategies for the FingersCrossedHandler.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- */
-interface ActivationStrategyInterface
-{
- /**
- * Returns whether the given record activates the handler.
- *
- * @param array $record
- * @return Boolean
- */
- public function isHandlerActivated(array $record);
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler\FingersCrossed;
-
-/**
- * Error level based activation strategy.
- *
- * @author Johannes M. Schmitt <schmittjoh@gmail.com>
- */
-class ErrorLevelActivationStrategy implements ActivationStrategyInterface
-{
- private $actionLevel;
-
- public function __construct($actionLevel)
- {
- $this->actionLevel = $actionLevel;
- }
-
- public function isHandlerActivated(array $record)
- {
- return $record['level'] >= $this->actionLevel;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
-use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
-use Monolog\Logger;
-
-/**
- * Buffers all records until a certain level is reached
- *
- * The advantage of this approach is that you don't get any clutter in your log files.
- * Only requests which actually trigger an error (or whatever your actionLevel is) will be
- * in the logs, but they will contain all records, not only those above the level threshold.
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-class FingersCrossedHandler extends AbstractHandler
-{
- protected $handler;
- protected $activationStrategy;
- protected $buffering = true;
- protected $bufferSize;
- protected $buffer = array();
- protected $stopBuffering;
-
- /**
- * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler).
- * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action
- * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- * @param Boolean $stopBuffering Whether the handler should stop buffering after being triggered (default true)
- */
- public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true)
- {
- if (null === $activationStrategy) {
- $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING);
- }
- if (!$activationStrategy instanceof ActivationStrategyInterface) {
- $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy);
- }
-
- $this->handler = $handler;
- $this->activationStrategy = $activationStrategy;
- $this->bufferSize = $bufferSize;
- $this->bubble = $bubble;
- $this->stopBuffering = $stopBuffering;
- }
-
- /**
- * {@inheritdoc}
- */
- public function isHandling(array $record)
- {
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function handle(array $record)
- {
- if ($this->processors) {
- foreach ($this->processors as $processor) {
- $record = call_user_func($processor, $record);
- }
- }
-
- if ($this->buffering) {
- $this->buffer[] = $record;
- if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) {
- array_shift($this->buffer);
- }
- if ($this->activationStrategy->isHandlerActivated($record)) {
- if ($this->stopBuffering) {
- $this->buffering = false;
- }
- if (!$this->handler instanceof HandlerInterface) {
- if (!is_callable($this->handler)) {
- throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object");
- }
- $this->handler = call_user_func($this->handler, $record, $this);
- if (!$this->handler instanceof HandlerInterface) {
- throw new \RuntimeException("The factory callable should return a HandlerInterface");
- }
- }
- $this->handler->handleBatch($this->buffer);
- $this->buffer = array();
- }
- } else {
- $this->handler->handle($record);
- }
-
- return false === $this->bubble;
- }
-
- /**
- * Resets the state of the handler. Stops forwarding records to the wrapped handler.
- */
- public function reset()
- {
- $this->buffering = true;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Formatter\WildfireFormatter;
-
-/**
- * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol.
- *
- * @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
- */
-class FirePHPHandler extends AbstractProcessingHandler
-{
- /**
- * WildFire JSON header message format
- */
- const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2';
-
- /**
- * FirePHP structure for parsing messages & their presentation
- */
- const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1';
-
- /**
- * Must reference a "known" plugin, otherwise headers won't display in FirePHP
- */
- const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3';
-
- /**
- * Header prefix for Wildfire to recognize & parse headers
- */
- const HEADER_PREFIX = 'X-Wf';
-
- /**
- * Whether or not Wildfire vendor-specific headers have been generated & sent yet
- */
- protected static $initialized = false;
-
- /**
- * Shared static message index between potentially multiple handlers
- * @var int
- */
- protected static $messageIndex = 1;
-
- protected static $sendHeaders = true;
-
- /**
- * Base header creation function used by init headers & record headers
- *
- * @param array $meta Wildfire Plugin, Protocol & Structure Indexes
- * @param string $message Log message
- * @return array Complete header string ready for the client as key and message as value
- */
- protected function createHeader(array $meta, $message)
- {
- $header = sprintf('%s-%s', self::HEADER_PREFIX, join('-', $meta));
-
- return array($header => $message);
- }
-
- /**
- * Creates message header from record
- *
- * @see createHeader()
- * @param array $record
- * @return string
- */
- protected function createRecordHeader(array $record)
- {
- // Wildfire is extensible to support multiple protocols & plugins in a single request,
- // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake.
- return $this->createHeader(
- array(1, 1, 1, self::$messageIndex++),
- $record['formatted']
- );
- }
-
- /**
- * {@inheritDoc}
- */
- protected function getDefaultFormatter()
- {
- return new WildfireFormatter();
- }
-
- /**
- * Wildfire initialization headers to enable message parsing
- *
- * @see createHeader()
- * @see sendHeader()
- * @return array
- */
- protected function getInitHeaders()
- {
- // Initial payload consists of required headers for Wildfire
- return array_merge(
- $this->createHeader(array('Protocol', 1), self::PROTOCOL_URI),
- $this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI),
- $this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI)
- );
- }
-
- /**
- * Send header string to the client
- *
- * @param string $header
- * @param string $content
- */
- protected function sendHeader($header, $content)
- {
- if (!headers_sent() && self::$sendHeaders) {
- header(sprintf('%s: %s', $header, $content));
- }
- }
-
- /**
- * Creates & sends header for a record, ensuring init headers have been sent prior
- *
- * @see sendHeader()
- * @see sendInitHeaders()
- * @param array $record
- */
- protected function write(array $record)
- {
- // WildFire-specific headers must be sent prior to any messages
- if (!self::$initialized) {
- self::$sendHeaders = $this->headersAccepted();
-
- foreach ($this->getInitHeaders() as $header => $content) {
- $this->sendHeader($header, $content);
- }
-
- self::$initialized = true;
- }
-
- $header = $this->createRecordHeader($record);
- $this->sendHeader(key($header), current($header));
- }
-
- /**
- * Verifies if the headers are accepted by the current user agent
- *
- * @return Boolean
- */
- protected function headersAccepted()
- {
- return !isset($_SERVER['HTTP_USER_AGENT'])
- || preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])
- || isset($_SERVER['HTTP_X_FIREPHP_VERSION']);
- }
-
- /**
- * BC getter for the sendHeaders property that has been made static
- */
- public function __get($property)
- {
- if ('sendHeaders' !== $property) {
- throw new \InvalidArgumentException('Undefined property '.$property);
- }
-
- return static::$sendHeaders;
- }
-
- /**
- * BC setter for the sendHeaders property that has been made static
- */
- public function __set($property, $value)
- {
- if ('sendHeaders' !== $property) {
- throw new \InvalidArgumentException('Undefined property '.$property);
- }
-
- static::$sendHeaders = $value;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Gelf\IMessagePublisher;
-use Monolog\Logger;
-use Monolog\Handler\AbstractProcessingHandler;
-use Monolog\Formatter\GelfMessageFormatter;
-
-/**
- * Handler to send messages to a Graylog2 (http://www.graylog2.org) server
- *
- * @author Matt Lehner <mlehner@gmail.com>
- */
-class GelfHandler extends AbstractProcessingHandler
-{
- /**
- * @var Gelf\IMessagePublisher the publisher object that sends the message to the server
- */
- protected $publisher;
-
- /**
- * @param Gelf\IMessagePublisher $publisher a publisher object
- * @param integer $level The minimum logging level at which this handler will be triggered
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- */
- public function __construct(IMessagePublisher $publisher, $level = Logger::DEBUG, $bubble = true)
- {
- parent::__construct($level, $bubble);
-
- $this->publisher = $publisher;
- }
-
- /**
- * {@inheritdoc}
- */
- public function close()
- {
- $this->publisher = null;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function write(array $record)
- {
- $this->publisher->publish($record['formatted']);
- }
-
- /**
- * {@inheritDoc}
- */
- protected function getDefaultFormatter()
- {
- return new GelfMessageFormatter();
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-/**
- * Forwards records to multiple handlers
- *
- * @author Lenar Lõhmus <lenar@city.ee>
- */
-class GroupHandler extends AbstractHandler
-{
- protected $handlers;
-
- /**
- * @param array $handlers Array of Handlers.
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- */
- public function __construct(array $handlers, $bubble = true)
- {
- foreach ($handlers as $handler) {
- if (!$handler instanceof HandlerInterface) {
- throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.');
- }
- }
-
- $this->handlers = $handlers;
- $this->bubble = $bubble;
- }
-
- /**
- * {@inheritdoc}
- */
- public function isHandling(array $record)
- {
- foreach ($this->handlers as $handler) {
- if ($handler->isHandling($record)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * {@inheritdoc}
- */
- public function handle(array $record)
- {
- if ($this->processors) {
- foreach ($this->processors as $processor) {
- $record = call_user_func($processor, $record);
- }
- }
-
- foreach ($this->handlers as $handler) {
- $handler->handle($record);
- }
-
- return false === $this->bubble;
- }
-
- /**
- * {@inheritdoc}
- */
- public function handleBatch(array $records)
- {
- foreach ($this->handlers as $handler) {
- $handler->handleBatch($records);
- }
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Formatter\FormatterInterface;
-
-/**
- * Interface that all Monolog Handlers must implement
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-interface HandlerInterface
-{
- /**
- * Checks whether the given record will be handled by this handler.
- *
- * This is mostly done for performance reasons, to avoid calling processors for nothing.
- *
- * Handlers should still check the record levels within handle(), returning false in isHandling()
- * is no guarantee that handle() will not be called, and isHandling() might not be called
- * for a given record.
- *
- * @param array $record
- *
- * @return Boolean
- */
- public function isHandling(array $record);
-
- /**
- * Handles a record.
- *
- * All records may be passed to this method, and the handler should discard
- * those that it does not want to handle.
- *
- * The return value of this function controls the bubbling process of the handler stack.
- * Unless the bubbling is interrupted (by returning true), the Logger class will keep on
- * calling further handlers in the stack with a given log record.
- *
- * @param array $record The record to handle
- * @return Boolean True means that this handler handled the record, and that bubbling is not permitted.
- * False means the record was either not processed or that this handler allows bubbling.
- */
- public function handle(array $record);
-
- /**
- * Handles a set of records at once.
- *
- * @param array $records The records to handle (an array of record arrays)
- */
- public function handleBatch(array $records);
-
- /**
- * Adds a processor in the stack.
- *
- * @param callable $callback
- */
- public function pushProcessor($callback);
-
- /**
- * Removes the processor on top of the stack and returns it.
- *
- * @return callable
- */
- public function popProcessor();
-
- /**
- * Sets the formatter.
- *
- * @param FormatterInterface $formatter
- */
- public function setFormatter(FormatterInterface $formatter);
-
- /**
- * Gets the formatter.
- *
- * @return FormatterInterface
- */
- public function getFormatter();
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-/**
- * Base class for all mail handlers
- *
- * @author Gyula Sallai
- */
-abstract class MailHandler extends AbstractProcessingHandler
-{
- /**
- * {@inheritdoc}
- */
- public function handleBatch(array $records)
- {
- $messages = array();
-
- foreach ($records as $record) {
- if ($record['level'] < $this->level) {
- continue;
- }
- $messages[] = $this->processRecord($record);
- }
-
- if (!empty($messages)) {
- $this->send((string) $this->getFormatter()->formatBatch($messages), $messages);
- }
- }
-
- /**
- * Send a mail with the given content
- *
- * @param string $content
- * @param array $records the array of log records that formed this content
- */
- abstract protected function send($content, array $records);
-
- /**
- * {@inheritdoc}
- */
- protected function write(array $record)
- {
- $this->send((string) $record['formatted'], array($record));
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-/**
- * Exception can be thrown if an extension for an handler is missing
- *
- * @author Christian Bergau <cbergau86@gmail.com>
- */
-class MissingExtensionException extends \Exception
-{
-
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Thomas Tourlourat <thomas@tourlourat.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-use Monolog\Formatter\NormalizerFormatter;
-
-/**
- * Logs to a MongoDB database.
- *
- * usage example:
- *
- * $log = new Logger('application');
- * $mongodb = new MongoDBHandler(new \Mongo("mongodb://localhost:27017"), "logs", "prod");
- * $log->pushHandler($mongodb);
- *
- * @author Thomas Tourlourat <thomas@tourlourat.com>
- */
-class MongoDBHandler extends AbstractProcessingHandler
-{
- private $mongoCollection;
-
- public function __construct($mongo, $database, $collection, $level = Logger::DEBUG, $bubble = true)
- {
- if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo)) {
- throw new \InvalidArgumentException('MongoClient or Mongo instance required');
- }
-
- $this->mongoCollection = $mongo->selectCollection($database, $collection);
-
- parent::__construct($level, $bubble);
- }
-
- protected function write(array $record)
- {
- $this->mongoCollection->save($record["formatted"]);
- }
-
- /**
- * {@inheritDoc}
- */
- protected function getDefaultFormatter()
- {
- return new NormalizerFormatter();
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-
-/**
- * NativeMailerHandler uses the mail() function to send the emails
- *
- * @author Christophe Coevoet <stof@notk.org>
- */
-class NativeMailerHandler extends MailHandler
-{
- protected $to;
- protected $subject;
- protected $headers = array(
- 'Content-type: text/plain; charset=utf-8'
- );
-
- /**
- * @param string|array $to The receiver of the mail
- * @param string $subject The subject of the mail
- * @param string $from The sender of the mail
- * @param integer $level The minimum logging level at which this handler will be triggered
- * @param boolean $bubble Whether the messages that are handled can bubble up the stack or not
- */
- public function __construct($to, $subject, $from, $level = Logger::ERROR, $bubble = true)
- {
- parent::__construct($level, $bubble);
- $this->to = is_array($to) ? $to : array($to);
- $this->subject = $subject;
- $this->addHeader(sprintf('From: %s', $from));
- }
-
- /**
- * @param string|array $headers Custom added headers
- */
- public function addHeader($headers)
- {
- foreach ((array) $headers as $header) {
- if (strpos($header, "\n") !== false || strpos($header, "\r") !== false) {
- throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons');
- }
- $this->headers[] = $header;
- }
- }
-
- /**
- * {@inheritdoc}
- */
- protected function send($content, array $records)
- {
- $content = wordwrap($content, 70);
- $headers = implode("\r\n", $this->headers) . "\r\n";
- foreach ($this->to as $to) {
- mail($to, $this->subject, $content, $headers);
- }
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-
-/**
- * Blackhole
- *
- * Any record it can handle will be thrown away. This can be used
- * to put on top of an existing stack to override it temporarily.
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-class NullHandler extends AbstractHandler
-{
- /**
- * @param integer $level The minimum logging level at which this handler will be triggered
- */
- public function __construct($level = Logger::DEBUG)
- {
- parent::__construct($level, false);
- }
-
- /**
- * {@inheritdoc}
- */
- public function handle(array $record)
- {
- if ($record['level'] < $this->level) {
- return false;
- }
-
- return true;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-
-/**
- * Sends notifications through the pushover api to mobile phones
- *
- * @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com>
- * @see https://www.pushover.net/api
- */
-class PushoverHandler extends SocketHandler
-{
- private $token;
- private $user;
- private $title;
-
- /**
- * @param string $token Pushover api token
- * @param string $user Pushover user id the message will be sent to
- * @param string $title Title sent to Pushover API
- * @param integer $level The minimum logging level at which this handler will be triggered
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- * @param Boolean $useSSL Whether to connect via SSL. Required when pushing messages to users that are not
- * the pushover.net app owner. OpenSSL is required for this option.
- */
- public function __construct($token, $user, $title = null, $level = Logger::CRITICAL, $bubble = true, $useSSL = true)
- {
- $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80';
- parent::__construct($connectionString, $level, $bubble);
-
- $this->token = $token;
- $this->user = $user;
- $this->title = $title ?: gethostname();
- }
-
- protected function generateDataStream($record)
- {
- $content = $this->buildContent($record);
-
- return $this->buildHeader($content) . $content;
- }
-
- private function buildContent($record)
- {
- // Pushover has a limit of 512 characters on title and message combined.
- $maxMessageLength = 512 - strlen($this->title);
- $message = substr($record['message'], 0, $maxMessageLength);
- $timestamp = $record['datetime']->getTimestamp();
-
- $dataArray = array(
- 'token' => $this->token,
- 'user' => $this->user,
- 'message' => $message,
- 'title' => $this->title,
- 'timestamp' => $timestamp
- );
-
- return http_build_query($dataArray);
- }
-
- private function buildHeader($content)
- {
- $header = "POST /1/messages.json HTTP/1.1\r\n";
- $header .= "Host: api.pushover.net\r\n";
- $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
- $header .= "Content-Length: " . strlen($content) . "\r\n";
- $header .= "\r\n";
-
- return $header;
- }
-
- public function write(array $record)
- {
- parent::write($record);
- $this->closeSocket();
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Formatter\LineFormatter;
-use Monolog\Logger;
-use Monolog\Handler\AbstractProcessingHandler;
-use Raven_Client;
-
-/**
- * Handler to send messages to a Sentry (https://github.com/dcramer/sentry) server
- * using raven-php (https://github.com/getsentry/raven-php)
- *
- * @author Marc Abramowitz <marc@marc-abramowitz.com>
- */
-class RavenHandler extends AbstractProcessingHandler
-{
- /**
- * Translates Monolog log levels to Raven log levels.
- */
- private $logLevels = array(
- Logger::DEBUG => Raven_Client::DEBUG,
- Logger::INFO => Raven_Client::INFO,
- Logger::NOTICE => Raven_Client::INFO,
- Logger::WARNING => Raven_Client::WARNING,
- Logger::ERROR => Raven_Client::ERROR,
- Logger::CRITICAL => Raven_Client::FATAL,
- Logger::ALERT => Raven_Client::FATAL,
- Logger::EMERGENCY => Raven_Client::FATAL,
- );
-
- /**
- * @var Raven_Client the client object that sends the message to the server
- */
- protected $ravenClient;
-
- /**
- * @param Raven_Client $ravenClient
- * @param integer $level The minimum logging level at which this handler will be triggered
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- */
- public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true)
- {
- parent::__construct($level, $bubble);
-
- $this->ravenClient = $ravenClient;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function write(array $record)
- {
- $level = $this->logLevels[$record['level']];
-
- $options = array();
- $options['level'] = $level;
- if (!empty($record['context'])) {
- $options['extra']['context'] = $record['context'];
- }
- if (!empty($record['extra'])) {
- $options['extra']['extra'] = $record['extra'];
- }
-
- $this->ravenClient->captureMessage(
- $record['formatted'],
- array(), // $params - not used
- version_compare(Raven_Client::VERSION, '0.1.0', '>') ? $options : $level, // $level or $options
- false // $stack
- );
- if ($record['level'] >= Logger::ERROR && isset($record['context']['exception'])) {
- $this->ravenClient->captureException($record['context']['exception']);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- protected function getDefaultFormatter()
- {
- return new LineFormatter('[%channel%] %message%');
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Thomas Tourlourat <thomas@tourlourat.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-use Monolog\Formatter\LineFormatter;
-
-/**
- * Logs to a Redis key using rpush
- *
- * usage example:
- *
- * $log = new Logger('application');
- * $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs", "prod");
- * $log->pushHandler($redis);
- *
- * @author Thomas Tourlourat <thomas@tourlourat.com>
- */
-class RedisHandler extends AbstractProcessingHandler
-{
- private $redisClient;
- private $redisKey;
-
- # redis instance, key to use
- public function __construct($redis, $key, $level = Logger::DEBUG, $bubble = true)
- {
- if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) {
- throw new \InvalidArgumentException('Predis\Client or Redis instance required');
- }
-
- $this->redisClient = $redis;
- $this->redisKey = $key;
-
- parent::__construct($level, $bubble);
- }
-
- protected function write(array $record)
- {
- $this->redisClient->rpush($this->redisKey, $record["formatted"]);
- }
-
- /**
- * {@inheritDoc}
- */
- protected function getDefaultFormatter()
- {
- return new LineFormatter();
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-
-/**
- * Stores logs to files that are rotated every day and a limited number of files are kept.
- *
- * This rotation is only intended to be used as a workaround. Using logrotate to
- * handle the rotation is strongly encouraged when you can use it.
- *
- * @author Christophe Coevoet <stof@notk.org>
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-class RotatingFileHandler extends StreamHandler
-{
- protected $filename;
- protected $maxFiles;
- protected $mustRotate;
- protected $nextRotation;
-
- /**
- * @param string $filename
- * @param integer $maxFiles The maximal amount of files to keep (0 means unlimited)
- * @param integer $level The minimum logging level at which this handler will be triggered
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- */
- public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true)
- {
- $this->filename = $filename;
- $this->maxFiles = (int) $maxFiles;
- $this->nextRotation = new \DateTime('tomorrow');
-
- parent::__construct($this->getTimedFilename(), $level, $bubble);
- }
-
- /**
- * {@inheritdoc}
- */
- public function close()
- {
- parent::close();
-
- if (true === $this->mustRotate) {
- $this->rotate();
- }
- }
-
- /**
- * {@inheritdoc}
- */
- protected function write(array $record)
- {
- // on the first record written, if the log is new, we should rotate (once per day)
- if (null === $this->mustRotate) {
- $this->mustRotate = !file_exists($this->url);
- }
-
- if ($this->nextRotation < $record['datetime']) {
- $this->mustRotate = true;
- $this->close();
- }
-
- parent::write($record);
- }
-
- /**
- * Rotates the files.
- */
- protected function rotate()
- {
- // update filename
- $this->url = $this->getTimedFilename();
- $this->nextRotation = new \DateTime('tomorrow');
-
- // skip GC of old logs if files are unlimited
- if (0 === $this->maxFiles) {
- return;
- }
-
- $fileInfo = pathinfo($this->filename);
- $glob = $fileInfo['dirname'].'/'.$fileInfo['filename'].'-*';
- if (!empty($fileInfo['extension'])) {
- $glob .= '.'.$fileInfo['extension'];
- }
- $iterator = new \GlobIterator($glob);
- $count = $iterator->count();
- if ($this->maxFiles >= $count) {
- // no files to remove
- return;
- }
-
- // Sorting the files by name to remove the older ones
- $array = iterator_to_array($iterator);
- usort($array, function($a, $b) {
- return strcmp($b->getFilename(), $a->getFilename());
- });
-
- foreach (array_slice($array, $this->maxFiles) as $file) {
- if ($file->isWritable()) {
- unlink($file->getRealPath());
- }
- }
- }
-
- protected function getTimedFilename()
- {
- $fileInfo = pathinfo($this->filename);
- $timedFilename = $fileInfo['dirname'].'/'.$fileInfo['filename'].'-'.date('Y-m-d');
- if (!empty($fileInfo['extension'])) {
- $timedFilename .= '.'.$fileInfo['extension'];
- }
-
- return $timedFilename;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-
-/**
- * Stores to any socket - uses fsockopen() or pfsockopen().
- *
- * @author Pablo de Leon Belloc <pablolb@gmail.com>
- * @see http://php.net/manual/en/function.fsockopen.php
- */
-class SocketHandler extends AbstractProcessingHandler
-{
- private $connectionString;
- private $connectionTimeout;
- private $resource;
- private $timeout = 0;
- private $persistent = false;
- private $errno;
- private $errstr;
-
- /**
- * @param string $connectionString Socket connection string
- * @param integer $level The minimum logging level at which this handler will be triggered
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- */
- public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true)
- {
- parent::__construct($level, $bubble);
- $this->connectionString = $connectionString;
- $this->connectionTimeout = (float) ini_get('default_socket_timeout');
- }
-
- /**
- * Connect (if necessary) and write to the socket
- *
- * @param array $record
- *
- * @throws \UnexpectedValueException
- * @throws \RuntimeException
- */
- public function write(array $record)
- {
- $this->connectIfNotConnected();
- $data = $this->generateDataStream($record);
- $this->writeToSocket($data);
- }
-
- /**
- * We will not close a PersistentSocket instance so it can be reused in other requests.
- */
- public function close()
- {
- if (!$this->isPersistent()) {
- $this->closeSocket();
- }
- }
-
- /**
- * Close socket, if open
- */
- public function closeSocket()
- {
- if (is_resource($this->resource)) {
- fclose($this->resource);
- $this->resource = null;
- }
- }
-
- /**
- * Set socket connection to nbe persistent. It only has effect before the connection is initiated.
- *
- * @param type $boolean
- */
- public function setPersistent($boolean)
- {
- $this->persistent = (boolean) $boolean;
- }
-
- /**
- * Set connection timeout. Only has effect before we connect.
- *
- * @param float $seconds
- *
- * @see http://php.net/manual/en/function.fsockopen.php
- */
- public function setConnectionTimeout($seconds)
- {
- $this->validateTimeout($seconds);
- $this->connectionTimeout = (float) $seconds;
- }
-
- /**
- * Set write timeout. Only has effect before we connect.
- *
- * @param float $seconds
- *
- * @see http://php.net/manual/en/function.stream-set-timeout.php
- */
- public function setTimeout($seconds)
- {
- $this->validateTimeout($seconds);
- $this->timeout = (float) $seconds;
- }
-
- /**
- * Get current connection string
- *
- * @return string
- */
- public function getConnectionString()
- {
- return $this->connectionString;
- }
-
- /**
- * Get persistent setting
- *
- * @return boolean
- */
- public function isPersistent()
- {
- return $this->persistent;
- }
-
- /**
- * Get current connection timeout setting
- *
- * @return float
- */
- public function getConnectionTimeout()
- {
- return $this->connectionTimeout;
- }
-
- /**
- * Get current in-transfer timeout
- *
- * @return float
- */
- public function getTimeout()
- {
- return $this->timeout;
- }
-
- /**
- * Check to see if the socket is currently available.
- *
- * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details.
- *
- * @return boolean
- */
- public function isConnected()
- {
- return is_resource($this->resource)
- && !feof($this->resource); // on TCP - other party can close connection.
- }
-
- /**
- * Wrapper to allow mocking
- */
- protected function pfsockopen()
- {
- return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout);
- }
-
- /**
- * Wrapper to allow mocking
- */
- protected function fsockopen()
- {
- return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout);
- }
-
- /**
- * Wrapper to allow mocking
- *
- * @see http://php.net/manual/en/function.stream-set-timeout.php
- */
- protected function streamSetTimeout()
- {
- $seconds = floor($this->timeout);
- $microseconds = round(($this->timeout - $seconds)*1e6);
-
- return stream_set_timeout($this->resource, $seconds, $microseconds);
- }
-
- /**
- * Wrapper to allow mocking
- */
- protected function fwrite($data)
- {
- return @fwrite($this->resource, $data);
- }
-
- /**
- * Wrapper to allow mocking
- */
- protected function streamGetMetadata()
- {
- return stream_get_meta_data($this->resource);
- }
-
- private function validateTimeout($value)
- {
- $ok = filter_var($value, FILTER_VALIDATE_FLOAT);
- if ($ok === false || $value < 0) {
- throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)");
- }
- }
-
- private function connectIfNotConnected()
- {
- if ($this->isConnected()) {
- return;
- }
- $this->connect();
- }
-
- protected function generateDataStream($record)
- {
- return (string) $record['formatted'];
- }
-
- private function connect()
- {
- $this->createSocketResource();
- $this->setSocketTimeout();
- }
-
- private function createSocketResource()
- {
- if ($this->isPersistent()) {
- $resource = $this->pfsockopen();
- } else {
- $resource = $this->fsockopen();
- }
- if (!$resource) {
- throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)");
- }
- $this->resource = $resource;
- }
-
- private function setSocketTimeout()
- {
- if (!$this->streamSetTimeout()) {
- throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()");
- }
- }
-
- private function writeToSocket($data)
- {
- $length = strlen($data);
- $sent = 0;
- while ($this->isConnected() && $sent < $length) {
- if (0 == $sent) {
- $chunk = $this->fwrite($data);
- } else {
- $chunk = $this->fwrite(substr($data, $sent));
- }
- if ($chunk === false) {
- throw new \RuntimeException("Could not write to socket");
- }
- $sent += $chunk;
- $socketInfo = $this->streamGetMetadata();
- if ($socketInfo['timed_out']) {
- throw new \RuntimeException("Write timed-out");
- }
- }
- if (!$this->isConnected() && $sent < $length) {
- throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)");
- }
- }
-
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-
-/**
- * Stores to any stream resource
- *
- * Can be used to store into php://stderr, remote and local files, etc.
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-class StreamHandler extends AbstractProcessingHandler
-{
- protected $stream;
- protected $url;
-
- /**
- * @param string $stream
- * @param integer $level The minimum logging level at which this handler will be triggered
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- */
- public function __construct($stream, $level = Logger::DEBUG, $bubble = true)
- {
- parent::__construct($level, $bubble);
- if (is_resource($stream)) {
- $this->stream = $stream;
- } else {
- $this->url = $stream;
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function close()
- {
- if (is_resource($this->stream)) {
- fclose($this->stream);
- }
- $this->stream = null;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function write(array $record)
- {
- if (null === $this->stream) {
- if (!$this->url) {
- throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
- }
- $errorMessage = null;
- set_error_handler(function ($code, $msg) use (&$errorMessage) {
- $errorMessage = preg_replace('{^fopen\(.*?\): }', '', $msg);
- });
- $this->stream = fopen($this->url, 'a');
- restore_error_handler();
- if (!is_resource($this->stream)) {
- $this->stream = null;
- throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$errorMessage, $this->url));
- }
- }
- fwrite($this->stream, (string) $record['formatted']);
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-
-/**
- * SwiftMailerHandler uses Swift_Mailer to send the emails
- *
- * @author Gyula Sallai
- */
-class SwiftMailerHandler extends MailHandler
-{
- protected $mailer;
- protected $message;
-
- /**
- * @param \Swift_Mailer $mailer The mailer to use
- * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced
- * @param integer $level The minimum logging level at which this handler will be triggered
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- */
- public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true)
- {
- parent::__construct($level, $bubble);
- $this->mailer = $mailer;
- if (!$message instanceof \Swift_Message && is_callable($message)) {
- $message = call_user_func($message);
- }
- if (!$message instanceof \Swift_Message) {
- throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it');
- }
- $this->message = $message;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function send($content, array $records)
- {
- $message = clone $this->message;
- $message->setBody($content);
-
- $this->mailer->send($message);
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-use Monolog\Formatter\LineFormatter;
-
-/**
- * Logs to syslog service.
- *
- * usage example:
- *
- * $log = new Logger('application');
- * $syslog = new SyslogHandler('myfacility', 'local6');
- * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%");
- * $syslog->setFormatter($formatter);
- * $log->pushHandler($syslog);
- *
- * @author Sven Paulus <sven@karlsruhe.org>
- */
-class SyslogHandler extends AbstractProcessingHandler
-{
- /**
- * Translates Monolog log levels to syslog log priorities.
- */
- private $logLevels = array(
- Logger::DEBUG => LOG_DEBUG,
- Logger::INFO => LOG_INFO,
- Logger::NOTICE => LOG_NOTICE,
- Logger::WARNING => LOG_WARNING,
- Logger::ERROR => LOG_ERR,
- Logger::CRITICAL => LOG_CRIT,
- Logger::ALERT => LOG_ALERT,
- Logger::EMERGENCY => LOG_EMERG,
- );
-
- /**
- * List of valid log facility names.
- */
- private $facilities = array(
- 'auth' => LOG_AUTH,
- 'authpriv' => LOG_AUTHPRIV,
- 'cron' => LOG_CRON,
- 'daemon' => LOG_DAEMON,
- 'kern' => LOG_KERN,
- 'lpr' => LOG_LPR,
- 'mail' => LOG_MAIL,
- 'news' => LOG_NEWS,
- 'syslog' => LOG_SYSLOG,
- 'user' => LOG_USER,
- 'uucp' => LOG_UUCP,
- );
-
- /**
- * @param string $ident
- * @param mixed $facility
- * @param integer $level The minimum logging level at which this handler will be triggered
- * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
- * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID
- */
- public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $logopts = LOG_PID)
- {
- parent::__construct($level, $bubble);
-
- if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
- $this->facilities['local0'] = LOG_LOCAL0;
- $this->facilities['local1'] = LOG_LOCAL1;
- $this->facilities['local2'] = LOG_LOCAL2;
- $this->facilities['local3'] = LOG_LOCAL3;
- $this->facilities['local4'] = LOG_LOCAL4;
- $this->facilities['local5'] = LOG_LOCAL5;
- $this->facilities['local6'] = LOG_LOCAL6;
- $this->facilities['local7'] = LOG_LOCAL7;
- }
-
- // convert textual description of facility to syslog constant
- if (array_key_exists(strtolower($facility), $this->facilities)) {
- $facility = $this->facilities[strtolower($facility)];
- } elseif (!in_array($facility, array_values($this->facilities), true)) {
- throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given');
- }
-
- if (!openlog($ident, $logopts, $facility)) {
- throw new \LogicException('Can\'t open syslog for ident "'.$ident.'" and facility "'.$facility.'"');
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function close()
- {
- closelog();
- }
-
- /**
- * {@inheritdoc}
- */
- protected function write(array $record)
- {
- syslog($this->logLevels[$record['level']], (string) $record['formatted']);
- }
-
- /**
- * {@inheritdoc}
- */
- protected function getDefaultFormatter()
- {
- return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%');
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Logger;
-
-/**
- * Used for testing purposes.
- *
- * It records all records and gives you access to them for verification.
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-class TestHandler extends AbstractProcessingHandler
-{
- protected $records = array();
- protected $recordsByLevel = array();
-
- public function getRecords()
- {
- return $this->records;
- }
-
- public function hasEmergency($record)
- {
- return $this->hasRecord($record, Logger::EMERGENCY);
- }
-
- public function hasAlert($record)
- {
- return $this->hasRecord($record, Logger::ALERT);
- }
-
- public function hasCritical($record)
- {
- return $this->hasRecord($record, Logger::CRITICAL);
- }
-
- public function hasError($record)
- {
- return $this->hasRecord($record, Logger::ERROR);
- }
-
- public function hasWarning($record)
- {
- return $this->hasRecord($record, Logger::WARNING);
- }
-
- public function hasNotice($record)
- {
- return $this->hasRecord($record, Logger::NOTICE);
- }
-
- public function hasInfo($record)
- {
- return $this->hasRecord($record, Logger::INFO);
- }
-
- public function hasDebug($record)
- {
- return $this->hasRecord($record, Logger::DEBUG);
- }
-
- public function hasEmergencyRecords()
- {
- return isset($this->recordsByLevel[Logger::EMERGENCY]);
- }
-
- public function hasAlertRecords()
- {
- return isset($this->recordsByLevel[Logger::ALERT]);
- }
-
- public function hasCriticalRecords()
- {
- return isset($this->recordsByLevel[Logger::CRITICAL]);
- }
-
- public function hasErrorRecords()
- {
- return isset($this->recordsByLevel[Logger::ERROR]);
- }
-
- public function hasWarningRecords()
- {
- return isset($this->recordsByLevel[Logger::WARNING]);
- }
-
- public function hasNoticeRecords()
- {
- return isset($this->recordsByLevel[Logger::NOTICE]);
- }
-
- public function hasInfoRecords()
- {
- return isset($this->recordsByLevel[Logger::INFO]);
- }
-
- public function hasDebugRecords()
- {
- return isset($this->recordsByLevel[Logger::DEBUG]);
- }
-
- protected function hasRecord($record, $level)
- {
- if (!isset($this->recordsByLevel[$level])) {
- return false;
- }
-
- if (is_array($record)) {
- $record = $record['message'];
- }
-
- foreach ($this->recordsByLevel[$level] as $rec) {
- if ($rec['message'] === $record) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function write(array $record)
- {
- $this->recordsByLevel[$record['level']][] = $record;
- $this->records[] = $record;
- }
-}
+++ /dev/null
-<?php
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Handler;
-
-use Monolog\Formatter\NormalizerFormatter;
-use Monolog\Logger;
-
-/**
- * Handler sending logs to Zend Monitor
- *
- * @author Christian Bergau <cbergau86@gmail.com>
- */
-class ZendMonitorHandler extends AbstractProcessingHandler
-{
- /**
- * Monolog level / ZendMonitor Custom Event priority map
- *
- * @var array
- */
- protected $levelMap = array(
- Logger::DEBUG => 1,
- Logger::INFO => 2,
- Logger::NOTICE => 3,
- Logger::WARNING => 4,
- Logger::ERROR => 5,
- Logger::CRITICAL => 6,
- Logger::ALERT => 7,
- Logger::EMERGENCY => 0,
- );
-
- /**
- * Construct
- *
- * @param int $level
- * @param bool $bubble
- * @throws MissingExtensionException
- */
- public function __construct($level = Logger::DEBUG, $bubble = true)
- {
- if (!function_exists('zend_monitor_custom_event')) {
- throw new MissingExtensionException('You must have Zend Server installed in order to use this handler');
- }
- parent::__construct($level, $bubble);
- }
-
- /**
- * {@inheritdoc}
- */
- protected function write(array $record)
- {
- $this->writeZendMonitorCustomEvent(
- $this->levelMap[$record['level']],
- $record['message'],
- $record['formatted']
- );
- }
-
- /**
- * Write a record to Zend Monitor
- *
- * @param int $level
- * @param string $message
- * @param array $formatted
- */
- protected function writeZendMonitorCustomEvent($level, $message, $formatted)
- {
- zend_monitor_custom_event($level, $message, $formatted);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getDefaultFormatter()
- {
- return new NormalizerFormatter();
- }
-
- /**
- * Get the level map
- *
- * @return array
- */
- public function getLevelMap()
- {
- return $this->levelMap;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog;
-
-use Monolog\Handler\HandlerInterface;
-use Monolog\Handler\StreamHandler;
-use Psr\Log\LoggerInterface;
-use Psr\Log\InvalidArgumentException;
-
-/**
- * Monolog log channel
- *
- * It contains a stack of Handlers and a stack of Processors,
- * and uses them to store records that are added to it.
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-class Logger implements LoggerInterface
-{
- /**
- * Detailed debug information
- */
- const DEBUG = 100;
-
- /**
- * Interesting events
- *
- * Examples: User logs in, SQL logs.
- */
- const INFO = 200;
-
- /**
- * Uncommon events
- */
- const NOTICE = 250;
-
- /**
- * Exceptional occurrences that are not errors
- *
- * Examples: Use of deprecated APIs, poor use of an API,
- * undesirable things that are not necessarily wrong.
- */
- const WARNING = 300;
-
- /**
- * Runtime errors
- */
- const ERROR = 400;
-
- /**
- * Critical conditions
- *
- * Example: Application component unavailable, unexpected exception.
- */
- const CRITICAL = 500;
-
- /**
- * Action must be taken immediately
- *
- * Example: Entire website down, database unavailable, etc.
- * This should trigger the SMS alerts and wake you up.
- */
- const ALERT = 550;
-
- /**
- * Urgent alert.
- */
- const EMERGENCY = 600;
-
- protected static $levels = array(
- 100 => 'DEBUG',
- 200 => 'INFO',
- 250 => 'NOTICE',
- 300 => 'WARNING',
- 400 => 'ERROR',
- 500 => 'CRITICAL',
- 550 => 'ALERT',
- 600 => 'EMERGENCY',
- );
-
- /**
- * @var DateTimeZone
- */
- protected static $timezone;
-
- protected $name;
-
- /**
- * The handler stack
- *
- * @var array of Monolog\Handler\HandlerInterface
- */
- protected $handlers;
-
- /**
- * Processors that will process all log records
- *
- * To process records of a single handler instead, add the processor on that specific handler
- *
- * @var array of callables
- */
- protected $processors;
-
- /**
- * @param string $name The logging channel
- * @param array $handlers Optional stack of handlers, the first one in the array is called first, etc.
- * @param array $processors Optional array of processors
- */
- public function __construct($name, array $handlers = array(), array $processors = array())
- {
- $this->name = $name;
- $this->handlers = $handlers;
- $this->processors = $processors;
- }
-
- /**
- * @return string
- */
- public function getName()
- {
- return $this->name;
- }
-
- /**
- * Pushes a handler on to the stack.
- *
- * @param HandlerInterface $handler
- */
- public function pushHandler(HandlerInterface $handler)
- {
- array_unshift($this->handlers, $handler);
- }
-
- /**
- * Pops a handler from the stack
- *
- * @return HandlerInterface
- */
- public function popHandler()
- {
- if (!$this->handlers) {
- throw new \LogicException('You tried to pop from an empty handler stack.');
- }
-
- return array_shift($this->handlers);
- }
-
- /**
- * Adds a processor on to the stack.
- *
- * @param callable $callback
- */
- public function pushProcessor($callback)
- {
- if (!is_callable($callback)) {
- throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given');
- }
- array_unshift($this->processors, $callback);
- }
-
- /**
- * Removes the processor on top of the stack and returns it.
- *
- * @return callable
- */
- public function popProcessor()
- {
- if (!$this->processors) {
- throw new \LogicException('You tried to pop from an empty processor stack.');
- }
-
- return array_shift($this->processors);
- }
-
- /**
- * Adds a log record.
- *
- * @param integer $level The logging level
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function addRecord($level, $message, array $context = array())
- {
- if (!$this->handlers) {
- $this->pushHandler(new StreamHandler('php://stderr', static::DEBUG));
- }
-
- if (!static::$timezone) {
- static::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC');
- }
-
- $record = array(
- 'message' => (string) $message,
- 'context' => $context,
- 'level' => $level,
- 'level_name' => static::getLevelName($level),
- 'channel' => $this->name,
- 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), static::$timezone)->setTimezone(static::$timezone),
- 'extra' => array(),
- );
- // check if any handler will handle this message
- $handlerKey = null;
- foreach ($this->handlers as $key => $handler) {
- if ($handler->isHandling($record)) {
- $handlerKey = $key;
- break;
- }
- }
- // none found
- if (null === $handlerKey) {
- return false;
- }
-
- // found at least one, process message and dispatch it
- foreach ($this->processors as $processor) {
- $record = call_user_func($processor, $record);
- }
- while (isset($this->handlers[$handlerKey]) &&
- false === $this->handlers[$handlerKey]->handle($record)) {
- $handlerKey++;
- }
-
- return true;
- }
-
- /**
- * Adds a log record at the DEBUG level.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function addDebug($message, array $context = array())
- {
- return $this->addRecord(static::DEBUG, $message, $context);
- }
-
- /**
- * Adds a log record at the INFO level.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function addInfo($message, array $context = array())
- {
- return $this->addRecord(static::INFO, $message, $context);
- }
-
- /**
- * Adds a log record at the NOTICE level.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function addNotice($message, array $context = array())
- {
- return $this->addRecord(static::NOTICE, $message, $context);
- }
-
- /**
- * Adds a log record at the WARNING level.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function addWarning($message, array $context = array())
- {
- return $this->addRecord(static::WARNING, $message, $context);
- }
-
- /**
- * Adds a log record at the ERROR level.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function addError($message, array $context = array())
- {
- return $this->addRecord(static::ERROR, $message, $context);
- }
-
- /**
- * Adds a log record at the CRITICAL level.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function addCritical($message, array $context = array())
- {
- return $this->addRecord(static::CRITICAL, $message, $context);
- }
-
- /**
- * Adds a log record at the ALERT level.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function addAlert($message, array $context = array())
- {
- return $this->addRecord(static::ALERT, $message, $context);
- }
-
- /**
- * Adds a log record at the EMERGENCY level.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function addEmergency($message, array $context = array())
- {
- return $this->addRecord(static::EMERGENCY, $message, $context);
- }
-
- /**
- * Gets the name of the logging level.
- *
- * @param integer $level
- * @return string
- */
- public static function getLevelName($level)
- {
- if (!isset(static::$levels[$level])) {
- throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels)));
- }
-
- return static::$levels[$level];
- }
-
- /**
- * Checks whether the Logger has a handler that listens on the given level
- *
- * @param integer $level
- * @return Boolean
- */
- public function isHandling($level)
- {
- $record = array(
- 'level' => $level,
- );
-
- foreach ($this->handlers as $key => $handler) {
- if ($handler->isHandling($record)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Adds a log record at an arbitrary level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param mixed $level The log level
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function log($level, $message, array $context = array())
- {
- if (is_string($level) && defined(__CLASS__.'::'.strtoupper($level))) {
- $level = constant(__CLASS__.'::'.strtoupper($level));
- }
-
- return $this->addRecord($level, $message, $context);
- }
-
- /**
- * Adds a log record at the DEBUG level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function debug($message, array $context = array())
- {
- return $this->addRecord(static::DEBUG, $message, $context);
- }
-
- /**
- * Adds a log record at the INFO level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function info($message, array $context = array())
- {
- return $this->addRecord(static::INFO, $message, $context);
- }
-
- /**
- * Adds a log record at the INFO level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function notice($message, array $context = array())
- {
- return $this->addRecord(static::NOTICE, $message, $context);
- }
-
- /**
- * Adds a log record at the WARNING level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function warn($message, array $context = array())
- {
- return $this->addRecord(static::WARNING, $message, $context);
- }
-
- /**
- * Adds a log record at the WARNING level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function warning($message, array $context = array())
- {
- return $this->addRecord(static::WARNING, $message, $context);
- }
-
- /**
- * Adds a log record at the ERROR level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function err($message, array $context = array())
- {
- return $this->addRecord(static::ERROR, $message, $context);
- }
-
- /**
- * Adds a log record at the ERROR level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function error($message, array $context = array())
- {
- return $this->addRecord(static::ERROR, $message, $context);
- }
-
- /**
- * Adds a log record at the CRITICAL level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function crit($message, array $context = array())
- {
- return $this->addRecord(static::CRITICAL, $message, $context);
- }
-
- /**
- * Adds a log record at the CRITICAL level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function critical($message, array $context = array())
- {
- return $this->addRecord(static::CRITICAL, $message, $context);
- }
-
- /**
- * Adds a log record at the ALERT level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function alert($message, array $context = array())
- {
- return $this->addRecord(static::ALERT, $message, $context);
- }
-
- /**
- * Adds a log record at the EMERGENCY level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function emerg($message, array $context = array())
- {
- return $this->addRecord(static::EMERGENCY, $message, $context);
- }
-
- /**
- * Adds a log record at the EMERGENCY level.
- *
- * This method allows for compatibility with common interfaces.
- *
- * @param string $message The log message
- * @param array $context The log context
- * @return Boolean Whether the record has been processed
- */
- public function emergency($message, array $context = array())
- {
- return $this->addRecord(static::EMERGENCY, $message, $context);
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Processor;
-
-/**
- * Injects line/file:class/function where the log message came from
- *
- * Warning: This only works if the handler processes the logs directly.
- * If you put the processor on a handler that is behind a FingersCrossedHandler
- * for example, the processor will only be called once the trigger level is reached,
- * and all the log records will have the same file/line/.. data from the call that
- * triggered the FingersCrossedHandler.
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-class IntrospectionProcessor
-{
- /**
- * @param array $record
- * @return array
- */
- public function __invoke(array $record)
- {
- $trace = debug_backtrace();
-
- // skip first since it's always the current method
- array_shift($trace);
- // the call_user_func call is also skipped
- array_shift($trace);
-
- $i = 0;
- while (isset($trace[$i]['class']) && false !== strpos($trace[$i]['class'], 'Monolog\\')) {
- $i++;
- }
-
- // we should have the call source now
- $record['extra'] = array_merge(
- $record['extra'],
- array(
- 'file' => isset($trace[$i-1]['file']) ? $trace[$i-1]['file'] : null,
- 'line' => isset($trace[$i-1]['line']) ? $trace[$i-1]['line'] : null,
- 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null,
- 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null,
- )
- );
-
- return $record;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Processor;
-
-/**
- * Injects memory_get_peak_usage in all records
- *
- * @see Monolog\Processor\MemoryProcessor::__construct() for options
- * @author Rob Jensen
- */
-class MemoryPeakUsageProcessor extends MemoryProcessor
-{
- /**
- * @param array $record
- * @return array
- */
- public function __invoke(array $record)
- {
- $bytes = memory_get_peak_usage($this->realUsage);
- $formatted = self::formatBytes($bytes);
-
- $record['extra'] = array_merge(
- $record['extra'],
- array(
- 'memory_peak_usage' => $formatted,
- )
- );
-
- return $record;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Processor;
-
-/**
- * Some methods that are common for all memory processors
- *
- * @author Rob Jensen
- */
-abstract class MemoryProcessor
-{
- protected $realUsage;
-
- /**
- * @param boolean $realUsage
- */
- public function __construct($realUsage = true)
- {
- $this->realUsage = (boolean) $realUsage;
- }
-
- /**
- * Formats bytes into a human readable string
- *
- * @param int $bytes
- * @return string
- */
- protected static function formatBytes($bytes)
- {
- $bytes = (int) $bytes;
-
- if ($bytes > 1024*1024) {
- return round($bytes/1024/1024, 2).' MB';
- } elseif ($bytes > 1024) {
- return round($bytes/1024, 2).' KB';
- }
-
- return $bytes . ' B';
- }
-
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Processor;
-
-/**
- * Injects memory_get_usage in all records
- *
- * @see Monolog\Processor\MemoryProcessor::__construct() for options
- * @author Rob Jensen
- */
-class MemoryUsageProcessor extends MemoryProcessor
-{
- /**
- * @param array $record
- * @return array
- */
- public function __invoke(array $record)
- {
- $bytes = memory_get_usage($this->realUsage);
- $formatted = self::formatBytes($bytes);
-
- $record['extra'] = array_merge(
- $record['extra'],
- array(
- 'memory_usage' => $formatted,
- )
- );
-
- return $record;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Processor;
-
-/**
- * Processes a record's message according to PSR-3 rules
- *
- * It replaces {foo} with the value from $context['foo']
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-class PsrLogMessageProcessor
-{
- /**
- * @param array $record
- * @return array
- */
- public function __invoke(array $record)
- {
- if (false === strpos($record['message'], '{')) {
- return $record;
- }
-
- $replacements = array();
- foreach ($record['context'] as $key => $val) {
- $replacements['{'.$key.'}'] = $val;
- }
-
- $record['message'] = strtr($record['message'], $replacements);
-
- return $record;
- }
-}
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Monolog package.
- *
- * (c) Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Monolog\Processor;
-
-/**
- * Injects url/method and remote IP of the current web request in all records
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- */
-class WebProcessor
-{
- protected $serverData;
-
- /**
- * @param mixed $serverData array or object w/ ArrayAccess that provides access to the $_SERVER data
- */
- public function __construct($serverData = null)
- {
- if (null === $serverData) {
- $this->serverData =& $_SERVER;
- } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) {
- $this->serverData = $serverData;
- } else {
- throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.');
- }
- }
-
- /**
- * @param array $record
- * @return array
- */
- public function __invoke(array $record)
- {
- // skip processing if for some reason request data
- // is not present (CLI or wonky SAPIs)
- if (!isset($this->serverData['REQUEST_URI'])) {
- return $record;
- }
-
- $record['extra'] = array_merge(
- $record['extra'],
- array(
- 'url' => $this->serverData['REQUEST_URI'],
- 'ip' => isset($this->serverData['REMOTE_ADDR']) ? $this->serverData['REMOTE_ADDR'] : null,
- 'http_method' => isset($this->serverData['REQUEST_METHOD']) ? $this->serverData['REQUEST_METHOD'] : null,
- 'server' => isset($this->serverData['SERVER_NAME']) ? $this->serverData['SERVER_NAME'] : null,
- 'referrer' => isset($this->serverData['HTTP_REFERER']) ? $this->serverData['HTTP_REFERER'] : null,
- )
- );
-
- return $record;
- }
-}
+++ /dev/null
-<?php
-
-namespace Psr\Log;
-
-/**
- * This is a simple Logger implementation that other Loggers can inherit from.
- *
- * It simply delegates all log-level-specific methods to the `log` method to
- * reduce boilerplate code that a simple Logger that does the same thing with
- * messages regardless of the error level has to implement.
- */
-abstract class AbstractLogger implements LoggerInterface
-{
- /**
- * System is unusable.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function emergency($message, array $context = array())
- {
- $this->log(LogLevel::EMERGENCY, $message, $context);
- }
-
- /**
- * Action must be taken immediately.
- *
- * Example: Entire website down, database unavailable, etc. This should
- * trigger the SMS alerts and wake you up.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function alert($message, array $context = array())
- {
- $this->log(LogLevel::ALERT, $message, $context);
- }
-
- /**
- * Critical conditions.
- *
- * Example: Application component unavailable, unexpected exception.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function critical($message, array $context = array())
- {
- $this->log(LogLevel::CRITICAL, $message, $context);
- }
-
- /**
- * Runtime errors that do not require immediate action but should typically
- * be logged and monitored.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function error($message, array $context = array())
- {
- $this->log(LogLevel::ERROR, $message, $context);
- }
-
- /**
- * Exceptional occurrences that are not errors.
- *
- * Example: Use of deprecated APIs, poor use of an API, undesirable things
- * that are not necessarily wrong.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function warning($message, array $context = array())
- {
- $this->log(LogLevel::WARNING, $message, $context);
- }
-
- /**
- * Normal but significant events.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function notice($message, array $context = array())
- {
- $this->log(LogLevel::NOTICE, $message, $context);
- }
-
- /**
- * Interesting events.
- *
- * Example: User logs in, SQL logs.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function info($message, array $context = array())
- {
- $this->log(LogLevel::INFO, $message, $context);
- }
-
- /**
- * Detailed debug information.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function debug($message, array $context = array())
- {
- $this->log(LogLevel::DEBUG, $message, $context);
- }
-}
+++ /dev/null
-<?php
-
-namespace Psr\Log;
-
-class InvalidArgumentException extends \InvalidArgumentException
-{
-}
+++ /dev/null
-<?php
-
-namespace Psr\Log;
-
-/**
- * Describes log levels
- */
-class LogLevel
-{
- const EMERGENCY = 'emergency';
- const ALERT = 'alert';
- const CRITICAL = 'critical';
- const ERROR = 'error';
- const WARNING = 'warning';
- const NOTICE = 'notice';
- const INFO = 'info';
- const DEBUG = 'debug';
-}
+++ /dev/null
-<?php
-
-namespace Psr\Log;
-
-/**
- * Describes a logger-aware instance
- */
-interface LoggerAwareInterface
-{
- /**
- * Sets a logger instance on the object
- *
- * @param LoggerInterface $logger
- * @return null
- */
- public function setLogger(LoggerInterface $logger);
-}
+++ /dev/null
-<?php
-
-namespace Psr\Log;
-
-/**
- * Basic Implementation of LoggerAwareInterface.
- */
-trait LoggerAwareTrait
-{
- /** @var LoggerInterface */
- protected $logger;
-
- /**
- * Sets a logger.
- *
- * @param LoggerInterface $logger
- */
- public function setLogger(LoggerInterface $logger)
- {
- $this->logger = $logger;
- }
-}
+++ /dev/null
-<?php
-
-namespace Psr\Log;
-
-/**
- * Describes a logger instance
- *
- * The message MUST be a string or object implementing __toString().
- *
- * The message MAY contain placeholders in the form: {foo} where foo
- * will be replaced by the context data in key "foo".
- *
- * The context array can contain arbitrary data, the only assumption that
- * can be made by implementors is that if an Exception instance is given
- * to produce a stack trace, it MUST be in a key named "exception".
- *
- * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
- * for the full interface specification.
- */
-interface LoggerInterface
-{
- /**
- * System is unusable.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function emergency($message, array $context = array());
-
- /**
- * Action must be taken immediately.
- *
- * Example: Entire website down, database unavailable, etc. This should
- * trigger the SMS alerts and wake you up.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function alert($message, array $context = array());
-
- /**
- * Critical conditions.
- *
- * Example: Application component unavailable, unexpected exception.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function critical($message, array $context = array());
-
- /**
- * Runtime errors that do not require immediate action but should typically
- * be logged and monitored.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function error($message, array $context = array());
-
- /**
- * Exceptional occurrences that are not errors.
- *
- * Example: Use of deprecated APIs, poor use of an API, undesirable things
- * that are not necessarily wrong.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function warning($message, array $context = array());
-
- /**
- * Normal but significant events.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function notice($message, array $context = array());
-
- /**
- * Interesting events.
- *
- * Example: User logs in, SQL logs.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function info($message, array $context = array());
-
- /**
- * Detailed debug information.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function debug($message, array $context = array());
-
- /**
- * Logs with an arbitrary level.
- *
- * @param mixed $level
- * @param string $message
- * @param array $context
- * @return null
- */
- public function log($level, $message, array $context = array());
-}
+++ /dev/null
-<?php
-
-namespace Psr\Log;
-
-/**
- * This is a simple Logger trait that classes unable to extend AbstractLogger
- * (because they extend another class, etc) can include.
- *
- * It simply delegates all log-level-specific methods to the `log` method to
- * reduce boilerplate code that a simple Logger that does the same thing with
- * messages regardless of the error level has to implement.
- */
-trait LoggerTrait
-{
- /**
- * System is unusable.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function emergency($message, array $context = array())
- {
- $this->log(LogLevel::EMERGENCY, $message, $context);
- }
-
- /**
- * Action must be taken immediately.
- *
- * Example: Entire website down, database unavailable, etc. This should
- * trigger the SMS alerts and wake you up.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function alert($message, array $context = array())
- {
- $this->log(LogLevel::ALERT, $message, $context);
- }
-
- /**
- * Critical conditions.
- *
- * Example: Application component unavailable, unexpected exception.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function critical($message, array $context = array())
- {
- $this->log(LogLevel::CRITICAL, $message, $context);
- }
-
- /**
- * Runtime errors that do not require immediate action but should typically
- * be logged and monitored.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function error($message, array $context = array())
- {
- $this->log(LogLevel::ERROR, $message, $context);
- }
-
- /**
- * Exceptional occurrences that are not errors.
- *
- * Example: Use of deprecated APIs, poor use of an API, undesirable things
- * that are not necessarily wrong.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function warning($message, array $context = array())
- {
- $this->log(LogLevel::WARNING, $message, $context);
- }
-
- /**
- * Normal but significant events.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function notice($message, array $context = array())
- {
- $this->log(LogLevel::NOTICE, $message, $context);
- }
-
- /**
- * Interesting events.
- *
- * Example: User logs in, SQL logs.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function info($message, array $context = array())
- {
- $this->log(LogLevel::INFO, $message, $context);
- }
-
- /**
- * Detailed debug information.
- *
- * @param string $message
- * @param array $context
- * @return null
- */
- public function debug($message, array $context = array())
- {
- $this->log(LogLevel::DEBUG, $message, $context);
- }
-
- /**
- * Logs with an arbitrary level.
- *
- * @param mixed $level
- * @param string $message
- * @param array $context
- * @return null
- */
- abstract public function log($level, $message, array $context = array());
-}
+++ /dev/null
-<?php
-
-namespace Psr\Log;
-
-/**
- * This Logger can be used to avoid conditional log calls
- *
- * Logging should always be optional, and if no logger is provided to your
- * library creating a NullLogger instance to have something to throw logs at
- * is a good way to avoid littering your code with `if ($this->logger) { }`
- * blocks.
- */
-class NullLogger extends AbstractLogger
-{
- /**
- * Logs with an arbitrary level.
- *
- * @param mixed $level
- * @param string $message
- * @param array $context
- * @return null
- */
- public function log($level, $message, array $context = array())
- {
- // noop
- }
-}
+++ /dev/null
-<?php
-
-namespace Psr\Log\Test;
-
-use Psr\Log\LogLevel;
-
-/**
- * Provides a base test class for ensuring compliance with the LoggerInterface
- *
- * Implementors can extend the class and implement abstract methods to run this as part of their test suite
- */
-abstract class LoggerInterfaceTest extends \PHPUnit_Framework_TestCase
-{
- /**
- * @return LoggerInterface
- */
- abstract function getLogger();
-
- /**
- * This must return the log messages in order with a simple formatting: "<LOG LEVEL> <MESSAGE>"
- *
- * Example ->error('Foo') would yield "error Foo"
- *
- * @return string[]
- */
- abstract function getLogs();
-
- public function testImplements()
- {
- $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger());
- }
-
- /**
- * @dataProvider provideLevelsAndMessages
- */
- public function testLogsAtAllLevels($level, $message)
- {
- $logger = $this->getLogger();
- $logger->{$level}($message, array('user' => 'Bob'));
- $logger->log($level, $message, array('user' => 'Bob'));
-
- $expected = array(
- $level.' message of level '.$level.' with context: Bob',
- $level.' message of level '.$level.' with context: Bob',
- );
- $this->assertEquals($expected, $this->getLogs());
- }
-
- public function provideLevelsAndMessages()
- {
- return array(
- LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'),
- LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'),
- LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'),
- LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'),
- LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'),
- LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'),
- LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'),
- LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'),
- );
- }
-
- /**
- * @expectedException Psr\Log\InvalidArgumentException
- */
- public function testThrowsOnInvalidLevel()
- {
- $logger = $this->getLogger();
- $logger->log('invalid level', 'Foo');
- }
-
- public function testContextReplacement()
- {
- $logger = $this->getLogger();
- $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar'));
-
- $expected = array('info {Message {nothing} Bob Bar a}');
- $this->assertEquals($expected, $this->getLogs());
- }
-
- public function testObjectCastToString()
- {
- $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString'));
- $dummy->expects($this->once())
- ->method('__toString')
- ->will($this->returnValue('DUMMY'));
-
- $this->getLogger()->warning($dummy);
- }
-
- public function testContextCanContainAnything()
- {
- $context = array(
- 'bool' => true,
- 'null' => null,
- 'string' => 'Foo',
- 'int' => 0,
- 'float' => 0.5,
- 'nested' => array('with object' => new DummyTest),
- 'object' => new \DateTime,
- 'resource' => fopen('php://memory', 'r'),
- );
-
- $this->getLogger()->warning('Crazy context data', $context);
- }
-
- public function testContextExceptionKeyCanBeExceptionOrOtherValues()
- {
- $this->getLogger()->warning('Random message', array('exception' => 'oops'));
- $this->getLogger()->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail')));
- }
-}
-
-class DummyTest
-{
-}
\ No newline at end of file
--- /dev/null
+vendor/
+composer.lock
+phpunit.xml
/**
* Registers this instance as an autoloader.
*
- * @param Boolean $prepend Whether to prepend the autoloader or not
+ * @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
*
* @param string $class The name of the class
*
- * @return Boolean|null True, if loaded
+ * @return bool|null True, if loaded
*/
public function loadClass($class)
{
--- /dev/null
+CHANGELOG
+=========
+
+2.4.0
+-----
+
+ * deprecated the DebugClassLoader as it has been moved to the Debug component instead
+
+2.3.0
+-----
+
+ * added a WinCacheClassLoader for WinCache
+
+2.1.0
+-----
+
+ * added a DebugClassLoader able to wrap any autoloader providing a findFile
+ method
+ * added a new ApcClassLoader and XcacheClassLoader using composition to wrap
+ other loaders
+ * added a new ClassLoader which does not distinguish between namespaced and
+ pear-like classes (as the PEAR convention is a subset of PSR-0) and
+ supports using Composer's namespace maps
+ * added a class map generator
+ * added support for loading globally-installed PEAR packages
* @param array $classes An array of classes to load
* @param string $cacheDir A cache directory
* @param string $name The cache name prefix
- * @param Boolean $autoReload Whether to flush the cache when the cache is stale or not
- * @param Boolean $adaptive Whether to remove already declared classes or not
+ * @param bool $autoReload Whether to flush the cache when the cache is stale or not
+ * @param bool $adaptive Whether to remove already declared classes or not
* @param string $extension File extension of the resulting file
*
* @throws \InvalidArgumentException When class can't be loaded
$classes = array_diff($classes, $declared);
// the cache is different depending on which classes are already declared
- $name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5);
+ $name = $name.'-'.substr(hash('sha256', implode('|', $classes)), 0, 5);
}
$classes = array_unique($classes);
*/
public static function enableTokenizer($bool)
{
- self::$useTokenizer = (Boolean) $bool;
+ self::$useTokenizer = (bool) $bool;
}
/**
/**
* Turns on searching the include for class files.
*
- * @param Boolean $useIncludePath
+ * @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
- * @return Boolean
+ * @return bool
*/
public function getUseIncludePath()
{
/**
* Registers this instance as an autoloader.
*
- * @param Boolean $prepend Whether to prepend the autoloader or not
+ * @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
*
* @param string $class The name of the class
*
- * @return Boolean|null True, if loaded
+ * @return bool|null True, if loaded
*/
public function loadClass($class)
{
$classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className).'.php';
foreach ($this->prefixes as $prefix => $dirs) {
- if (0 === strpos($class, $prefix)) {
+ if ($class === strstr($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) {
return $dir.DIRECTORY_SEPARATOR.$classPath;
namespace Symfony\Component\ClassLoader;
+if (!defined('T_TRAIT')) {
+ define('T_TRAIT', 0);
+}
+
/**
* ClassMapGenerator
*
/**
* Iterate over all files in the given directory searching for classes
*
- * @param Iterator|string $dir The directory to search in or an iterator
+ * @param \Iterator|string $dir The directory to search in or an iterator
*
* @return array A class map array
*/
{
$contents = file_get_contents($path);
$tokens = token_get_all($contents);
- $T_TRAIT = version_compare(PHP_VERSION, '5.4', '<') ? -1 : T_TRAIT;
$classes = array();
break;
case T_CLASS:
case T_INTERFACE:
- case $T_TRAIT:
+ case T_TRAIT:
// Find the classname
while (($t = $tokens[++$i]) && is_array($t)) {
if (T_STRING === $t[0]) {
* @author Christophe Coevoet <stof@notk.org>
*
* @api
+ *
+ * @deprecated Deprecated since version 2.4, to be removed in 3.0. Use the DebugClassLoader provided by the Debug component instead.
*/
class DebugClassLoader
{
$this->classFinder = $classFinder;
}
+ /**
+ * Gets the wrapped class loader.
+ *
+ * @return object a class loader instance
+ */
+ public function getClassLoader()
+ {
+ return $this->classFinder;
+ }
+
/**
* Replaces all autoloaders implementing a findFile method by a DebugClassLoader wrapper.
*/
*
* @param string $class The name of the class
*
- * @return Boolean|null True, if loaded
+ * @return bool|null True, if loaded
*
* @throws \RuntimeException
*/
}
/**
- * {@inheritDoc}
+ * {@inheritdoc}
*/
public function loadClass($class)
{
--- /dev/null
+Copyright (c) 2004-2014 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.
/**
* Registers this instance as an autoloader.
*
- * @param Boolean $prepend Whether to prepend the autoloader or not
+ * @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader;
+
+/**
+ * A PSR-4 compatible class loader.
+ *
+ * See http://www.php-fig.org/psr/psr-4/
+ *
+ * @author Alexander M. Turek <me@derrabus.de>
+ */
+class Psr4ClassLoader
+{
+ /**
+ * @var array
+ */
+ private $prefixes = array();
+
+ /**
+ * @param string $prefix
+ * @param string $baseDir
+ */
+ public function addPrefix($prefix, $baseDir)
+ {
+ $prefix = trim($prefix, '\\').'\\';
+ $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+ $this->prefixes[] = array($prefix, $baseDir);
+ }
+
+ /**
+ * @param string $class
+ *
+ * @return string|null
+ */
+ public function findFile($class)
+ {
+ $class = ltrim($class, '\\');
+
+ foreach ($this->prefixes as $current) {
+ list($currentPrefix, $currentBaseDir) = $current;
+ if (0 === strpos($class, $currentPrefix)) {
+ $classWithoutPrefix = substr($class, strlen($currentPrefix));
+ $file = $currentBaseDir . str_replace('\\', DIRECTORY_SEPARATOR, $classWithoutPrefix) . '.php';
+ if (file_exists($file)) {
+ return $file;
+ }
+ }
+ }
+ }
+
+ /**
+ * @param string $class
+ *
+ * @return bool
+ */
+ public function loadClass($class)
+ {
+ $file = $this->findFile($class);
+ if (null !== $file) {
+ require $file;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+ }
+
+ /**
+ * Removes this instance from the registered autoloaders.
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+ }
+}
--- /dev/null
+ClassLoader Component
+=====================
+
+ClassLoader loads your project classes automatically if they follow some
+standard PHP conventions.
+
+The Universal ClassLoader is able to autoload classes that implement the PSR-0
+standard or the PEAR naming convention.
+
+First, register the autoloader:
+
+ require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
+
+ use Symfony\Component\ClassLoader\UniversalClassLoader;
+
+ $loader = new UniversalClassLoader();
+ $loader->register();
+
+Then, register some namespaces with the `registerNamespace()` method:
+
+ $loader->registerNamespace('Symfony', __DIR__.'/src');
+ $loader->registerNamespace('Monolog', __DIR__.'/vendor/monolog/src');
+
+The `registerNamespace()` method takes a namespace prefix and a path where to
+look for the classes as arguments.
+
+You can also register a sub-namespaces:
+
+ $loader->registerNamespace('Doctrine\\Common', __DIR__.'/vendor/doctrine-common/lib');
+
+The order of registration is significant and the first registered namespace
+takes precedence over later registered one.
+
+You can also register more than one path for a given namespace:
+
+ $loader->registerNamespace('Symfony', array(__DIR__.'/src', __DIR__.'/symfony/src'));
+
+Alternatively, you can use the `registerNamespaces()` method to register more
+than one namespace at once:
+
+ $loader->registerNamespaces(array(
+ 'Symfony' => array(__DIR__.'/src', __DIR__.'/symfony/src'),
+ 'Doctrine\\Common' => __DIR__.'/vendor/doctrine-common/lib',
+ 'Doctrine' => __DIR__.'/vendor/doctrine/lib',
+ 'Monolog' => __DIR__.'/vendor/monolog/src',
+ ));
+
+For better performance, you can use the APC based version of the universal
+class loader:
+
+ require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
+ require_once __DIR__.'/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php';
+
+ use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
+
+ $loader = new ApcUniversalClassLoader('apc.prefix.');
+
+Furthermore, the component provides tools to aggregate classes into a single
+file, which is especially useful to improve performance on servers that do not
+provide byte caches.
+
+Resources
+---------
+
+You can run the unit tests with the following command:
+
+ $ cd path/to/Symfony/Component/ClassLoader/
+ $ composer.phar install
+ $ phpunit
public function getLoadClassTests()
{
return array(
- array('\\Apc\\Namespaced\\Foo', '\\Apc\\Namespaced\\Foo', '->loadClass() loads Apc\Namespaced\Foo class'),
+ array('\\Apc\\Namespaced\\Foo', 'Apc\\Namespaced\\Foo', '->loadClass() loads Apc\Namespaced\Foo class'),
array('Apc_Pearlike_Foo', 'Apc_Pearlike_Foo', '->loadClass() loads Apc_Pearlike_Foo class'),
- array('\\Apc\\Namespaced\\Bar', '\\Apc\\Namespaced\\Bar', '->loadClass() loads Apc\Namespaced\Bar class with a leading slash'),
- array('Apc_Pearlike_Bar', '\\Apc_Pearlike_Bar', '->loadClass() loads Apc_Pearlike_Bar class with a leading slash'),
);
}
public function getLoadClassFromFallbackTests()
{
return array(
- array('\\Apc\\Namespaced\\Baz', '\\Apc\\Namespaced\\Baz', '->loadClass() loads Apc\Namespaced\Baz class'),
+ array('\\Apc\\Namespaced\\Baz', 'Apc\\Namespaced\\Baz', '->loadClass() loads Apc\Namespaced\Baz class'),
array('Apc_Pearlike_Baz', 'Apc_Pearlike_Baz', '->loadClass() loads Apc_Pearlike_Baz class'),
- array('\\Apc\\Namespaced\\FooBar', '\\Apc\\Namespaced\\FooBar', '->loadClass() loads Apc\Namespaced\Baz class from fallback dir'),
+ array('\\Apc\\Namespaced\\FooBar', 'Apc\\Namespaced\\FooBar', '->loadClass() loads Apc\Namespaced\Baz class from fallback dir'),
array('Apc_Pearlike_FooBar', 'Apc_Pearlike_FooBar', '->loadClass() loads Apc_Pearlike_Baz class from fallback dir'),
);
}
'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha',
'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta',
),
- '\Apc\NamespaceCollision\A\Foo',
+ 'Apc\NamespaceCollision\A\Foo',
'->loadClass() loads NamespaceCollision\A\Foo from alpha.',
),
array(
'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta',
'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha',
),
- '\Apc\NamespaceCollision\A\Bar',
+ 'Apc\NamespaceCollision\A\Bar',
'->loadClass() loads NamespaceCollision\A\Bar from alpha.',
),
array(
'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha',
'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta',
),
- '\Apc\NamespaceCollision\A\B\Foo',
+ 'Apc\NamespaceCollision\A\B\Foo',
'->loadClass() loads NamespaceCollision\A\B\Foo from beta.',
),
array(
'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta',
'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha',
),
- '\Apc\NamespaceCollision\A\B\Bar',
+ 'Apc\NamespaceCollision\A\B\Bar',
'->loadClass() loads NamespaceCollision\A\B\Bar from beta.',
),
);
}
/**
- * @expectedException InvalidArgumentException
+ * @expectedException \InvalidArgumentException
*/
public function testUnableToLoadClassException()
{
public function getLoadNonexistentClassTests()
{
return array(
- array('\\Pearlike3_Bar', '\\Pearlike3_Bar', '->loadClass() loads non exising Pearlike3_Bar class with a leading slash'),
+ array('\\Pearlike3_Bar', '\\Pearlike3_Bar', '->loadClass() loads non existing Pearlike3_Bar class with a leading slash'),
);
}
public function testCreateMapFinderSupport()
{
- if (!class_exists('Symfony\\Component\\Finder\\Finder')) {
- $this->markTestSkipped('Finder component is not available');
- }
-
$finder = new \Symfony\Component\Finder\Finder();
$finder->files()->in(__DIR__.'/Fixtures/beta/NamespaceCollision');
+++ /dev/null
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\ClassLoader\Tests;
-
-use Symfony\Component\ClassLoader\ClassLoader;
-use Symfony\Component\ClassLoader\DebugClassLoader;
-
-class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
-{
- private $loader;
-
- protected function setUp()
- {
- $this->loader = new ClassLoader();
- spl_autoload_register(array($this->loader, 'loadClass'));
- }
-
- protected function tearDown()
- {
- spl_autoload_unregister(array($this->loader, 'loadClass'));
- }
-
- public function testIdempotence()
- {
- DebugClassLoader::enable();
- DebugClassLoader::enable();
-
- $functions = spl_autoload_functions();
- foreach ($functions as $function) {
- if (is_array($function) && $function[0] instanceof DebugClassLoader) {
- $reflClass = new \ReflectionClass($function[0]);
- $reflProp = $reflClass->getProperty('classFinder');
- $reflProp->setAccessible(true);
-
- $this->assertNotInstanceOf('Symfony\Component\ClassLoader\DebugClassLoader', $reflProp->getValue($function[0]));
-
- return;
- }
- }
-
- throw new \Exception('DebugClassLoader did not register');
- }
-}
--- /dev/null
+This file should be skipped.
--- /dev/null
+<?php
+
+namespace Acme\DemoLib;
+
+class Class_With_Underscores
+{
+}
--- /dev/null
+<?php
+
+namespace Acme\DemoLib;
+
+class Foo
+{
+}
--- /dev/null
+<?php
+
+namespace Acme\DemoLib\Lets\Go\Deeper;
+
+class Class_With_Underscores
+{
+}
--- /dev/null
+<?php
+
+namespace Acme\DemoLib\Lets\Go\Deeper;
+
+class Foo
+{
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader\Tests;
+
+use Symfony\Component\ClassLoader\Psr4ClassLoader;
+
+class Psr4ClassLoaderTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @param string $className
+ * @dataProvider getLoadClassTests
+ */
+ public function testLoadClass($className)
+ {
+ $loader = new Psr4ClassLoader();
+ $loader->addPrefix(
+ 'Acme\\DemoLib',
+ __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'psr-4'
+ );
+ $loader->loadClass($className);
+ $this->assertTrue(class_exists($className), sprintf('loadClass() should load %s', $className));
+ }
+
+ /**
+ * @return array
+ */
+ public function getLoadClassTests()
+ {
+ return array(
+ array('Acme\\DemoLib\\Foo'),
+ array('Acme\\DemoLib\\Class_With_Underscores'),
+ array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Foo'),
+ array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Class_With_Underscores')
+ );
+ }
+
+ /**
+ * @param string $className
+ * @dataProvider getLoadNonexistentClassTests
+ */
+ public function testLoadNonexistentClass($className)
+ {
+ $loader = new Psr4ClassLoader();
+ $loader->addPrefix(
+ 'Acme\\DemoLib',
+ __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'psr-4'
+ );
+ $loader->loadClass($className);
+ $this->assertFalse(class_exists($className), sprintf('loadClass() should not load %s', $className));
+ }
+
+ /**
+ * @return array
+ */
+ public function getLoadNonexistentClassTests()
+ {
+ return array(
+ array('Acme\\DemoLib\\I_Do_Not_Exist'),
+ array('UnknownVendor\\SomeLib\\I_Do_Not_Exist')
+ );
+ }
+}
* Turns on searching the include for class files. Allows easy loading
* of installed PEAR packages
*
- * @param Boolean $useIncludePath
+ * @param bool $useIncludePath
*/
public function useIncludePath($useIncludePath)
{
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
- * @return Boolean
+ * @return bool
*/
public function getUseIncludePath()
{
/**
* Registers this instance as an autoloader.
*
- * @param Boolean $prepend Whether to prepend the autoloader or not
+ * @param bool $prepend Whether to prepend the autoloader or not
*
* @api
*/
*
* @param string $class The name of the class
*
- * @return Boolean|null True, if loaded
+ * @return bool|null True, if loaded
*/
public function loadClass($class)
{
/**
* Registers this instance as an autoloader.
*
- * @param Boolean $prepend Whether to prepend the autoloader or not
+ * @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
*
* @param string $class The name of the class
*
- * @return Boolean|null True, if loaded
+ * @return bool|null True, if loaded
*/
public function loadClass($class)
{
/**
* Registers this instance as an autoloader.
*
- * @param Boolean $prepend Whether to prepend the autoloader or not
+ * @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
*
* @param string $class The name of the class
*
- * @return Boolean|null True, if loaded
+ * @return bool|null True, if loaded
*/
public function loadClass($class)
{
if (xcache_isset($this->prefix.$class)) {
$file = xcache_get($this->prefix.$class);
} else {
- xcache_set($this->prefix.$class, $file = $this->classFinder->findFile($class));
+ $file = $this->classFinder->findFile($class);
+ xcache_set($this->prefix.$class, $file);
}
return $file;
--- /dev/null
+{
+ "name": "symfony/class-loader",
+ "type": "library",
+ "description": "Symfony ClassLoader Component",
+ "keywords": [],
+ "homepage": "http://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "http://symfony.com/contributors"
+ }
+ ],
+ "minimum-stability": "dev",
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "symfony/finder": "~2.0"
+ },
+ "autoload": {
+ "psr-0": { "Symfony\\Component\\ClassLoader\\": "" }
+ },
+ "target-dir": "Symfony/Component/ClassLoader",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.5-dev"
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
+ backupGlobals="false"
+ colors="true"
+ bootstrap="vendor/autoload.php"
+>
+ <testsuites>
+ <testsuite name="Symfony ClassLoader Component Test Suite">
+ <directory>./Tests/</directory>
+ </testsuite>
+ </testsuites>
+
+ <filter>
+ <whitelist>
+ <directory>./</directory>
+ <exclude>
+ <directory>./Resources</directory>
+ <directory>./Tests</directory>
+ <directory>./vendor</directory>
+ </exclude>
+ </whitelist>
+ </filter>
+</phpunit>
--- /dev/null
+vendor/
+composer.lock
+phpunit.xml
--- /dev/null
+CHANGELOG
+=========
+
+2.5.0
+-----
+
+ * added Debug\TraceableEventDispatcher (originally in HttpKernel)
+ * changed Debug\TraceableEventDispatcherInterface to extend EventDispatcherInterface
+ * added RegisterListenersPass (originally in HttpKernel)
+
+2.1.0
+-----
+
+ * added TraceableEventDispatcherInterface
+ * added ContainerAwareEventDispatcher
+ * added a reference to the EventDispatcher on the Event
+ * added a reference to the Event name on the event
+ * added fluid interface to the dispatch() method which now returns the Event
+ object
+ * added GenericEvent event class
+ * added the possibility for subscribers to subscribe several times for the
+ same event
+ * added ImmutableEventDispatcher
* @param string $eventName Event for which the listener is added
* @param array $callback The service ID of the listener service & the method
* name that has to be called
- * @param integer $priority The higher this value, the earlier an event listener
+ * @param int $priority The higher this value, the earlier an event listener
* will be triggered in the chain.
* Defaults to 0.
*
public function hasListeners($eventName = null)
{
if (null === $eventName) {
- return (Boolean) count($this->listenerIds) || (Boolean) count($this->listeners);
+ return (bool) count($this->listenerIds) || (bool) count($this->listeners);
}
if (isset($this->listenerIds[$eventName])) {
}
/**
- * {@inheritDoc}
+ * {@inheritdoc}
*
* Lazily loads listeners for this event from the dependency injection
* container.
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Debug;
+
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\Stopwatch\Stopwatch;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Collects some data about event listeners.
+ *
+ * This event dispatcher delegates the dispatching to another one.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TraceableEventDispatcher implements TraceableEventDispatcherInterface
+{
+ protected $logger;
+ protected $stopwatch;
+
+ private $called;
+ private $dispatcher;
+
+ /**
+ * Constructor.
+ *
+ * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
+ * @param Stopwatch $stopwatch A Stopwatch instance
+ * @param LoggerInterface $logger A LoggerInterface instance
+ */
+ public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null)
+ {
+ $this->dispatcher = $dispatcher;
+ $this->stopwatch = $stopwatch;
+ $this->logger = $logger;
+ $this->called = array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addListener($eventName, $listener, $priority = 0)
+ {
+ $this->dispatcher->addListener($eventName, $listener, $priority);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addSubscriber(EventSubscriberInterface $subscriber)
+ {
+ $this->dispatcher->addSubscriber($subscriber);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeListener($eventName, $listener)
+ {
+ return $this->dispatcher->removeListener($eventName, $listener);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeSubscriber(EventSubscriberInterface $subscriber)
+ {
+ return $this->dispatcher->removeSubscriber($subscriber);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getListeners($eventName = null)
+ {
+ return $this->dispatcher->getListeners($eventName);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasListeners($eventName = null)
+ {
+ return $this->dispatcher->hasListeners($eventName);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function dispatch($eventName, Event $event = null)
+ {
+ if (null === $event) {
+ $event = new Event();
+ }
+
+ $this->preProcess($eventName);
+ $this->preDispatch($eventName, $event);
+
+ $e = $this->stopwatch->start($eventName, 'section');
+
+ $this->dispatcher->dispatch($eventName, $event);
+
+ if ($e->isStarted()) {
+ $e->stop();
+ }
+
+ $this->postDispatch($eventName, $event);
+ $this->postProcess($eventName);
+
+ return $event;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCalledListeners()
+ {
+ $called = array();
+ foreach ($this->called as $eventName => $listeners) {
+ foreach ($listeners as $listener) {
+ $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
+ $called[$eventName.'.'.$info['pretty']] = $info;
+ }
+ }
+
+ return $called;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getNotCalledListeners()
+ {
+ try {
+ $allListeners = $this->getListeners();
+ } catch (\Exception $e) {
+ if (null !== $this->logger) {
+ $this->logger->info(sprintf('An exception was thrown while getting the uncalled listeners (%s)', $e->getMessage()), array('exception' => $e));
+ }
+
+ // unable to retrieve the uncalled listeners
+ return array();
+ }
+
+ $notCalled = array();
+ foreach ($allListeners as $eventName => $listeners) {
+ foreach ($listeners as $listener) {
+ $called = false;
+ if (isset($this->called[$eventName])) {
+ foreach ($this->called[$eventName] as $l) {
+ if ($l->getWrappedListener() === $listener) {
+ $called = true;
+
+ break;
+ }
+ }
+ }
+
+ if (!$called) {
+ $info = $this->getListenerInfo($listener, $eventName);
+ $notCalled[$eventName.'.'.$info['pretty']] = $info;
+ }
+ }
+ }
+
+ return $notCalled;
+ }
+
+ /**
+ * Proxies all method calls to the original event dispatcher.
+ *
+ * @param string $method The method name
+ * @param array $arguments The method arguments
+ *
+ * @return mixed
+ */
+ public function __call($method, $arguments)
+ {
+ return call_user_func_array(array($this->dispatcher, $method), $arguments);
+ }
+
+ /**
+ * Called before dispatching the event.
+ *
+ * @param string $eventName The event name
+ * @param Event $event The event
+ */
+ protected function preDispatch($eventName, Event $event)
+ {
+ }
+
+ /**
+ * Called after dispatching the event.
+ *
+ * @param string $eventName The event name
+ * @param Event $event The event
+ */
+ protected function postDispatch($eventName, Event $event)
+ {
+ }
+
+ private function preProcess($eventName)
+ {
+ foreach ($this->dispatcher->getListeners($eventName) as $listener) {
+ $this->dispatcher->removeListener($eventName, $listener);
+ $info = $this->getListenerInfo($listener, $eventName);
+ $name = isset($info['class']) ? $info['class'] : $info['type'];
+ $this->dispatcher->addListener($eventName, new WrappedListener($listener, $name, $this->stopwatch));
+ }
+ }
+
+ private function postProcess($eventName)
+ {
+ $skipped = false;
+ foreach ($this->dispatcher->getListeners($eventName) as $listener) {
+ // Unwrap listener
+ $this->dispatcher->removeListener($eventName, $listener);
+ $this->dispatcher->addListener($eventName, $listener->getWrappedListener());
+
+ $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
+ if ($listener->wasCalled()) {
+ if (null !== $this->logger) {
+ $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty']));
+ }
+
+ if (!isset($this->called[$eventName])) {
+ $this->called[$eventName] = new \SplObjectStorage();
+ }
+
+ $this->called[$eventName]->attach($listener);
+ }
+
+ if (null !== $this->logger && $skipped) {
+ $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName));
+ }
+
+ if ($listener->stoppedPropagation()) {
+ if (null !== $this->logger) {
+ $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName));
+ }
+
+ $skipped = true;
+ }
+ }
+ }
+
+ /**
+ * Returns information about the listener
+ *
+ * @param object $listener The listener
+ * @param string $eventName The event name
+ *
+ * @return array Information about the listener
+ */
+ private function getListenerInfo($listener, $eventName)
+ {
+ $info = array(
+ 'event' => $eventName,
+ );
+ if ($listener instanceof \Closure) {
+ $info += array(
+ 'type' => 'Closure',
+ 'pretty' => 'closure'
+ );
+ } elseif (is_string($listener)) {
+ try {
+ $r = new \ReflectionFunction($listener);
+ $file = $r->getFileName();
+ $line = $r->getStartLine();
+ } catch (\ReflectionException $e) {
+ $file = null;
+ $line = null;
+ }
+ $info += array(
+ 'type' => 'Function',
+ 'function' => $listener,
+ 'file' => $file,
+ 'line' => $line,
+ 'pretty' => $listener,
+ );
+ } elseif (is_array($listener) || (is_object($listener) && is_callable($listener))) {
+ if (!is_array($listener)) {
+ $listener = array($listener, '__invoke');
+ }
+ $class = is_object($listener[0]) ? get_class($listener[0]) : $listener[0];
+ try {
+ $r = new \ReflectionMethod($class, $listener[1]);
+ $file = $r->getFileName();
+ $line = $r->getStartLine();
+ } catch (\ReflectionException $e) {
+ $file = null;
+ $line = null;
+ }
+ $info += array(
+ 'type' => 'Method',
+ 'class' => $class,
+ 'method' => $listener[1],
+ 'file' => $file,
+ 'line' => $line,
+ 'pretty' => $class.'::'.$listener[1],
+ );
+ }
+
+ return $info;
+ }
+}
namespace Symfony\Component\EventDispatcher\Debug;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
-interface TraceableEventDispatcherInterface
+interface TraceableEventDispatcherInterface extends EventDispatcherInterface
{
/**
* Gets the called listeners.
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Debug;
+
+use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class WrappedListener
+{
+ private $listener;
+ private $name;
+ private $called;
+ private $stoppedPropagation;
+ private $stopwatch;
+
+ public function __construct($listener, $name, Stopwatch $stopwatch)
+ {
+ $this->listener = $listener;
+ $this->name = $name;
+ $this->stopwatch = $stopwatch;
+ $this->called = false;
+ $this->stoppedPropagation = false;
+ }
+
+ public function getWrappedListener()
+ {
+ return $this->listener;
+ }
+
+ public function wasCalled()
+ {
+ return $this->called;
+ }
+
+ public function stoppedPropagation()
+ {
+ return $this->stoppedPropagation;
+ }
+
+ public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher)
+ {
+ $this->called = true;
+
+ $e = $this->stopwatch->start($this->name, 'event_listener');
+
+ call_user_func($this->listener, $event, $eventName, $dispatcher);
+
+ if ($e->isStarted()) {
+ $e->stop();
+ }
+
+ if ($event->isPropagationStopped()) {
+ $this->stoppedPropagation = true;
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+
+/**
+ * Compiler pass to register tagged services for an event dispatcher.
+ */
+class RegisterListenersPass implements CompilerPassInterface
+{
+ /**
+ * @var string
+ */
+ protected $dispatcherService;
+
+ /**
+ * @var string
+ */
+ protected $listenerTag;
+
+ /**
+ * @var string
+ */
+ protected $subscriberTag;
+
+ /**
+ * Constructor.
+ *
+ * @param string $dispatcherService Service name of the event dispatcher in processed container
+ * @param string $listenerTag Tag name used for listener
+ * @param string $subscriberTag Tag name used for subscribers
+ */
+ public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber')
+ {
+ $this->dispatcherService = $dispatcherService;
+ $this->listenerTag = $listenerTag;
+ $this->subscriberTag = $subscriberTag;
+ }
+
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
+ return;
+ }
+
+ $definition = $container->findDefinition($this->dispatcherService);
+
+ foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) {
+ $def = $container->getDefinition($id);
+ if (!$def->isPublic()) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id));
+ }
+
+ if ($def->isAbstract()) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id));
+ }
+
+ foreach ($events as $event) {
+ $priority = isset($event['priority']) ? $event['priority'] : 0;
+
+ if (!isset($event['event'])) {
+ throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
+ }
+
+ if (!isset($event['method'])) {
+ $event['method'] = 'on'.preg_replace_callback(array(
+ '/(?<=\b)[a-z]/i',
+ '/[^a-z0-9]/i',
+ ), function ($matches) { return strtoupper($matches[0]); }, $event['event']);
+ $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
+ }
+
+ $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority));
+ }
+ }
+
+ foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) {
+ $def = $container->getDefinition($id);
+ if (!$def->isPublic()) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id));
+ }
+
+ // We must assume that the class value has been correctly filled, even if the service is created by a factory
+ $class = $def->getClass();
+
+ $refClass = new \ReflectionClass($class);
+ $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
+ if (!$refClass->implementsInterface($interface)) {
+ throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
+ }
+
+ $definition->addMethodCall('addSubscriberService', array($id, $class));
+ }
+ }
+}
class Event
{
/**
- * @var Boolean Whether no further event listeners should be triggered
+ * @var bool Whether no further event listeners should be triggered
*/
private $propagationStopped = false;
* Returns whether further event listeners should be triggered.
*
* @see Event::stopPropagation
- * @return Boolean Whether propagation was already stopped for this event.
+ * @return bool Whether propagation was already stopped for this event.
*
* @api
*/
*
* @param EventDispatcherInterface $dispatcher
*
+ * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
+ *
* @api
*/
public function setDispatcher(EventDispatcherInterface $dispatcher)
*
* @return EventDispatcherInterface
*
+ * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
+ *
* @api
*/
public function getDispatcher()
*
* @return string
*
+ * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call.
+ *
* @api
*/
public function getName()
*
* @param string $name The event name.
*
+ * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call.
+ *
* @api
*/
public function setName($name)
}
}
- return $this->sorted;
+ return array_filter($this->sorted);
}
/**
*/
public function hasListeners($eventName = null)
{
- return (Boolean) count($this->getListeners($eventName));
+ return (bool) count($this->getListeners($eventName));
}
/**
* This method can be overridden to add functionality that is executed
* for each listener.
*
- * @param array[callback] $listeners The event listeners.
- * @param string $eventName The name of the event to dispatch.
- * @param Event $event The event object to pass to the event handlers/listeners.
+ * @param callable[] $listeners The event listeners.
+ * @param string $eventName The name of the event to dispatch.
+ * @param Event $event The event object to pass to the event handlers/listeners.
*/
protected function doDispatch($listeners, $eventName, Event $event)
{
foreach ($listeners as $listener) {
- call_user_func($listener, $event);
+ call_user_func($listener, $event, $eventName, $this);
if ($event->isPropagationStopped()) {
break;
}
*
* @param string $eventName The event to listen on
* @param callable $listener The listener
- * @param integer $priority The higher this value, the earlier an event
+ * @param int $priority The higher this value, the earlier an event
* listener will be triggered in the chain (defaults to 0)
*
* @api
*
* @param string $eventName The name of the event
*
- * @return Boolean true if the specified event has any listeners, false otherwise
+ * @return bool true if the specified event has any listeners, false otherwise
*/
public function hasListeners($eventName = null);
}
class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
{
/**
- * Observer pattern subject.
+ * Event subject.
*
* @var mixed usually object or callable
*/
*
* @param string $key Key of arguments array.
*
- * @return boolean
+ * @return bool
*/
public function hasArgument($key)
{
*
* @param string $key Array key.
*
- * @return boolean
+ * @return bool
*/
public function offsetExists($key)
{
--- /dev/null
+Copyright (c) 2004-2014 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.
--- /dev/null
+EventDispatcher Component
+=========================
+
+The Symfony2 EventDispatcher component implements the Mediator pattern in a
+simple and effective way to make your projects truly extensible.
+
+ use Symfony\Component\EventDispatcher\EventDispatcher;
+ use Symfony\Component\EventDispatcher\Event;
+
+ $dispatcher = new EventDispatcher();
+
+ $dispatcher->addListener('event_name', function (Event $event) {
+ // ...
+ });
+
+ $dispatcher->dispatch('event_name');
+
+Resources
+---------
+
+You can run the unit tests with the following command:
+
+ $ cd path/to/Symfony/Component/EventDispatcher/
+ $ composer.phar install
+ $ phpunit
class ContainerAwareEventDispatcherTest extends \PHPUnit_Framework_TestCase
{
- protected function setUp()
- {
- if (!class_exists('Symfony\Component\DependencyInjection\Container')) {
- $this->markTestSkipped('The "DependencyInjection" component is not available');
- }
- }
-
public function testAddAListenerService()
{
$event = new Event();
public function testGetListenersOnLazyLoad()
{
- $event = new Event();
-
$service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
$container = new Container();
public function testRemoveAfterDispatch()
{
- $event = new Event();
-
$service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
$container = new Container();
public function testRemoveBeforeDispatch()
{
- $event = new Event();
-
$service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service');
$container = new Container();
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Tests\Debug;
+
+use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\Stopwatch\Stopwatch;
+
+class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
+{
+ public function testAddRemoveListener()
+ {
+ $dispatcher = new EventDispatcher();
+ $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+ $tdispatcher->addListener('foo', $listener = function () { ; });
+ $listeners = $dispatcher->getListeners('foo');
+ $this->assertCount(1, $listeners);
+ $this->assertSame($listener, $listeners[0]);
+
+ $tdispatcher->removeListener('foo', $listener);
+ $this->assertCount(0, $dispatcher->getListeners('foo'));
+ }
+
+ public function testGetListeners()
+ {
+ $dispatcher = new EventDispatcher();
+ $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+ $tdispatcher->addListener('foo', $listener = function () { ; });
+ $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo'));
+ }
+
+ public function testHasListeners()
+ {
+ $dispatcher = new EventDispatcher();
+ $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+ $this->assertFalse($dispatcher->hasListeners('foo'));
+ $this->assertFalse($tdispatcher->hasListeners('foo'));
+
+ $tdispatcher->addListener('foo', $listener = function () { ; });
+ $this->assertTrue($dispatcher->hasListeners('foo'));
+ $this->assertTrue($tdispatcher->hasListeners('foo'));
+ }
+
+ public function testAddRemoveSubscriber()
+ {
+ $dispatcher = new EventDispatcher();
+ $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+ $subscriber = new EventSubscriber();
+
+ $tdispatcher->addSubscriber($subscriber);
+ $listeners = $dispatcher->getListeners('foo');
+ $this->assertCount(1, $listeners);
+ $this->assertSame(array($subscriber, 'call'), $listeners[0]);
+
+ $tdispatcher->removeSubscriber($subscriber);
+ $this->assertCount(0, $dispatcher->getListeners('foo'));
+ }
+
+ public function testGetCalledListeners()
+ {
+ $dispatcher = new EventDispatcher();
+ $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+ $tdispatcher->addListener('foo', $listener = function () { ; });
+
+ $this->assertEquals(array(), $tdispatcher->getCalledListeners());
+ $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure')), $tdispatcher->getNotCalledListeners());
+
+ $tdispatcher->dispatch('foo');
+
+ $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure')), $tdispatcher->getCalledListeners());
+ $this->assertEquals(array(), $tdispatcher->getNotCalledListeners());
+ }
+
+ public function testLogger()
+ {
+ $logger = $this->getMock('Psr\Log\LoggerInterface');
+
+ $dispatcher = new EventDispatcher();
+ $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
+ $tdispatcher->addListener('foo', $listener1 = function () { ; });
+ $tdispatcher->addListener('foo', $listener2 = function () { ; });
+
+ $logger->expects($this->at(0))->method('debug')->with("Notified event \"foo\" to listener \"closure\".");
+ $logger->expects($this->at(1))->method('debug')->with("Notified event \"foo\" to listener \"closure\".");
+
+ $tdispatcher->dispatch('foo');
+ }
+
+ public function testLoggerWithStoppedEvent()
+ {
+ $logger = $this->getMock('Psr\Log\LoggerInterface');
+
+ $dispatcher = new EventDispatcher();
+ $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
+ $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); });
+ $tdispatcher->addListener('foo', $listener2 = function () { ; });
+
+ $logger->expects($this->at(0))->method('debug')->with("Notified event \"foo\" to listener \"closure\".");
+ $logger->expects($this->at(1))->method('debug')->with("Listener \"closure\" stopped propagation of the event \"foo\".");
+ $logger->expects($this->at(2))->method('debug')->with("Listener \"closure\" was not called for event \"foo\".");
+
+ $tdispatcher->dispatch('foo');
+ }
+
+ public function testDispatchCallListeners()
+ {
+ $called = array();
+
+ $dispatcher = new EventDispatcher();
+ $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+ $tdispatcher->addListener('foo', $listener1 = function () use (&$called) { $called[] = 'foo1'; });
+ $tdispatcher->addListener('foo', $listener2 = function () use (&$called) { $called[] = 'foo2'; });
+
+ $tdispatcher->dispatch('foo');
+
+ $this->assertEquals(array('foo1', 'foo2'), $called);
+ }
+
+ public function testDispatchNested()
+ {
+ $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+ $loop = 1;
+ $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) {
+ ++$loop;
+ if (2 == $loop) {
+ $dispatcher->dispatch('foo');
+ }
+ });
+
+ $dispatcher->dispatch('foo');
+ }
+
+ public function testDispatchReusedEventNested()
+ {
+ $nestedCall = false;
+ $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+ $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) {
+ $dispatcher->dispatch('bar', $e);
+ });
+ $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) {
+ $nestedCall = true;
+ });
+
+ $this->assertFalse($nestedCall);
+ $dispatcher->dispatch('foo');
+ $this->assertTrue($nestedCall);
+ }
+}
+
+class EventSubscriber implements EventSubscriberInterface
+{
+ public static function getSubscribedEvents()
+ {
+ return array('foo' => 'call');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
+
+class RegisterListenersPassTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * Tests that event subscribers not implementing EventSubscriberInterface
+ * trigger an exception.
+ *
+ * @expectedException \InvalidArgumentException
+ */
+ public function testEventSubscriberWithoutInterface()
+ {
+ // one service, not implementing any interface
+ $services = array(
+ 'my_event_subscriber' => array(0 => array()),
+ );
+
+ $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
+ $definition->expects($this->atLeastOnce())
+ ->method('isPublic')
+ ->will($this->returnValue(true));
+ $definition->expects($this->atLeastOnce())
+ ->method('getClass')
+ ->will($this->returnValue('stdClass'));
+
+ $builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
+ $builder->expects($this->any())
+ ->method('hasDefinition')
+ ->will($this->returnValue(true));
+
+ // We don't test kernel.event_listener here
+ $builder->expects($this->atLeastOnce())
+ ->method('findTaggedServiceIds')
+ ->will($this->onConsecutiveCalls(array(), $services));
+
+ $builder->expects($this->atLeastOnce())
+ ->method('getDefinition')
+ ->will($this->returnValue($definition));
+
+ $registerListenersPass = new RegisterListenersPass();
+ $registerListenersPass->process($builder);
+ }
+
+ public function testValidEventSubscriber()
+ {
+ $services = array(
+ 'my_event_subscriber' => array(0 => array()),
+ );
+
+ $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
+ $definition->expects($this->atLeastOnce())
+ ->method('isPublic')
+ ->will($this->returnValue(true));
+ $definition->expects($this->atLeastOnce())
+ ->method('getClass')
+ ->will($this->returnValue('Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService'));
+
+ $builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
+ $builder->expects($this->any())
+ ->method('hasDefinition')
+ ->will($this->returnValue(true));
+
+ // We don't test kernel.event_listener here
+ $builder->expects($this->atLeastOnce())
+ ->method('findTaggedServiceIds')
+ ->will($this->onConsecutiveCalls(array(), $services));
+
+ $builder->expects($this->atLeastOnce())
+ ->method('getDefinition')
+ ->will($this->returnValue($definition));
+
+ $builder->expects($this->atLeastOnce())
+ ->method('findDefinition')
+ ->will($this->returnValue($definition));
+
+ $registerListenersPass = new RegisterListenersPass();
+ $registerListenersPass->process($builder);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage The service "foo" must be public as event listeners are lazy-loaded.
+ */
+ public function testPrivateEventListener()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_listener', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new RegisterListenersPass();
+ $registerListenersPass->process($container);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage The service "foo" must be public as event subscribers are lazy-loaded.
+ */
+ public function testPrivateEventSubscriber()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_subscriber', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new RegisterListenersPass();
+ $registerListenersPass->process($container);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage The service "foo" must not be abstract as event listeners are lazy-loaded.
+ */
+ public function testAbstractEventListener()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_listener', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new RegisterListenersPass();
+ $registerListenersPass->process($container);
+ }
+}
+
+class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
+{
+ public static function getSubscribedEvents() {}
+}
const preBar = 'pre.bar';
const postBar = 'post.bar';
+ /**
+ * @var EventDispatcher
+ */
private $dispatcher;
private $listener;
public function testEventReceivesTheDispatcherInstance()
{
- $test = $this;
+ $dispatcher = null;
$this->dispatcher->addListener('test', function ($event) use (&$dispatcher) {
$dispatcher = $event->getDispatcher();
});
$this->assertSame($this->dispatcher, $dispatcher);
}
+ public function testEventReceivesTheDispatcherInstanceAsArgument()
+ {
+ $listener = new TestWithDispatcher();
+ $this->dispatcher->addListener('test', array($listener, 'foo'));
+ $this->assertNull($listener->name);
+ $this->assertNull($listener->dispatcher);
+ $this->dispatcher->dispatch('test');
+ $this->assertEquals('test', $listener->name);
+ $this->assertSame($this->dispatcher, $listener->dispatcher);
+ }
+
/**
* @see https://bugs.php.net/bug.php?id=62976
*
{
$dispatcher = new EventDispatcher();
$dispatcher->addListener('bug.62976', new CallableClass());
- $dispatcher->removeListener('bug.62976', function() {});
+ $dispatcher->removeListener('bug.62976', function () {});
$this->assertTrue($dispatcher->hasListeners('bug.62976'));
}
+
+ public function testHasListenersWhenAddedCallbackListenerIsRemoved()
+ {
+ $listener = function () {};
+ $this->dispatcher->addListener('foo', $listener);
+ $this->dispatcher->removeListener('foo', $listener);
+ $this->assertFalse($this->dispatcher->hasListeners());
+ }
+
+ public function testGetListenersWhenAddedCallbackListenerIsRemoved()
+ {
+ $listener = function () {};
+ $this->dispatcher->addListener('foo', $listener);
+ $this->dispatcher->removeListener('foo', $listener);
+ $this->assertSame(array(), $this->dispatcher->getListeners());
+ }
+
+ public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled()
+ {
+ $this->assertFalse($this->dispatcher->hasListeners('foo'));
+ $this->assertFalse($this->dispatcher->hasListeners());
+ }
}
class CallableClass
}
}
+class TestWithDispatcher
+{
+ public $name;
+ public $dispatcher;
+
+ public function foo(Event $e, $name, $dispatcher)
+ {
+ $this->name = $name;
+ $this->dispatcher = $dispatcher;
+ }
+}
+
class TestEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
*/
protected function setUp()
{
- $this->event = new Event;
+ $this->event = new Event();
$this->dispatcher = new EventDispatcher();
}
protected function tearDown()
{
$this->event = null;
- $this->eventDispatcher = null;
+ $this->dispatcher = null;
}
public function testIsPropagationStopped()
{
parent::setUp();
- $this->subject = new \StdClass();
- $this->event = new GenericEvent($this->subject, array('name' => 'Event'), 'foo');
+ $this->subject = new \stdClass();
+ $this->event = new GenericEvent($this->subject, array('name' => 'Event'));
}
/**
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
--- /dev/null
+{
+ "name": "symfony/event-dispatcher",
+ "type": "library",
+ "description": "Symfony EventDispatcher Component",
+ "keywords": [],
+ "homepage": "http://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "http://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "symfony/dependency-injection": "~2.0",
+ "symfony/config": "~2.0",
+ "symfony/stopwatch": "~2.2",
+ "psr/log": "~1.0"
+ },
+ "suggest": {
+ "symfony/dependency-injection": "",
+ "symfony/http-kernel": ""
+ },
+ "autoload": {
+ "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" }
+ },
+ "target-dir": "Symfony/Component/EventDispatcher",
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.5-dev"
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
+ backupGlobals="false"
+ colors="true"
+ bootstrap="vendor/autoload.php"
+>
+ <testsuites>
+ <testsuite name="Symfony EventDispatcher Component Test Suite">
+ <directory>./Tests/</directory>
+ </testsuite>
+ </testsuites>
+
+ <filter>
+ <whitelist>
+ <directory>./</directory>
+ <exclude>
+ <directory>./Resources</directory>
+ <directory>./Tests</directory>
+ <directory>./vendor</directory>
+ </exclude>
+ </whitelist>
+ </filter>
+</phpunit>
'Aws' => AWS_FILE_PREFIX,
'Guzzle' => AWS_FILE_PREFIX,
'Symfony' => AWS_FILE_PREFIX,
- 'Doctrine' => AWS_FILE_PREFIX,
- 'Psr' => AWS_FILE_PREFIX,
- 'Monolog' => AWS_FILE_PREFIX
+
+ // Not needed for basic S3-functionality.
+ //'Doctrine' => AWS_FILE_PREFIX,
+ //'Psr' => AWS_FILE_PREFIX,
+ //'Monolog' => AWS_FILE_PREFIX
));
$classLoader->register();