| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 | 
							- <?php
 
- namespace Aws\Api\Serializer;
 
- use Aws\Api\MapShape;
 
- use Aws\Api\Service;
 
- use Aws\Api\Operation;
 
- use Aws\Api\Shape;
 
- use Aws\Api\StructureShape;
 
- use Aws\Api\TimestampShape;
 
- use Aws\CommandInterface;
 
- use Aws\EndpointV2\EndpointProviderV2;
 
- use Aws\EndpointV2\EndpointV2SerializerTrait;
 
- use Aws\EndpointV2\Ruleset\RulesetEndpoint;
 
- use GuzzleHttp\Psr7;
 
- use GuzzleHttp\Psr7\Request;
 
- use GuzzleHttp\Psr7\Uri;
 
- use GuzzleHttp\Psr7\UriResolver;
 
- use Psr\Http\Message\RequestInterface;
 
- /**
 
-  * Serializes HTTP locations like header, uri, payload, etc...
 
-  * @internal
 
-  */
 
- abstract class RestSerializer
 
- {
 
-     use EndpointV2SerializerTrait;
 
-     /** @var Service */
 
-     private $api;
 
-     /** @var Uri */
 
-     private $endpoint;
 
-     /**
 
-      * @param Service $api      Service API description
 
-      * @param string  $endpoint Endpoint to connect to
 
-      */
 
-     public function __construct(Service $api, $endpoint)
 
-     {
 
-         $this->api = $api;
 
-         $this->endpoint = Psr7\Utils::uriFor($endpoint);
 
-     }
 
-     /**
 
-      * @param CommandInterface $command Command to serialize into a request.
 
-      * @param $endpointProvider Provider used for dynamic endpoint resolution.
 
-      * @param $clientArgs Client arguments used for dynamic endpoint resolution.
 
-      *
 
-      * @return RequestInterface
 
-      */
 
-     public function __invoke(
 
-         CommandInterface $command,
 
-         $endpoint = null
 
-     )
 
-     {
 
-         $operation = $this->api->getOperation($command->getName());
 
-         $commandArgs = $command->toArray();
 
-         $opts = $this->serialize($operation, $commandArgs);
 
-         $headers = isset($opts['headers']) ? $opts['headers'] : [];
 
-         if ($endpoint instanceof RulesetEndpoint) {
 
-             $this->setEndpointV2RequestOptions($endpoint, $headers);
 
-         }
 
-         $uri = $this->buildEndpoint($operation, $commandArgs, $opts);
 
-         return new Request(
 
-             $operation['http']['method'],
 
-             $uri,
 
-             $headers,
 
-             isset($opts['body']) ? $opts['body'] : null
 
-         );
 
-     }
 
-     /**
 
-      * Modifies a hash of request options for a payload body.
 
-      *
 
-      * @param StructureShape   $member  Member to serialize
 
-      * @param array            $value   Value to serialize
 
-      * @param array            $opts    Request options to modify.
 
-      */
 
-     abstract protected function payload(
 
-         StructureShape $member,
 
-         array $value,
 
-         array &$opts
 
-     );
 
-     private function serialize(Operation $operation, array $args)
 
-     {
 
-         $opts = [];
 
-         $input = $operation->getInput();
 
-         // Apply the payload trait if present
 
-         if ($payload = $input['payload']) {
 
-             $this->applyPayload($input, $payload, $args, $opts);
 
-         }
 
-         foreach ($args as $name => $value) {
 
-             if ($input->hasMember($name)) {
 
-                 $member = $input->getMember($name);
 
-                 $location = $member['location'];
 
-                 if (!$payload && !$location) {
 
-                     $bodyMembers[$name] = $value;
 
-                 } elseif ($location == 'header') {
 
-                     $this->applyHeader($name, $member, $value, $opts);
 
-                 } elseif ($location == 'querystring') {
 
-                     $this->applyQuery($name, $member, $value, $opts);
 
-                 } elseif ($location == 'headers') {
 
-                     $this->applyHeaderMap($name, $member, $value, $opts);
 
-                 }
 
-             }
 
-         }
 
-         if (isset($bodyMembers)) {
 
-             $this->payload($operation->getInput(), $bodyMembers, $opts);
 
-         } else if (!isset($opts['body']) && $this->hasPayloadParam($input, $payload)) {
 
-             $this->payload($operation->getInput(), [], $opts);
 
-         }
 
-         return $opts;
 
-     }
 
-     private function applyPayload(StructureShape $input, $name, array $args, array &$opts)
 
-     {
 
-         if (!isset($args[$name])) {
 
-             return;
 
-         }
 
-         $m = $input->getMember($name);
 
-         if ($m['streaming'] ||
 
-            ($m['type'] == 'string' || $m['type'] == 'blob')
 
-         ) {
 
-             // Streaming bodies or payloads that are strings are
 
-             // always just a stream of data.
 
-             $opts['body'] = Psr7\Utils::streamFor($args[$name]);
 
-             return;
 
-         }
 
-         $this->payload($m, $args[$name], $opts);
 
-     }
 
-     private function applyHeader($name, Shape $member, $value, array &$opts)
 
-     {
 
-         if ($member->getType() === 'timestamp') {
 
-             $timestampFormat = !empty($member['timestampFormat'])
 
-                 ? $member['timestampFormat']
 
-                 : 'rfc822';
 
-             $value = TimestampShape::format($value, $timestampFormat);
 
-         } elseif ($member->getType() === 'boolean') {
 
-             $value = $value ? 'true' : 'false';
 
-         }
 
-         if ($member['jsonvalue']) {
 
-             $value = json_encode($value);
 
-             if (empty($value) && JSON_ERROR_NONE !== json_last_error()) {
 
-                 throw new \InvalidArgumentException('Unable to encode the provided value'
 
-                     . ' with \'json_encode\'. ' . json_last_error_msg());
 
-             }
 
-             $value = base64_encode($value);
 
-         }
 
-         $opts['headers'][$member['locationName'] ?: $name] = $value;
 
-     }
 
-     /**
 
-      * Note: This is currently only present in the Amazon S3 model.
 
-      */
 
-     private function applyHeaderMap($name, Shape $member, array $value, array &$opts)
 
-     {
 
-         $prefix = $member['locationName'];
 
-         foreach ($value as $k => $v) {
 
-             $opts['headers'][$prefix . $k] = $v;
 
-         }
 
-     }
 
-     private function applyQuery($name, Shape $member, $value, array &$opts)
 
-     {
 
-         if ($member instanceof MapShape) {
 
-             $opts['query'] = isset($opts['query']) && is_array($opts['query'])
 
-                 ? $opts['query'] + $value
 
-                 : $value;
 
-         } elseif ($value !== null) {
 
-             $type = $member->getType();
 
-             if ($type === 'boolean') {
 
-                 $value = $value ? 'true' : 'false';
 
-             } elseif ($type === 'timestamp') {
 
-                 $timestampFormat = !empty($member['timestampFormat'])
 
-                     ? $member['timestampFormat']
 
-                     : 'iso8601';
 
-                 $value = TimestampShape::format($value, $timestampFormat);
 
-             }
 
-             $opts['query'][$member['locationName'] ?: $name] = $value;
 
-         }
 
-     }
 
-     private function buildEndpoint(Operation $operation, array $args, array $opts)
 
-     {
 
-         // Create an associative array of variable definitions used in expansions
 
-         $varDefinitions = $this->getVarDefinitions($operation, $args);
 
-         $relative = preg_replace_callback(
 
-             '/\{([^\}]+)\}/',
 
-             function (array $matches) use ($varDefinitions) {
 
-                 $isGreedy = substr($matches[1], -1, 1) == '+';
 
-                 $k = $isGreedy ? substr($matches[1], 0, -1) : $matches[1];
 
-                 if (!isset($varDefinitions[$k])) {
 
-                     return '';
 
-                 }
 
-                 if ($isGreedy) {
 
-                     return str_replace('%2F', '/', rawurlencode($varDefinitions[$k]));
 
-                 }
 
-                 return rawurlencode($varDefinitions[$k]);
 
-             },
 
-             $operation['http']['requestUri']
 
-         );
 
-         // Add the query string variables or appending to one if needed.
 
-         if (!empty($opts['query'])) {
 
-            $relative = $this->appendQuery($opts['query'], $relative);
 
-         }
 
-         $path = $this->endpoint->getPath();
 
-         //Accounts for trailing '/' in path when custom endpoint
 
-         //is provided to endpointProviderV2
 
-         if ($this->api->isModifiedModel()
 
-             && $this->api->getServiceName() === 's3'
 
-         ) {
 
-             if (substr($path, -1) === '/' && $relative[0] === '/') {
 
-                 $path = rtrim($path, '/');
 
-             }
 
-             $relative = $path . $relative;
 
-             if (strpos($relative, '../') !== false
 
-                 || substr($relative, -2) === '..'
 
-             ) {
 
-                 if ($relative[0] !== '/') {
 
-                     $relative = '/' . $relative;
 
-                 }
 
-                 return new Uri($this->endpoint->withPath('') . $relative);
 
-             }
 
-         }
 
-         // If endpoint has path, remove leading '/' to preserve URI resolution.
 
-         if ($path && $relative[0] === '/') {
 
-             $relative = substr($relative, 1);
 
-         }
 
-         //Append path to endpoint when leading '//...'
 
-         // present as uri cannot be properly resolved
 
-         if ($this->api->isModifiedModel()
 
-             && strpos($relative, '//') === 0
 
-         ) {
 
-             return new Uri($this->endpoint . $relative);
 
-         }
 
-         // Expand path place holders using Amazon's slightly different URI
 
-         // template syntax.
 
-         return UriResolver::resolve($this->endpoint, new Uri($relative));
 
-     }
 
-     /**
 
-      * @param StructureShape $input
 
-      */
 
-     private function hasPayloadParam(StructureShape $input, $payload)
 
-     {
 
-         if ($payload) {
 
-             $potentiallyEmptyTypes = ['blob','string'];
 
-             if ($this->api->getMetadata('protocol') == 'rest-xml') {
 
-                 $potentiallyEmptyTypes[] = 'structure';
 
-             }
 
-             $payloadMember = $input->getMember($payload);
 
-             if (in_array($payloadMember['type'], $potentiallyEmptyTypes)) {
 
-                 return false;
 
-             }
 
-         }
 
-         foreach ($input->getMembers() as $member) {
 
-             if (!isset($member['location'])) {
 
-                 return true;
 
-             }
 
-         }
 
-         return false;
 
-     }
 
-     private function appendQuery($query, $endpoint)
 
-     {
 
-         $append = Psr7\Query::build($query);
 
-         return $endpoint .= strpos($endpoint, '?') !== false ? "&{$append}" : "?{$append}";
 
-     }
 
-     private function getVarDefinitions($command, $args)
 
-     {
 
-         $varDefinitions = [];
 
-         foreach ($command->getInput()->getMembers() as $name => $member) {
 
-             if ($member['location'] == 'uri') {
 
-                 $varDefinitions[$member['locationName'] ?: $name] =
 
-                     isset($args[$name])
 
-                         ? $args[$name]
 
-                         : null;
 
-             }
 
-         }
 
-         return $varDefinitions;
 
-     }
 
- }
 
 
  |