/**
* @var string Current version of the SDK
*/
- const VERSION = '2.6.15';
+ const VERSION = '2.7.5';
/**
* Create a new service locator for the AWS SDK
namespace Aws\Common\Client;
use Aws\Common\Aws;
-use Aws\Common\Credentials\Credentials;
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Credentials\NullCredentials;
use Aws\Common\Enum\ClientOptions as Options;
/**
* Get an endpoint for a specific region from a service description
- *
- * @param ServiceDescriptionInterface $description Service description
- * @param string $region Region of the endpoint
- * @param string $scheme URL scheme
- *
- * @return string
- * @throws InvalidArgumentException
+ * @deprecated This function will no longer be updated to work with new regions.
*/
public static function getEndpoint(ServiceDescriptionInterface $description, $region, $scheme)
{
$config = $this->getConfig();
$formerRegion = $config->get(Options::REGION);
$global = $this->serviceDescription->getData('globalEndpoint');
+ $provider = $config->get('endpoint_provider');
+
+ if (!$provider) {
+ throw new \RuntimeException('No endpoint provider configured');
+ }
// Only change the region if the service does not have a global endpoint
if (!$global || $this->serviceDescription->getData('namespace') === 'S3') {
- $baseUrl = self::getEndpoint($this->serviceDescription, $region, $config->get(Options::SCHEME));
- $this->setBaseUrl($baseUrl);
- $config->set(Options::BASE_URL, $baseUrl)->set(Options::REGION, $region);
+
+ $endpoint = call_user_func(
+ $provider,
+ array(
+ 'scheme' => $config->get(Options::SCHEME),
+ 'region' => $region,
+ 'service' => $config->get(Options::SERVICE)
+ )
+ );
+
+ $this->setBaseUrl($endpoint['endpoint']);
+ $config->set(Options::BASE_URL, $endpoint['endpoint']);
+ $config->set(Options::REGION, $region);
// Update the signature if necessary
$signature = $this->getSignature();
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\Exception\InvalidArgumentException;
use Aws\Common\Exception\NamespaceExceptionFactory;
use Aws\Common\Exception\Parser\DefaultXmlExceptionParser;
use Aws\Common\Exception\Parser\ExceptionParserInterface;
use Aws\Common\Iterator\AwsResourceIteratorFactory;
+use Aws\Common\RulesEndpointProvider;
use Aws\Common\Signature\EndpointSignatureInterface;
use Aws\Common\Signature\SignatureInterface;
use Aws\Common\Signature\SignatureV2;
use Guzzle\Plugin\Backoff\ExponentialBackoffStrategy;
use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy;
-use Guzzle\Service\Client;
use Guzzle\Service\Description\ServiceDescription;
use Guzzle\Service\Resource\ResourceIteratorClassFactory;
use Guzzle\Log\LogAdapterInterface;
(self::$commonConfigRequirements + $this->configRequirements)
);
+ if (!isset($config['endpoint_provider'])) {
+ $config['endpoint_provider'] = RulesEndpointProvider::fromDefaults();
+ }
+
// Resolve the endpoint, signature, and credentials
$description = $this->updateConfigFromDescription($config);
$signature = $this->getSignature($description, $config);
$this->setIteratorsConfig($iterators);
}
- // Ensure that the service description has regions
- if (!$description->getData('regions')) {
- throw new InvalidArgumentException(
- 'No regions found in the ' . $description->getData('serviceFullName'). ' description'
- );
- }
-
// Make sure a valid region is set
$region = $config->get(Options::REGION);
$global = $description->getData('globalEndpoint');
+
if (!$global && !$region) {
throw new InvalidArgumentException(
'A region is required when using ' . $description->getData('serviceFullName')
- . '. Set "region" to one of: ' . implode(', ', array_keys($description->getData('regions')))
);
} elseif ($global && (!$region || $description->getData('namespace') !== 'S3')) {
- $region = Region::US_EAST_1;
- $config->set(Options::REGION, $region);
+ $region = 'us-east-1';
+ $config->set(Options::REGION, 'us-east-1');
}
if (!$config->get(Options::BASE_URL)) {
- // Set the base URL using the scheme and hostname of the service's region
- $config->set(Options::BASE_URL, AbstractClient::getEndpoint(
- $description,
- $region,
- $config->get(Options::SCHEME)
- ));
+ $endpoint = call_user_func(
+ $config->get('endpoint_provider'),
+ array(
+ 'scheme' => $config->get(Options::SCHEME),
+ 'region' => $region,
+ 'service' => $config->get(Options::SERVICE)
+ )
+ );
+ $config->set(Options::BASE_URL, $endpoint['endpoint']);
+
+ // Set a signature if one was not explicitly provided.
+ if (!$config->hasKey(Options::SIGNATURE)
+ && isset($endpoint['signatureVersion'])
+ ) {
+ $config->set(Options::SIGNATURE, $endpoint['signatureVersion']);
+ }
}
return $description;
const EU_WEST_1 = 'eu-west-1';
const IRELAND = 'eu-west-1';
+
+ const EU_CENTRAL_1 = 'eu-central-1';
+ const FRANKFURT = 'eu-central-1';
const AP_SOUTHEAST_1 = 'ap-southeast-1';
const SINGAPORE = 'ap-southeast-1';
$useNative = function_exists('hex2bin');
}
+ if (!$useNative && strlen($hash) % 2 !== 0) {
+ $hash = '0' . $hash;
+ }
+
return $useNative ? hex2bin($hash) : pack("H*", $hash);
}
/**
* Return a new instance of the UploadBuilder
*
- * @return self
+ * @return static
*/
public static function newInstance()
{
*
* @param AwsClientInterface $client Client to use
*
- * @return self
+ * @return $this
*/
public function setClient(AwsClientInterface $client)
{
* multipart upload. When an ID is passed, the builder will create a
* state object using the data from a ListParts API response.
*
- * @return self
+ * @return $this
*/
public function resumeFrom($state)
{
* You can also stream from a resource returned from fopen or a Guzzle
* {@see EntityBody} object.
*
- * @return self
+ * @return $this
* @throws InvalidArgumentException when the source cannot be found or opened
*/
public function setSource($source)
*
* @param array $headers Headers to add to the uploaded object
*
- * @return self
+ * @return $this
*/
public function setHeaders(array $headers)
{
'class' => 'Aws\CloudWatch\CloudWatchClient'
),
+ 'cloudwatchlogs' => array(
+ 'alias' => 'CloudWatchLogs',
+ 'extends' => 'default_settings',
+ 'class' => 'Aws\CloudWatchLogs\CloudWatchLogsClient'
+ ),
+
'cognito-identity' => array(
'alias' => 'CognitoIdentity',
'extends' => 'default_settings',
'cognitosync' => array('extends' => 'cognito-sync'),
- 'cloudwatchlogs' => array(
- 'alias' => 'CloudWatchLogs',
+ 'codedeploy' => array(
+ 'alias' => 'CodeDeploy',
'extends' => 'default_settings',
- 'class' => 'Aws\CloudWatchLogs\CloudWatchLogsClient'
+ 'class' => 'Aws\CodeDeploy\CodeDeployClient'
+ ),
+
+ 'config' => array(
+ 'alias' => 'ConfigService',
+ 'extends' => 'default_settings',
+ 'class' => 'Aws\ConfigService\ConfigServiceClient'
),
'datapipeline' => array(
'class' => 'Aws\Kinesis\KinesisClient'
),
+ 'kms' => array(
+ 'alias' => 'Kms',
+ 'extends' => 'default_settings',
+ 'class' => 'Aws\Kms\KmsClient'
+ ),
+
+ 'lambda' => array(
+ 'alias' => 'Lambda',
+ 'extends' => 'default_settings',
+ 'class' => 'Aws\Lambda\LambdaClient'
+ ),
+
'iam' => array(
'alias' => 'Iam',
'extends' => 'default_settings',
--- /dev/null
+<?php
+return array(
+ 'version' => 2,
+ 'endpoints' => array(
+ '*/*' => array(
+ 'endpoint' => '{service}.{region}.amazonaws.com'
+ ),
+ 'cn-north-1/*' => array(
+ 'endpoint' => '{service}.{region}.amazonaws.com.cn',
+ 'signatureVersion' => 'v4'
+ ),
+ 'us-gov-west-1/iam' => array(
+ 'endpoint' => 'iam.us-gov.amazonaws.com'
+ ),
+ 'us-gov-west-1/sts' => array(
+ 'endpoint' => 'sts.us-gov.amazonaws.com'
+ ),
+ 'us-gov-west-1/s3' => array(
+ 'endpoint' => 's3-{region}.amazonaws.com'
+ ),
+ '*/cloudfront' => array(
+ 'endpoint' => 'cloudfront.amazonaws.com'
+ ),
+ '*/iam' => array(
+ 'endpoint' => 'iam.amazonaws.com'
+ ),
+ '*/importexport' => array(
+ 'endpoint' => 'importexport.amazonaws.com'
+ ),
+ '*/route53' => array(
+ 'endpoint' => 'route53.amazonaws.com'
+ ),
+ '*/sts' => array(
+ 'endpoint' => 'sts.amazonaws.com'
+ ),
+ 'us-east-1/sdb' => array(
+ 'endpoint' => 'sdb.amazonaws.com'
+ ),
+ 'us-east-1/s3' => array(
+ 'endpoint' => 's3.amazonaws.com'
+ ),
+ 'us-west-1/s3' => array(
+ 'endpoint' => 's3-{region}.amazonaws.com'
+ ),
+ 'us-west-2/s3' => array(
+ 'endpoint' => 's3-{region}.amazonaws.com'
+ ),
+ 'eu-west-1/s3' => array(
+ 'endpoint' => 's3-{region}.amazonaws.com'
+ ),
+ 'ap-southeast-1/s3' => array(
+ 'endpoint' => 's3-{region}.amazonaws.com'
+ ),
+ 'ap-southeast-2/s3' => array(
+ 'endpoint' => 's3-{region}.amazonaws.com'
+ ),
+ 'ap-northeast-1/s3' => array(
+ 'endpoint' => 's3-{region}.amazonaws.com'
+ ),
+ 'sa-east-1/s3' => array(
+ 'endpoint' => 's3-{region}.amazonaws.com'
+ )
+ )
+);
--- /dev/null
+<?php
+namespace Aws\Common;
+
+/**
+ * Provides endpoints based on a rules configuration file.
+ */
+class RulesEndpointProvider
+{
+ /** @var array */
+ private $patterns;
+
+ /**
+ * @param array $patterns Hash of endpoint patterns mapping to endpoint
+ * configurations.
+ */
+ public function __construct(array $patterns)
+ {
+ $this->patterns = $patterns;
+ }
+
+ /**
+ * Creates and returns the default RulesEndpointProvider based on the
+ * public rule sets.
+ *
+ * @return self
+ */
+ public static function fromDefaults()
+ {
+ return new self(require __DIR__ . '/Resources/public-endpoints.php');
+ }
+
+ public function __invoke(array $args = array())
+ {
+ if (!isset($args['service'])) {
+ throw new \InvalidArgumentException('Requires a "service" value');
+ }
+
+ if (!isset($args['region'])) {
+ throw new \InvalidArgumentException('Requires a "region" value');
+ }
+
+ foreach ($this->getKeys($args['region'], $args['service']) as $key) {
+ if (isset($this->patterns['endpoints'][$key])) {
+ return $this->expand($this->patterns['endpoints'][$key], $args);
+ }
+ }
+
+ throw new \RuntimeException('Could not resolve endpoint');
+ }
+
+ private function expand(array $config, array $args)
+ {
+ $scheme = isset($args['scheme']) ? $args['scheme'] : 'https';
+ $config['endpoint'] = $scheme . '://' . str_replace(
+ array('{service}', '{region}'),
+ array($args['service'], $args['region']),
+ $config['endpoint']
+ );
+
+ return $config;
+ }
+
+ private function getKeys($region, $service)
+ {
+ return array("$region/$service", "$region/*", "*/$service", "*/*");
+ }
+}
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;
*/
private function createSigningContext(RequestInterface $request, $payload)
{
+ $signable = array(
+ 'host' => true,
+ 'date' => true,
+ 'content-md5' => true
+ );
+
// Normalize the path as required by SigV4 and ensure it's absolute
$canon = $request->getMethod() . "\n"
. $this->createCanonicalizedPath($request) . "\n"
. $this->getCanonicalizedQueryString($request) . "\n";
- // Create the canonical headers
- $headers = array();
+ $canonHeaders = array();
+
foreach ($request->getHeaders()->getAll() as $key => $values) {
$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]);
+ if (isset($signable[$key]) || substr($key, 0, 6) === 'x-amz-') {
+ $values = $values->toArray();
+ if (count($values) == 1) {
+ $values = $values[0];
+ } else {
+ sort($values);
+ $values = implode(',', $values);
}
+ $canonHeaders[$key] = $key . ':' . preg_replace('/\s+/', ' ', $values);
}
}
- // The headers must be sorted
- ksort($headers);
-
- // Continue to build the canonical request by adding headers
- foreach ($headers as $key => $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{$payload}";
+ ksort($canonHeaders);
+ $signedHeadersString = implode(';', array_keys($canonHeaders));
+ $canon .= implode("\n", $canonHeaders) . "\n\n"
+ . $signedHeadersString . "\n"
+ . $payload;
return array(
'canonical_request' => $canon,
- 'signed_headers' => $signedHeaders
+ 'signed_headers' => $signedHeadersString
);
}
foreach ($queryParams as $key => $values) {
if (is_array($values)) {
sort($values);
+ } elseif ($values === 0) {
+ $values = array('0');
} elseif (!$values) {
$values = array('');
}
*
* @param array $data Array of ACP data
*
- * @return self
+ * @return Acp
*/
public static function fromArray(array $data)
{
*
* @param Grantee $owner ACP policy owner
*
- * @return self
+ * @return $this
*
* @throws InvalidArgumentException if the grantee does not have an ID set
*/
*
* @param array|\Traversable $grants List of grants for the ACP
*
- * @return self
+ * @return $this
*
* @throws InvalidArgumentException
*/
*
* @param Grant $grant Grant to add
*
- * @return self
+ * @return $this
*/
public function addGrant(Grant $grant)
{
*
* @param AbstractCommand $command Command to be updated
*
- * @return self
+ * @return $this
*/
public function updateCommand(AbstractCommand $command)
{
/**
* Static method for chainable instantiation
*
- * @return self
+ * @return static
*/
public static function newInstance()
{
- return new self;
+ return new static;
}
/**
* @param string $id Owner identifier
* @param string $displayName Owner display name
*
- * @return self
+ * @return $this
*/
public function setOwner($id, $displayName = null)
{
* @param string $id Grantee identifier
* @param string $displayName Grantee display name
*
- * @return self
+ * @return $this
*/
public function addGrantForUser($permission, $id, $displayName = null)
{
* @param string $permission Permission for the Grant
* @param string $email Grantee email address
*
- * @return self
+ * @return $this
*/
public function addGrantForEmail($permission, $email)
{
* @param string $permission Permission for the Grant
* @param string $group Grantee group
*
- * @return self
+ * @return $this
*/
public function addGrantForGroup($permission, $group)
{
* @param string $permission Permission for the Grant
* @param Grantee $grantee The Grantee for the Grant
*
- * @return self
+ * @return $this
*/
public function addGrant($permission, Grantee $grantee)
{
*
* @param string $bucket Name of the bucket to clear
*
- * @return self
+ * @return $this
*/
public function setBucket($bucket)
{
*
* @param \Iterator $iterator Iterator used to yield the keys to be deleted
*
- * @return self
+ * @return $this
*/
public function setIterator(\Iterator $iterator)
{
* @param string $mfa MFA token to send with each request. The value is the concatenation of the authentication
* device's serial number, a space, and the value displayed on your authentication device.
*
- * @return self
+ * @return $this
*/
public function setMfa($mfa)
{
* @param string $bucket Bucket that contains the objects to delete
* @param string $mfa MFA token to use with the request
*
- * @return self
+ * @return static
*/
public static function factory(AwsClientInterface $client, $bucket, $mfa = null)
{
->transferWith(new DeleteObjectsTransfer($client, $bucket, $mfa))
->build();
- return new self($batch);
+ return new static($batch);
}
/**
* @param string $key Key of the object
* @param string $versionId VersionID of the object
*
- * @return self
+ * @return $this
*/
public function addKey($key, $versionId = null)
{
throw new InvalidArgumentException('Item must be a DeleteObject command or array containing a Key and VersionId key.');
}
- return $this->decoratedBatch->add($item);
+ return parent::add($item);
}
}
*
* @param string $token MFA token
*
- * @return self
+ * @return $this
*/
public function setMfa($token)
{
*
* @param Grantee $grantee Affected grantee
*
- * @return self
+ * @return $this
*/
public function setGrantee(Grantee $grantee)
{
*
* @param string $permission Permission applied
*
- * @return self
+ * @return $this
*
* @throws InvalidArgumentException
*/
*/
public function getHeaderValue()
{
- $key = self::$headerMap[$this->type];
+ $key = static::$headerMap[$this->type];
return "{$key}=\"{$this->id}\"";
}
*
* @param string $bucket Name of the bucket
*
- * @return self
+ * @return $this
*/
public function setBucket($bucket)
{
*
* @param string $key Key of the object to upload
*
- * @return self
+ * @return $this
*/
public function setKey($key)
{
*
* @param int $minSize Minimum acceptable part size in bytes
*
- * @return self
+ * @return $this
*/
public function setMinPartSize($minSize)
{
*
* @param int $concurrency Concurrency level
*
- * @return self
+ * @return $this
*/
public function setConcurrency($concurrency)
{
*
* @param string $md5 MD5 hash of the entire body
*
- * @return self
+ * @return $this
*/
public function setMd5($md5)
{
*
* @param bool $calculateMd5 Set to true to calculate the MD5 hash of the body
*
- * @return self
+ * @return $this
*/
public function calculateMd5($calculateMd5)
{
*
* @param bool $usePartMd5 Set to true to calculate the MD5 has of each part
*
- * @return self
+ * @return $this
*/
public function calculatePartMd5($usePartMd5)
{
*
* @param Acp $acp ACP to set on the object
*
- * @return self
+ * @return $this
*/
public function setAcp(Acp $acp)
{
* @param string $name Option name
* @param string $value Option value
*
- * @return self
+ * @return $this
*/
public function setOption($name, $value)
{
*
* @param array $options Array of CreateMultipartUpload operation parameters
*
- * @return self
+ * @return $this
*/
public function addOptions(array $options)
{
*
* @param array $options Transfer options
*
- * @return self
+ * @return $this
*/
public function setTransferOptions(array $options)
{
'https' => true,
'hostname' => 's3-eu-west-1.amazonaws.com',
),
+ 'eu-central-1' => array(
+ 'http' => true,
+ 'https' => true,
+ 'hostname' => 's3-eu-central-1.amazonaws.com',
+ ),
'ap-northeast-1' => array(
'http' => true,
'https' => true,
'location' => 'header',
'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-key-MD5',
),
+ 'CopySourceSSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-copy-source-server-side-encryption-aws-kms-key-id',
+ ),
'ACP' => array(
'type' => 'object',
'additionalProperties' => true,
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'ACP' => array(
'type' => 'object',
'additionalProperties' => true,
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'SaveAs' => array(
'location' => 'response_body',
),
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
),
'errorResponses' => array(
array(
'location' => 'uri',
),
'TopicConfiguration' => array(
- 'required' => true,
'type' => 'object',
'location' => 'xml',
'properties' => array(
+ 'Id' => array(
+ 'type' => 'string',
+ ),
+ 'Events' => array(
+ 'type' => 'array',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Event',
+ 'type' => 'string',
+ ),
+ ),
'Event' => array(
'type' => 'string',
),
),
),
),
+ 'QueueConfiguration' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Id' => array(
+ 'type' => 'string',
+ ),
+ 'Event' => array(
+ 'type' => 'string',
+ ),
+ 'Events' => array(
+ 'type' => 'array',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Event',
+ 'type' => 'string',
+ ),
+ ),
+ 'Queue' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'CloudFunctionConfiguration' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Id' => array(
+ 'type' => 'string',
+ ),
+ 'Event' => array(
+ 'type' => 'string',
+ ),
+ 'Events' => array(
+ 'type' => 'array',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Event',
+ 'type' => 'string',
+ ),
+ ),
+ 'CloudFunction' => array(
+ 'type' => 'string',
+ ),
+ 'InvocationRole' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
),
),
'PutBucketPolicy' => array(
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'ACP' => array(
'type' => 'object',
'additionalProperties' => true,
'Aws\\S3\\S3Client::explodeKey',
),
),
+ 'VersionId' => array(
+ 'type' => 'string',
+ 'location' => 'query',
+ 'sentAs' => 'versionId',
+ ),
'Days' => array(
'required' => true,
'type' => 'numeric',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
),
),
'UploadPartCopy' => array(
'location' => 'header',
'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-key-MD5',
),
+ 'CopySourceSSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'command.expects' => array(
'static' => true,
'default' => 'application/xml',
'location' => 'header',
'sentAs' => 'x-amz-version-id',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'RequestId' => array(
'location' => 'header',
'sentAs' => 'x-amz-request-id',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'RequestId' => array(
'location' => 'header',
'sentAs' => 'x-amz-request-id',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'RequestId' => array(
'location' => 'header',
'sentAs' => 'x-amz-request-id',
'type' => 'object',
'location' => 'xml',
'properties' => array(
+ 'Id' => array(
+ 'type' => 'string',
+ ),
+ 'Events' => array(
+ 'type' => 'array',
+ 'sentAs' => 'Event',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Event',
+ 'type' => 'string',
+ 'sentAs' => 'Event',
+ ),
+ ),
'Event' => array(
'type' => 'string',
),
),
),
),
+ 'QueueConfiguration' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Id' => array(
+ 'type' => 'string',
+ ),
+ 'Event' => array(
+ 'type' => 'string',
+ ),
+ 'Events' => array(
+ 'type' => 'array',
+ 'sentAs' => 'Event',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Event',
+ 'type' => 'string',
+ 'sentAs' => 'Event',
+ ),
+ ),
+ 'Queue' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ 'CloudFunctionConfiguration' => array(
+ 'type' => 'object',
+ 'location' => 'xml',
+ 'properties' => array(
+ 'Id' => array(
+ 'type' => 'string',
+ ),
+ 'Event' => array(
+ 'type' => 'string',
+ ),
+ 'Events' => array(
+ 'type' => 'array',
+ 'sentAs' => 'Event',
+ 'data' => array(
+ 'xmlFlattened' => true,
+ ),
+ 'items' => array(
+ 'name' => 'Event',
+ 'type' => 'string',
+ 'sentAs' => 'Event',
+ ),
+ ),
+ 'CloudFunction' => array(
+ 'type' => 'string',
+ ),
+ 'InvocationRole' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
'RequestId' => array(
'location' => 'header',
'sentAs' => 'x-amz-request-id',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'RequestId' => array(
'location' => 'header',
'sentAs' => 'x-amz-request-id',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'RequestId' => array(
'location' => 'header',
'sentAs' => 'x-amz-request-id',
'type' => 'string',
'location' => 'xml',
),
+ 'Delimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
'NextUploadIdMarker' => array(
'type' => 'string',
'location' => 'xml',
'type' => 'string',
'location' => 'xml',
),
+ 'Delimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
'MaxKeys' => array(
'type' => 'numeric',
'location' => 'xml',
'type' => 'string',
'location' => 'xml',
),
+ 'Delimiter' => array(
+ 'type' => 'string',
+ 'location' => 'xml',
+ ),
'MaxKeys' => array(
'type' => 'numeric',
'location' => 'xml',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'RequestId' => array(
'location' => 'header',
'sentAs' => 'x-amz-request-id',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'RequestId' => array(
'location' => 'header',
'sentAs' => 'x-amz-request-id',
'location' => 'header',
'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
),
+ 'SSEKMSKeyId' => array(
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
+ ),
'RequestId' => array(
'location' => 'header',
'sentAs' => 'x-amz-request-id',
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;
*
* @param array|Collection $config Client configuration data
*
- * @return self
+ * @return S3Client
* @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] = self::createBackoffPlugin($exceptionParser);
+ $config[Options::BACKOFF] = static::createBackoffPlugin($exceptionParser);
}
- $config[Options::SIGNATURE] = $signature = self::createSignature($config);
+ $config[Options::SIGNATURE] = $signature = static::createSignature($config);
$client = ClientBuilder::factory(__NAMESPACE__)
->setConfig($config)
// Add aliases for some S3 operations
$default = CompositeFactory::getDefaultChain($client);
$default->add(
- new AliasFactory($client, self::$commandAliases),
+ new AliasFactory($client, static::$commandAliases),
'Guzzle\Service\Command\Factory\ServiceDescriptionFactory'
);
$client->setCommandFactory($default);
{
$currentValue = isset($config[Options::SIGNATURE]) ? $config[Options::SIGNATURE] : null;
+ // Force v4 if no value is provided, a region is in the config, and
+ // the region starts with "cn-" or "eu-central-".
+ $requiresV4 = !$currentValue
+ && isset($config['region'])
+ && (strpos($config['region'], 'eu-central-') === 0
+ || strpos($config['region'], 'cn-') === 0);
+
// 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-')
- ) {
+ if ($currentValue == 'v4' || $requiresV4) {
// Force SignatureV4 for specific regions or if specified in the config
$currentValue = new S3SignatureV4('s3');
} elseif (!$currentValue || $currentValue == 's3') {
/**
* Register the Amazon S3 stream wrapper and associates it with this client object
*
- * @return self
+ * @return $this
*/
public function registerStreamWrapper()
{
->setTransferOptions($options->toArray())
->addOptions($options['params'])
->setOption('ACL', $acl)
- ->build()
- ->upload();
+ ->build();
if ($options['before_upload']) {
$transfer->getEventDispatcher()->addListener(
);
}
- return $transfer;
+ return $transfer->upload();
}
/**
public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
{
if (!$request->hasHeader('x-amz-content-sha256')) {
- $request->setHeader('x-amz-content-sha256', $this->getPresignedPayload($request));
+ $request->setHeader(
+ 'x-amz-content-sha256',
+ $this->getPayload($request)
+ );
}
parent::signRequest($request, $credentials);
*/
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;
+ return 'UNSIGNED-PAYLOAD';
}
/**
}
stream_wrapper_register('s3', get_called_class(), STREAM_IS_URL);
- self::$client = $client;
+ static::$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())) {
+ if ($mode == 'x' && static::$client->doesObjectExist($params['Bucket'], $params['Key'], $this->getOptions())) {
$errors[] = "{$path} already exists on Amazon S3";
}
}
try {
- self::$client->putObject($params);
+ static::$client->putObject($params);
return true;
} catch (\Exception $e) {
return $this->triggerError($e->getMessage());
{
try {
$this->clearStatInfo($path);
- self::$client->deleteObject($this->getParams($path));
+ static::$client->deleteObject($this->getParams($path));
return true;
} catch (\Exception $e) {
return $this->triggerError($e->getMessage());
public function url_stat($path, $flags)
{
// Check if this path is in the url_stat cache
- if (isset(self::$nextStat[$path])) {
- return self::$nextStat[$path];
+ if (isset(static::$nextStat[$path])) {
+ return static::$nextStat[$path];
}
$parts = $this->getParams($path);
if (!$parts['Key']) {
// Stat "directories": buckets, or "s3://"
- if (!$parts['Bucket'] || self::$client->doesBucketExist($parts['Bucket'])) {
+ if (!$parts['Bucket'] || static::$client->doesBucketExist($parts['Bucket'])) {
return $this->formatUrlStat($path);
} else {
return $this->triggerError("File or directory not found: {$path}", $flags);
try {
try {
- $result = self::$client->headObject($parts)->toArray();
+ $result = static::$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);
}
} 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(
+ $result = static::$client->listObjects(array(
'Bucket' => $parts['Bucket'],
'Prefix' => rtrim($parts['Key'], '/') . '/',
'MaxKeys' => 1
try {
if (!$params['Key']) {
- self::$client->deleteBucket(array('Bucket' => $params['Bucket']));
+ static::$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(
+ $result = static::$client->listObjects(array(
'Bucket' => $params['Bucket'],
'Prefix' => $prefix,
'MaxKeys' => 1
$operationParams['Delimiter'] = $delimiter;
}
- $objectIterator = self::$client->getIterator('ListObjects', $operationParams, array(
+ $objectIterator = static::$client->getIterator('ListObjects', $operationParams, array(
'return_prefixes' => true,
'sort_results' => true
));
// Cache the object data for quick url_stat lookups used with
// RecursiveDirectoryIterator.
- self::$nextStat = array($key => $stat);
+ static::$nextStat = array($key => $stat);
$this->objectIterator->next();
return $result;
try {
// Copy the object and allow overriding default parameters if desired, but by default copy metadata
- self::$client->copyObject($this->getOptions() + array(
+ static::$client->copyObject($this->getOptions() + array(
'Bucket' => $partsTo['Bucket'],
'Key' => $partsTo['Key'],
'CopySource' => '/' . $partsFrom['Bucket'] . '/' . rawurlencode($partsFrom['Key']),
'MetadataDirective' => 'COPY'
));
// Delete the original object
- self::$client->deleteObject(array(
+ static::$client->deleteObject(array(
'Bucket' => $partsFrom['Bucket'],
'Key' => $partsFrom['Key']
) + $this->getOptions());
protected function openReadStream(array $params, array &$errors)
{
// Create the command and serialize the request
- $request = $this->getSignedRequest(self::$client->getCommand('GetObject', $params));
+ $request = $this->getSignedRequest(static::$client->getCommand('GetObject', $params));
// Create a stream that uses the EntityBody object
$factory = $this->getOption('stream_factory') ?: new PhpStreamRequestFactory();
$this->body = $factory->fromRequest($request, array(), array('stream_class' => 'Guzzle\Http\EntityBody'));
{
try {
// Get the body of the object
- $this->body = self::$client->getObject($params)->get('Body');
+ $this->body = static::$client->getObject($params)->get('Body');
$this->body->seek(0, SEEK_END);
} catch (S3Exception $e) {
// The object does not exist, so use a simple write stream
*/
protected function clearStatInfo($path = null)
{
- self::$nextStat = array();
+ static::$nextStat = array();
if ($path) {
clearstatcache(true, $path);
}
*/
private function createBucket($path, array $params)
{
- if (self::$client->doesBucketExist($params['Bucket'])) {
+ if (static::$client->doesBucketExist($params['Bucket'])) {
return $this->triggerError("Directory already exists: {$path}");
}
try {
- self::$client->createBucket($params);
+ static::$client->createBucket($params);
$this->clearStatInfo($path);
return true;
} catch (\Exception $e) {
$params['Body'] = '';
// Fail if this pseudo directory key already exists
- if (self::$client->doesObjectExist($params['Bucket'], $params['Key'])) {
+ if (static::$client->doesObjectExist($params['Bucket'], $params['Key'])) {
return $this->triggerError("Directory already exists: {$path}");
}
try {
- self::$client->putObject($params);
+ static::$client->putObject($params);
$this->clearStatInfo($path);
return true;
} catch (\Exception $e) {
protected $debug;
/**
- * @return self
+ * @return static
*/
public static function getInstance()
{
*
* @param string $bucket Amazon S3 bucket name
*
- * @return self
+ * @return $this
*/
public function setBucket($bucket)
{
*
* @param S3Client $client Amazon S3 client
*
- * @return self
+ * @return $this
*/
public function setClient(S3Client $client)
{
*
* @param \Iterator $iterator
*
- * @return self
+ * @return $this
*/
public function setSourceIterator(\Iterator $iterator)
{
*
* @param FileNameConverterInterface $converter Filename to object key provider
*
- * @return self
+ * @return $this
*/
public function setSourceFilenameConverter(FilenameConverterInterface $converter)
{
*
* @param FileNameConverterInterface $converter Filename to object key provider
*
- * @return self
+ * @return $this
*/
public function setTargetFilenameConverter(FilenameConverterInterface $converter)
{
*
* @param string $baseDir Base directory, which will be deleted from each uploaded object key
*
- * @return self
+ * @return $this
*/
public function setBaseDir($baseDir)
{
*
* @param string $keyPrefix Prefix for each uploaded key
*
- * @return self
+ * @return $this
*/
public function setKeyPrefix($keyPrefix)
{
*
* @param string $delimiter Delimiter to use to separate paths
*
- * @return self
+ * @return $this
*/
public function setDelimiter($delimiter)
{
*
* @param array $params Associative array of PutObject (upload) GetObject (download) parameters
*
- * @return self
+ * @return $this
*/
public function setOperationParams(array $params)
{
*
* @param int $concurrency Number of concurrent transfers
*
- * @return self
+ * @return $this
*/
public function setConcurrency($concurrency)
{
*
* @param bool $force Set to true to force transfers without checking if it has changed
*
- * @return self
+ * @return $this
*/
public function force($force = false)
{
*
* @param bool|resource $enabledOrResource Set to true or false to enable or disable debug output. Pass an opened
* fopen resource to write to instead of writing to standard out.
- * @return self
+ * @return $this
*/
public function enableDebugOutput($enabledOrResource = true)
{
*
* @param string $search Regular expression search (in preg_match format). Any filename that matches this regex
* will not be transferred.
- * @return self
+ * @return $this
*/
public function addRegexFilter($search)
{
/**
* Hook to implement in subclasses
*
- * @return self
+ * @return AbstractSync
*/
abstract protected function specificBuild();
*
* @param string $directory Directory
*
- * @return self
+ * @return $this
*/
public function setDirectory($directory)
{
*
* @param string $path Path that contains files to upload
*
- * @return self
+ * @return $this
*/
public function uploadFromDirectory($path)
{
*
* @param string $glob Glob expression
*
- * @return self
+ * @return $this
* @link http://www.php.net/manual/en/function.glob.php
*/
public function uploadFromGlob($glob)
*
* @param string $acl Canned ACL for each upload
*
- * @return self
+ * @return $this
*/
public function setAcl($acl)
{
*
* @param Acp $acp Access control policy
*
- * @return self
+ * @return $this
*/
public function setAcp(Acp $acp)
{
*
* @param int $size Size threshold
*
- * @return self
+ * @return $this
*/
public function setMultipartUploadSize($size)
{
* ApcClassLoader implements a wrapping autoloader cached in APC for PHP 5.3.
*
* It expects an object implementing a findFile method to find the file. This
- * allow using it as a wrapper around the other loaders of the component (the
+ * allows using it as a wrapper around the other loaders of the component (the
* ClassLoader and the UniversalClassLoader for instance) but also around any
* other autoloader following this convention (the Composer one for instance)
*
/**
* The class loader object being decorated.
*
- * @var \Symfony\Component\ClassLoader\ClassLoader
+ * @var object
* A class loader object that implements the findFile() method.
*/
protected $decorated;
{
return call_user_func_array(array($this->decorated, $method), $args);
}
-
}
foreach ($classes as $class) {
$map[$class] = $path;
}
-
}
return $map;
public function addPrefix($prefix, $baseDir)
{
$prefix = trim($prefix, '\\').'\\';
- $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+ $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
$this->prefixes[] = array($prefix, $baseDir);
}
list($currentPrefix, $currentBaseDir) = $current;
if (0 === strpos($class, $currentPrefix)) {
$classWithoutPrefix = substr($class, strlen($currentPrefix));
- $file = $currentBaseDir . str_replace('\\', DIRECTORY_SEPARATOR, $classWithoutPrefix) . '.php';
+ $file = $currentBaseDir.str_replace('\\', DIRECTORY_SEPARATOR, $classWithoutPrefix).'.php';
if (file_exists($file)) {
return $file;
}
First, register the autoloader:
- require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
+```php
+require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
- use Symfony\Component\ClassLoader\UniversalClassLoader;
+use Symfony\Component\ClassLoader\UniversalClassLoader;
- $loader = new UniversalClassLoader();
- $loader->register();
+$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');
+```php
+$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');
+```php
+$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'));
+```php
+$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',
- ));
+```php
+$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';
+```php
+require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
+require_once __DIR__.'/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php';
- use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
+use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
- $loader = new ApcUniversalClassLoader('apc.prefix.');
+$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
$this->assertTrue(class_exists($className), $message);
}
- public function getLoadClassTests()
- {
- return array(
+ public function getLoadClassTests()
+ {
+ return array(
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'),
);
- }
+ }
/**
* @dataProvider getLoadClassFromFallbackTests
$this->assertTrue(class_exists($className), $message);
}
- public function getLoadClassFromFallbackTests()
- {
- return array(
+ public function getLoadClassFromFallbackTests()
+ {
+ return array(
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_Pearlike_FooBar', 'Apc_Pearlike_FooBar', '->loadClass() loads Apc_Pearlike_Baz class from fallback dir'),
);
- }
+ }
/**
* @dataProvider getLoadClassNamespaceCollisionTests
$this->assertTrue(class_exists($className), $message);
}
- public function getLoadClassNamespaceCollisionTests()
- {
- return array(
+ public function getLoadClassNamespaceCollisionTests()
+ {
+ return array(
array(
array(
'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha',
'->loadClass() loads NamespaceCollision\A\B\Bar from beta.',
),
);
- }
+ }
/**
* @dataProvider getLoadClassPrefixCollisionTests
$this->assertTrue(class_exists($className), $message);
}
- public function getLoadClassPrefixCollisionTests()
- {
- return array(
+ public function getLoadClassPrefixCollisionTests()
+ {
+ return array(
array(
array(
'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc',
'->loadClass() loads ApcPrefixCollision_A_B_Bar from beta.',
),
);
- }
+ }
}
'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php',
'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php',
'Namespaced\\WithComments' => realpath(__DIR__).'/Fixtures/Namespaced/WithComments.php',
- )
+ ),
),
array(__DIR__.'/Fixtures/beta/NamespaceCollision', array(
'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php',
namespace ClassesWithParents;
-class A extends B {}
+class A extends B
+{
+}
namespace ClassesWithParents;
-class B implements CInterface {}
+class B implements CInterface
+{
+}
class SomeClass extends SomeParent implements SomeInterface
{
-
}
interface SomeInterface
{
-
}
abstract class SomeParent
{
-
}
<?php
namespace {
- class A {}
+ class A
+ {
+ }
}
namespace Alpha {
- class A {}
- class B {}
+ class A
+ {
+ }
+ class B
+ {
+ }
}
namespace Beta {
- class A {}
- class B {}
+ class A
+ {
+ }
+ class B
+ {
+ }
}
namespace Foo\Bar;
-class A {}
-class B {}
+class A
+{
+}
+class B
+{
+}
<?php
trait TD
-{}
+{
+}
trait TZ
{
class CBar implements IBar
{
- use TBar, TFooBar;
+ use TBar;
+ use TFooBar;
}
}
$loader = new Psr4ClassLoader();
$loader->addPrefix(
'Acme\\DemoLib',
- __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'psr-4'
+ __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'psr-4'
);
$loader->loadClass($className);
$this->assertTrue(class_exists($className), sprintf('loadClass() should load %s', $className));
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')
+ array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Class_With_Underscores'),
);
}
$loader = new Psr4ClassLoader();
$loader->addPrefix(
'Acme\\DemoLib',
- __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'psr-4'
+ __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(
array('Acme\\DemoLib\\I_Do_Not_Exist'),
- array('UnknownVendor\\SomeLib\\I_Do_Not_Exist')
+ array('UnknownVendor\\SomeLib\\I_Do_Not_Exist'),
);
}
}
return $file;
}
}
-
} else {
// PEAR-like class name
$normalizedClass = str_replace('_', DIRECTORY_SEPARATOR, $class).'.php';
namespace Symfony\Component\ClassLoader;
/**
- * XcacheClassLoader implements a wrapping autoloader cached in Xcache for PHP 5.3.
+ * XcacheClassLoader implements a wrapping autoloader cached in XCache for PHP 5.3.
*
* It expects an object implementing a findFile method to find the file. This
* allows using it as a wrapper around the other loaders of the component (the
class XcacheClassLoader
{
private $prefix;
- private $classFinder;
+
+ /**
+ * @var object A class loader object that implements the findFile() method
+ */
+ private $decorated;
/**
* Constructor.
*
- * @param string $prefix A prefix to create a namespace in Xcache
- * @param object $classFinder An object that implements findFile() method.
+ * @param string $prefix The XCache namespace prefix to use.
+ * @param object $decorated A class loader object that implements the findFile() method.
*
* @throws \RuntimeException
* @throws \InvalidArgumentException
*
* @api
*/
- public function __construct($prefix, $classFinder)
+ public function __construct($prefix, $decorated)
{
- if (!extension_loaded('Xcache')) {
- throw new \RuntimeException('Unable to use XcacheClassLoader as Xcache is not enabled.');
+ if (!extension_loaded('xcache')) {
+ throw new \RuntimeException('Unable to use XcacheClassLoader as XCache is not enabled.');
}
- if (!method_exists($classFinder, 'findFile')) {
+ if (!method_exists($decorated, 'findFile')) {
throw new \InvalidArgumentException('The class finder must implement a "findFile" method.');
}
$this->prefix = $prefix;
- $this->classFinder = $classFinder;
+ $this->decorated = $decorated;
}
/**
if (xcache_isset($this->prefix.$class)) {
$file = xcache_get($this->prefix.$class);
} else {
- $file = $this->classFinder->findFile($class);
+ $file = $this->decorated->findFile($class);
xcache_set($this->prefix.$class, $file);
}
return $file;
}
+
+ /**
+ * Passes through all unknown calls onto the decorated object.
+ */
+ public function __call($method, $args)
+ {
+ return call_user_func_array(array($this->decorated, $method), $args);
+ }
}
if ($listener instanceof \Closure) {
$info += array(
'type' => 'Closure',
- 'pretty' => 'closure'
+ 'pretty' => 'closure',
);
} elseif (is_string($listener)) {
try {
/**
* Removes an event listener from the specified events.
*
- * @param string|array $eventName The event(s) to remove a listener from
- * @param callable $listener The listener to remove
+ * @param string $eventName The event to remove a listener from
+ * @param callable $listener The listener to remove
*/
public function removeListener($eventName, $listener);
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;
+```php
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\Event;
- $dispatcher = new EventDispatcher();
+$dispatcher = new EventDispatcher();
- $dispatcher->addListener('event_name', function (Event $event) {
- // ...
- });
+$dispatcher->addListener('event_name', function (Event $event) {
+ // ...
+});
- $dispatcher->dispatch('event_name');
+$dispatcher->dispatch('event_name');
+```
Resources
---------
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
- $tdispatcher->addListener('foo', $listener = function () { ; });
+ $tdispatcher->addListener('foo', $listener = function () {; });
$listeners = $dispatcher->getListeners('foo');
$this->assertCount(1, $listeners);
$this->assertSame($listener, $listeners[0]);
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
- $tdispatcher->addListener('foo', $listener = function () { ; });
+ $tdispatcher->addListener('foo', $listener = function () {; });
$this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo'));
}
$this->assertFalse($dispatcher->hasListeners('foo'));
$this->assertFalse($tdispatcher->hasListeners('foo'));
- $tdispatcher->addListener('foo', $listener = function () { ; });
+ $tdispatcher->addListener('foo', $listener = function () {; });
$this->assertTrue($dispatcher->hasListeners('foo'));
$this->assertTrue($tdispatcher->hasListeners('foo'));
}
{
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
- $tdispatcher->addListener('foo', $listener = function () { ; });
+ $tdispatcher->addListener('foo', $listener = function () {; });
$this->assertEquals(array(), $tdispatcher->getCalledListeners());
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure')), $tdispatcher->getNotCalledListeners());
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
- $tdispatcher->addListener('foo', $listener1 = function () { ; });
- $tdispatcher->addListener('foo', $listener2 = function () { ; });
+ $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\".");
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
$tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); });
- $tdispatcher->addListener('foo', $listener2 = 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("Listener \"closure\" stopped propagation of the event \"foo\".");
->method('getClass')
->will($this->returnValue('stdClass'));
- $builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
+ $builder = $this->getMock(
+ 'Symfony\Component\DependencyInjection\ContainerBuilder',
+ array('hasDefinition', 'findTaggedServiceIds', 'getDefinition')
+ );
$builder->expects($this->any())
->method('hasDefinition')
->will($this->returnValue(true));
->method('getClass')
->will($this->returnValue('Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService'));
- $builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
+ $builder = $this->getMock(
+ 'Symfony\Component\DependencyInjection\ContainerBuilder',
+ array('hasDefinition', 'findTaggedServiceIds', 'getDefinition', 'findDefinition')
+ );
$builder->expects($this->any())
->method('hasDefinition')
->will($this->returnValue(true));
class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
{
- public static function getSubscribedEvents() {}
+ public static function getSubscribedEvents()
+ {
+ }
}
{
return array('pre.foo' => array(
array('preFoo1'),
- array('preFoo2', 10)
+ array('preFoo2', 10),
));
}
}
*/
class GenericEventTest extends \PHPUnit_Framework_TestCase
{
-
/**
* @var GenericEvent
*/
"php": ">=5.3.3"
},
"require-dev": {
- "symfony/dependency-injection": "~2.0",
+ "symfony/dependency-injection": "~2.0,<2.6.0",
"symfony/config": "~2.0",
"symfony/stopwatch": "~2.2",
"psr/log": "~1.0"