Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?phpnamespace 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 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,$endpointProvider = null,$clientArgs = null){$operation = $this->api->getOperation($command->getName());$commandArgs = $command->toArray();$opts = $this->serialize($operation, $commandArgs);$headers = isset($opts['headers']) ? $opts['headers'] : [];if ($endpointProvider instanceof EndpointProviderV2) {$this->setRequestOptions($endpointProvider,$command,$operation,$commandArgs,$clientArgs,$headers);$this->endpoint = new Uri($this->endpoint);}$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 presentif ($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 endpointProviderV2if ($this->api->isModifiedModel()&& $this->api->getServiceName() === 's3') {if (substr($path, -1) === '/' && $relative[0] === '/') {$path = rtrim($path, '/');}$relative = $path . $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 resolvedif ($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;}}