<?php
namespace Aws\Api\ErrorParser;

use Aws\Api\Parser\MetadataParserTrait;
use Aws\Api\Parser\PayloadParserTrait;
use Aws\Api\Service;
use Aws\Api\StructureShape;
use Aws\CommandInterface;
use Psr\Http\Message\ResponseInterface;

abstract class AbstractErrorParser
{
    use MetadataParserTrait;
    use PayloadParserTrait;

    /**
     * @var Service
     */
    protected $api;

    /**
     * @param Service $api
     */
    public function __construct(Service $api = null)
    {
        $this->api = $api;
    }

    abstract protected function payload(
        ResponseInterface $response,
        StructureShape $member
    );

    protected function extractPayload(
        StructureShape $member,
        ResponseInterface $response
    ) {
        if ($member instanceof StructureShape) {
            // Structure members parse top-level data into a specific key.
            return $this->payload($response, $member);
        } else {
            // Streaming data is just the stream from the response body.
            return $response->getBody();
        }
    }

    protected function populateShape(
        array &$data,
        ResponseInterface $response,
        CommandInterface $command = null
    ) {
        $data['body'] = [];

        if (!empty($command) && !empty($this->api)) {

            // If modeled error code is indicated, check for known error shape
            if (!empty($data['code'])) {

                $errors = $this->api->getOperation($command->getName())->getErrors();
                foreach ($errors as $key => $error) {

                    // If error code matches a known error shape, populate the body
                    if ($data['code'] == $error['name']
                        && $error instanceof StructureShape
                    ) {
                        $modeledError = $error;
                        $data['body'] = $this->extractPayload(
                            $modeledError,
                            $response
                        );
                        $data['error_shape'] = $modeledError;

                        foreach ($error->getMembers() as $name => $member) {
                            switch ($member['location']) {
                                case 'header':
                                    $this->extractHeader($name, $member, $response, $data['body']);
                                    break;
                                case 'headers':
                                    $this->extractHeaders($name, $member, $response, $data['body']);
                                    break;
                                case 'statusCode':
                                    $this->extractStatus($name, $response, $data['body']);
                                    break;
                            }
                        }

                        break;
                    }
                }
            }
        }

        return $data;
    }
}


<?php
namespace Aws\Api\ErrorParser;

use Aws\Api\Parser\PayloadParserTrait;
use Aws\Api\StructureShape;
use Psr\Http\Message\ResponseInterface;

/**
 * Provides basic JSON error parsing functionality.
 */
trait JsonParserTrait
{
    use PayloadParserTrait;

    private function genericHandler(ResponseInterface $response)
    {
        $code = (string) $response->getStatusCode();
        if ($this->api
            && !is_null($this->api->getMetadata('awsQueryCompatible'))
            && $response->getHeaderLine('x-amzn-query-error')
        ) {
            $queryError = $response->getHeaderLine('x-amzn-query-error');
            $parts = explode(';', $queryError);
            if (isset($parts) && count($parts) == 2 && $parts[0] && $parts[1]) {
                $error_code = $parts[0];
                $error_type = $parts[1];
            }
        }
        if (!isset($error_type)) {
            $error_type = $code[0] == '4' ? 'client' : 'server';
        }

        return [
            'request_id'  => (string) $response->getHeaderLine('x-amzn-requestid'),
            'code'        => isset($error_code) ? $error_code : null,
            'message'     => null,
            'type'        => $error_type,
            'parsed'      => $this->parseJson($response->getBody(), $response)
        ];
    }

    protected function payload(
        ResponseInterface $response,
        StructureShape $member
    ) {
        $jsonBody = $this->parseJson($response->getBody(), $response);

        if ($jsonBody) {
            return $this->parser->parse($member, $jsonBody);
        }
    }
}


<?php
namespace Aws\Api\ErrorParser;

use Aws\Api\Parser\JsonParser;
use Aws\Api\Service;
use Aws\CommandInterface;
use Psr\Http\Message\ResponseInterface;

/**
 * Parsers JSON-RPC errors.
 */
class JsonRpcErrorParser extends AbstractErrorParser
{
    use JsonParserTrait;

    private $parser;

    public function __construct(Service $api = null, JsonParser $parser = null)
    {
        parent::__construct($api);
        $this->parser = $parser ?: new JsonParser();
    }

    public function __invoke(
        ResponseInterface $response,
        CommandInterface $command = null
    ) {
        $data = $this->genericHandler($response);

        // Make the casing consistent across services.
        if ($data['parsed']) {
            $data['parsed'] = array_change_key_case($data['parsed']);
        }

        if (isset($data['parsed']['__type'])) {
            if (!isset($data['code'])) {
                $parts = explode('#', $data['parsed']['__type']);
                $data['code'] = isset($parts[1]) ? $parts[1] : $parts[0];
            }
            $data['message'] = isset($data['parsed']['message'])
                ? $data['parsed']['message']
                : null;
        }

        $this->populateShape($data, $response, $command);

        return $data;
    }
}


<?php
namespace Aws\Api\ErrorParser;

use Aws\Api\Parser\JsonParser;
use Aws\Api\Service;
use Aws\Api\StructureShape;
use Aws\CommandInterface;
use Psr\Http\Message\ResponseInterface;

/**
 * Parses JSON-REST errors.
 */
class RestJsonErrorParser extends AbstractErrorParser
{
    use JsonParserTrait;

    private $parser;

    public function __construct(Service $api = null, JsonParser $parser = null)
    {
        parent::__construct($api);
        $this->parser = $parser ?: new JsonParser();
    }

    public function __invoke(
        ResponseInterface $response,
        CommandInterface $command = null
    ) {
        $data = $this->genericHandler($response);

        // Merge in error data from the JSON body
        if ($json = $data['parsed']) {
            $data = array_replace($data, $json);
        }

        // Correct error type from services like Amazon Glacier
        if (!empty($data['type'])) {
            $data['type'] = strtolower($data['type']);
        }

        // Retrieve the error code from services like Amazon Elastic Transcoder
        if ($code = $response->getHeaderLine('x-amzn-errortype')) {
            $colon = strpos($code, ':');
            $data['code'] = $colon ? substr($code, 0, $colon) : $code;
        }

        // Retrieve error message directly
        $data['message'] = isset($data['parsed']['message'])
            ? $data['parsed']['message']
            : (isset($data['parsed']['Message'])
                ? $data['parsed']['Message']
                : null);

        $this->populateShape($data, $response, $command);

        return $data;
    }
}


<?php
namespace Aws\Api\ErrorParser;

use Aws\Api\Parser\PayloadParserTrait;
use Aws\Api\Parser\XmlParser;
use Aws\Api\Service;
use Aws\Api\StructureShape;
use Aws\CommandInterface;
use Psr\Http\Message\ResponseInterface;

/**
 * Parses XML errors.
 */
class XmlErrorParser extends AbstractErrorParser
{
    use PayloadParserTrait;

    protected $parser;

    public function __construct(Service $api = null, XmlParser $parser = null)
    {
        parent::__construct($api);
        $this->parser = $parser ?: new XmlParser();
    }

    public function __invoke(
        ResponseInterface $response,
        CommandInterface $command = null
    ) {
        $code = (string) $response->getStatusCode();

        $data = [
            'type' => $code[0] == '4' ? 'client' : 'server',
            'request_id' => null,
            'code' => null,
            'message' => null,
            'parsed' => null
        ];

        $body = $response->getBody();
        if ($body->getSize() > 0) {
            $this->parseBody($this->parseXml($body, $response), $data);
        } else {
            $this->parseHeaders($response, $data);
        }

        $this->populateShape($data, $response, $command);

        return $data;
    }

    private function parseHeaders(ResponseInterface $response, array &$data)
    {
        if ($response->getStatusCode() == '404') {
            $data['code'] = 'NotFound';
        }

        $data['message'] = $response->getStatusCode() . ' '
            . $response->getReasonPhrase();

        if ($requestId = $response->getHeaderLine('x-amz-request-id')) {
            $data['request_id'] = $requestId;
            $data['message'] .= " (Request-ID: $requestId)";
        }
    }

    private function parseBody(\SimpleXMLElement $body, array &$data)
    {
        $data['parsed'] = $body;
        $prefix = $this->registerNamespacePrefix($body);

        if ($tempXml = $body->xpath("//{$prefix}Code[1]")) {
            $data['code'] = (string) $tempXml[0];
        }

        if ($tempXml = $body->xpath("//{$prefix}Message[1]")) {
            $data['message'] = (string) $tempXml[0];
        }

        $tempXml = $body->xpath("//{$prefix}RequestId[1]");
        if (isset($tempXml[0])) {
            $data['request_id'] = (string)$tempXml[0];
        }
    }

    protected function registerNamespacePrefix(\SimpleXMLElement $element)
    {
        $namespaces = $element->getDocNamespaces();
        if (!isset($namespaces[''])) {
            return '';
        }

        // Account for the default namespace being defined and PHP not
        // being able to handle it :(.
        $element->registerXPathNamespace('ns', $namespaces['']);
        return 'ns:';
    }

    protected function payload(
        ResponseInterface $response,
        StructureShape $member
    ) {
        $xmlBody = $this->parseXml($response->getBody(), $response);
        $prefix = $this->registerNamespacePrefix($xmlBody);
        $errorBody = $xmlBody->xpath("//{$prefix}Error");

        if (is_array($errorBody) && !empty($errorBody[0])) {
            return $this->parser->parse($member, $errorBody[0]);
        }
    }
}


<?php
namespace Aws\Api\Parser;

use Aws\Api\Service;
use Aws\Api\StructureShape;
use Aws\CommandInterface;
use Aws\ResultInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;

/**
 * @internal
 */
abstract class AbstractParser
{
    /** @var \Aws\Api\Service Representation of the service API*/
    protected $api;

    /** @var callable */
    protected $parser;

    /**
     * @param Service $api Service description.
     */
    public function __construct(Service $api)
    {
        $this->api = $api;
    }

    /**
     * @param CommandInterface  $command  Command that was executed.
     * @param ResponseInterface $response Response that was received.
     *
     * @return ResultInterface
     */
    abstract public function __invoke(
        CommandInterface $command,
        ResponseInterface $response
    );

    abstract public function parseMemberFromStream(
        StreamInterface $stream,
        StructureShape $member,
        $response
    );
}


<?php
namespace Aws\Api\Parser;

use Aws\Api\DateTimeResult;
use Aws\Api\Shape;
use Aws\Api\StructureShape;
use Aws\Result;
use Aws\CommandInterface;
use Psr\Http\Message\ResponseInterface;

/**
 * @internal
 */
abstract class AbstractRestParser extends AbstractParser
{
    use PayloadParserTrait;

    /**
     * Parses a payload from a response.
     *
     * @param ResponseInterface $response Response to parse.
     * @param StructureShape    $member   Member to parse
     * @param array             $result   Result value
     *
     * @return mixed
     */
    abstract protected function payload(
        ResponseInterface $response,
        StructureShape $member,
        array &$result
    );

    public function __invoke(
        CommandInterface $command,
        ResponseInterface $response
    ) {
        $output = $this->api->getOperation($command->getName())->getOutput();
        $result = [];

        if ($payload = $output['payload']) {
            $this->extractPayload($payload, $output, $response, $result);
        }

        foreach ($output->getMembers() as $name => $member) {
            switch ($member['location']) {
                case 'header':
                    $this->extractHeader($name, $member, $response, $result);
                    break;
                case 'headers':
                    $this->extractHeaders($name, $member, $response, $result);
                    break;
                case 'statusCode':
                    $this->extractStatus($name, $response, $result);
                    break;
            }
        }

        if (!$payload
            && $response->getBody()->getSize() > 0
            && count($output->getMembers()) > 0
        ) {
            // if no payload was found, then parse the contents of the body
            $this->payload($response, $output, $result);
        }

        return new Result($result);
    }

    private function extractPayload(
        $payload,
        StructureShape $output,
        ResponseInterface $response,
        array &$result
    ) {
        $member = $output->getMember($payload);

        if (!empty($member['eventstream'])) {
            $result[$payload] = new EventParsingIterator(
                $response->getBody(),
                $member,
                $this
            );
        } else if ($member instanceof StructureShape) {
            // Structure members parse top-level data into a specific key.
            $result[$payload] = [];
            $this->payload($response, $member, $result[$payload]);
        } else {
            // Streaming data is just the stream from the response body.
            $result[$payload] = $response->getBody();
        }
    }

    /**
     * Extract a single header from the response into the result.
     */
    private function extractHeader(
        $name,
        Shape $shape,
        ResponseInterface $response,
        &$result
    ) {
        $value = $response->getHeaderLine($shape['locationName'] ?: $name);

        switch ($shape->getType()) {
            case 'float':
            case 'double':
                $value = (float) $value;
                break;
            case 'long':
                $value = (int) $value;
                break;
            case 'boolean':
                $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
                break;
            case 'blob':
                $value = base64_decode($value);
                break;
            case 'timestamp':
                try {
                    $value = DateTimeResult::fromTimestamp(
                        $value,
                        !empty($shape['timestampFormat']) ? $shape['timestampFormat'] : null
                    );
                    break;
                } catch (\Exception $e) {
                    // If the value cannot be parsed, then do not add it to the
                    // output structure.
                    return;
                }
            case 'string':
                try {
                    if ($shape['jsonvalue']) {
                        $value = $this->parseJson(base64_decode($value), $response);
                    }

                    // If value is not set, do not add to output structure.
                    if (!isset($value)) {
                        return;
                    }
                    break;
                } catch (\Exception $e) {
                    //If the value cannot be parsed, then do not add it to the
                    //output structure.
                    return;
                }
        }

        $result[$name] = $value;
    }

    /**
     * Extract a map of headers with an optional prefix from the response.
     */
    private function extractHeaders(
        $name,
        Shape $shape,
        ResponseInterface $response,
        &$result
    ) {
        // Check if the headers are prefixed by a location name
        $result[$name] = [];
        $prefix = $shape['locationName'];
        $prefixLen = $prefix !== null ? strlen($prefix) : 0;

        foreach ($response->getHeaders() as $k => $values) {
            if (!$prefixLen) {
                $result[$name][$k] = implode(', ', $values);
            } elseif (stripos($k, $prefix) === 0) {
                $result[$name][substr($k, $prefixLen)] = implode(', ', $values);
            }
        }
    }

    /**
     * Places the status code of the response into the result array.
     */
    private function extractStatus(
        $name,
        ResponseInterface $response,
        array &$result
    ) {
        $result[$name] = (int) $response->getStatusCode();
    }
}


<?php
namespace Aws\Api\Parser;

use Aws\Api\StructureShape;
use Aws\CommandInterface;
use Aws\Exception\AwsException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use GuzzleHttp\Psr7;

/**
 * @internal Decorates a parser and validates the x-amz-crc32 header.
 */
class Crc32ValidatingParser extends AbstractParser
{
    /**
     * @param callable $parser Parser to wrap.
     */
    public function __construct(callable $parser)
    {
        $this->parser = $parser;
    }

    public function __invoke(
        CommandInterface $command,
        ResponseInterface $response
    ) {
        if ($expected = $response->getHeaderLine('x-amz-crc32')) {
            $hash = hexdec(Psr7\Utils::hash($response->getBody(), 'crc32b'));
            if ($expected != $hash) {
                throw new AwsException(
                    "crc32 mismatch. Expected {$expected}, found {$hash}.",
                    $command,
                    [
                        'code'             => 'ClientChecksumMismatch',
                        'connection_error' => true,
                        'response'         => $response
                    ]
                );
            }
        }

        $fn = $this->parser;
        return $fn($command, $response);
    }

    public function parseMemberFromStream(
        StreamInterface $stream,
        StructureShape $member,
        $response
    ) {
        return $this->parser->parseMemberFromStream($stream, $member, $response);
    }
}


<?php

namespace Aws\Api\Parser;

use \Iterator;
use Aws\Api\DateTimeResult;
use GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
use Aws\Api\Parser\Exception\ParserException;

/**
 * @internal Implements a decoder for a binary encoded event stream that will
 * decode, validate, and provide individual events from the stream.
 */
class DecodingEventStreamIterator implements Iterator
{
    const HEADERS = 'headers';
    const PAYLOAD = 'payload';

    const LENGTH_TOTAL = 'total_length';
    const LENGTH_HEADERS = 'headers_length';

    const CRC_PRELUDE = 'prelude_crc';

    const BYTES_PRELUDE = 12;
    const BYTES_TRAILING = 4;

    private static $preludeFormat = [
        self::LENGTH_TOTAL => 'decodeUint32',
        self::LENGTH_HEADERS => 'decodeUint32',
        self::CRC_PRELUDE => 'decodeUint32',
    ];

    private static $lengthFormatMap = [
        1 => 'decodeUint8',
        2 => 'decodeUint16',
        4 => 'decodeUint32',
        8 => 'decodeUint64',
    ];

    private static $headerTypeMap = [
        0 => 'decodeBooleanTrue',
        1 => 'decodeBooleanFalse',
        2 => 'decodeInt8',
        3 => 'decodeInt16',
        4 => 'decodeInt32',
        5 => 'decodeInt64',
        6 => 'decodeBytes',
        7 => 'decodeString',
        8 => 'decodeTimestamp',
        9 => 'decodeUuid',
    ];

    /** @var StreamInterface Stream of eventstream shape to parse. */
    private $stream;

    /** @var array Currently parsed event. */
    private $currentEvent;

    /** @var int Current in-order event key. */
    private $key;

    /** @var resource|\HashContext CRC32 hash context for event validation */
    private $hashContext;

    /** @var int $currentPosition */
    private $currentPosition;

    /**
     * DecodingEventStreamIterator constructor.
     *
     * @param StreamInterface $stream
     */
    public function __construct(StreamInterface $stream)
    {
        $this->stream = $stream;
        $this->rewind();
    }

    private function parseHeaders($headerBytes)
    {
        $headers = [];
        $bytesRead = 0;

        while ($bytesRead < $headerBytes) {
            list($key, $numBytes) = $this->decodeString(1);
            $bytesRead += $numBytes;

            list($type, $numBytes) = $this->decodeUint8();
            $bytesRead += $numBytes;

            $f = self::$headerTypeMap[$type];
            list($value, $numBytes) = $this->{$f}();
            $bytesRead += $numBytes;

            if (isset($headers[$key])) {
                throw new ParserException('Duplicate key in event headers.');
            }
            $headers[$key] = $value;
        }

        return [$headers, $bytesRead];
    }

    private function parsePrelude()
    {
        $prelude = [];
        $bytesRead = 0;

        $calculatedCrc = null;
        foreach (self::$preludeFormat as $key => $decodeFunction) {
            if ($key === self::CRC_PRELUDE) {
                $hashCopy = hash_copy($this->hashContext);
                $calculatedCrc = hash_final($this->hashContext, true);
                $this->hashContext = $hashCopy;
            }
            list($value, $numBytes) = $this->{$decodeFunction}();
            $bytesRead += $numBytes;

            $prelude[$key] = $value;
        }

        if (unpack('N', $calculatedCrc)[1] !== $prelude[self::CRC_PRELUDE]) {
            throw new ParserException('Prelude checksum mismatch.');
        }

        return [$prelude, $bytesRead];
    }

    private function parseEvent()
    {
        $event = [];

        if ($this->stream->tell() < $this->stream->getSize()) {
            $this->hashContext = hash_init('crc32b');

            $bytesLeft = $this->stream->getSize() - $this->stream->tell();
            list($prelude, $numBytes) = $this->parsePrelude();
            if ($prelude[self::LENGTH_TOTAL] > $bytesLeft) {
                throw new ParserException('Message length too long.');
            }
            $bytesLeft -= $numBytes;

            if ($prelude[self::LENGTH_HEADERS] > $bytesLeft) {
                throw new ParserException('Headers length too long.');
            }

            list(
                $event[self::HEADERS],
                $numBytes
            ) = $this->parseHeaders($prelude[self::LENGTH_HEADERS]);

            $event[self::PAYLOAD] = Psr7\Utils::streamFor(
                $this->readAndHashBytes(
                    $prelude[self::LENGTH_TOTAL] - self::BYTES_PRELUDE
                    - $numBytes - self::BYTES_TRAILING
                )
            );

            $calculatedCrc = hash_final($this->hashContext, true);
            $messageCrc = $this->stream->read(4);
            if ($calculatedCrc !== $messageCrc) {
                throw new ParserException('Message checksum mismatch.');
            }
        }

        return $event;
    }

    // Iterator Functionality

    /**
     * @return array
     */
    #[\ReturnTypeWillChange]
    public function current()
    {
        return $this->currentEvent;
    }

    /**
     * @return int
     */
    #[\ReturnTypeWillChange]
    public function key()
    {
        return $this->key;
    }

    #[\ReturnTypeWillChange]
    public function next()
    {
        $this->currentPosition = $this->stream->tell();
        if ($this->valid()) {
            $this->key++;
            $this->currentEvent = $this->parseEvent();
        }
    }

    #[\ReturnTypeWillChange]
    public function rewind()
    {
        $this->stream->rewind();
        $this->key = 0;
        $this->currentPosition = 0;
        $this->currentEvent = $this->parseEvent();
    }

    /**
     * @return bool
     */
    #[\ReturnTypeWillChange]
    public function valid()
    {
        return $this->currentPosition < $this->stream->getSize();
    }

    // Decoding Utilities

    private function readAndHashBytes($num)
    {
        $bytes = $this->stream->read($num);
        hash_update($this->hashContext, $bytes);
        return $bytes;
    }

    private function decodeBooleanTrue()
    {
        return [true, 0];
    }

    private function decodeBooleanFalse()
    {
        return [false, 0];
    }

    private function uintToInt($val, $size)
    {
        $signedCap = pow(2, $size - 1);
        if ($val > $signedCap) {
            $val -= (2 * $signedCap);
        }
        return $val;
    }

    private function decodeInt8()
    {
        $val = (int)unpack('C', $this->readAndHashBytes(1))[1];
        return [$this->uintToInt($val, 8), 1];
    }

    private function decodeUint8()
    {
        return [unpack('C', $this->readAndHashBytes(1))[1], 1];
    }

    private function decodeInt16()
    {
        $val = (int)unpack('n', $this->readAndHashBytes(2))[1];
        return [$this->uintToInt($val, 16), 2];
    }

    private function decodeUint16()
    {
        return [unpack('n', $this->readAndHashBytes(2))[1], 2];
    }

    private function decodeInt32()
    {
        $val = (int)unpack('N', $this->readAndHashBytes(4))[1];
        return [$this->uintToInt($val, 32), 4];
    }

    private function decodeUint32()
    {
        return [unpack('N', $this->readAndHashBytes(4))[1], 4];
    }

    private function decodeInt64()
    {
        $val = $this->unpackInt64($this->readAndHashBytes(8))[1];
        return [$this->uintToInt($val, 64), 8];
    }

    private function decodeUint64()
    {
        return [$this->unpackInt64($this->readAndHashBytes(8))[1], 8];
    }

    private function unpackInt64($bytes)
    {
        if (version_compare(PHP_VERSION, '5.6.3', '<')) {
            $d = unpack('N2', $bytes);
            return [1 => $d[1] << 32 | $d[2]];
        }
        return unpack('J', $bytes);
    }

    private function decodeBytes($lengthBytes=2)
    {
        if (!isset(self::$lengthFormatMap[$lengthBytes])) {
            throw new ParserException('Undefined variable length format.');
        }
        $f = self::$lengthFormatMap[$lengthBytes];
        list($len, $bytes) = $this->{$f}();
        return [$this->readAndHashBytes($len), $len + $bytes];
    }

    private function decodeString($lengthBytes=2)
    {
        if (!isset(self::$lengthFormatMap[$lengthBytes])) {
            throw new ParserException('Undefined variable length format.');
        }
        $f = self::$lengthFormatMap[$lengthBytes];
        list($len, $bytes) = $this->{$f}();
        return [$this->readAndHashBytes($len), $len + $bytes];
    }

    private function decodeTimestamp()
    {
        list($val, $bytes) = $this->decodeInt64();
        return [
            DateTimeResult::createFromFormat('U.u', $val / 1000),
            $bytes
        ];
    }

    private function decodeUuid()
    {
        $val = unpack('H32', $this->readAndHashBytes(16))[1];
        return [
            substr($val, 0, 8) . '-'
            . substr($val, 8, 4) . '-'
            . substr($val, 12, 4) . '-'
            . substr($val, 16, 4) . '-'
            . substr($val, 20, 12),
            16
        ];
    }
}


<?php

namespace Aws\Api\Parser;

use \Iterator;
use Aws\Exception\EventStreamDataException;
use Aws\Api\Parser\Exception\ParserException;
use Aws\Api\StructureShape;
use Psr\Http\Message\StreamInterface;

/**
 * @internal Implements a decoder for a binary encoded event stream that will
 * decode, validate, and provide individual events from the stream.
 */
class EventParsingIterator implements Iterator
{
    /** @var StreamInterface */
    private $decodingIterator;

    /** @var StructureShape */
    private $shape;

    /** @var AbstractParser */
    private $parser;

    public function __construct(
        StreamInterface $stream,
        StructureShape $shape,
        AbstractParser $parser
    ) {
        $this->decodingIterator = new DecodingEventStreamIterator($stream);
        $this->shape = $shape;
        $this->parser = $parser;
    }

    #[\ReturnTypeWillChange]
    public function current()
    {
        return $this->parseEvent($this->decodingIterator->current());
    }

    #[\ReturnTypeWillChange]
    public function key()
    {
        return $this->decodingIterator->key();
    }

    #[\ReturnTypeWillChange]
    public function next()
    {
        $this->decodingIterator->next();
    }

    #[\ReturnTypeWillChange]
    public function rewind()
    {
        $this->decodingIterator->rewind();
    }

    #[\ReturnTypeWillChange]
    public function valid()
    {
        return $this->decodingIterator->valid();
    }

    private function parseEvent(array $event)
    {
        if (!empty($event['headers'][':message-type'])) {
            if ($event['headers'][':message-type'] === 'error') {
                return $this->parseError($event);
            }
            if ($event['headers'][':message-type'] !== 'event') {
                throw new ParserException('Failed to parse unknown message type.');
            }
        }

        if (empty($event['headers'][':event-type'])) {
            throw new ParserException('Failed to parse without event type.');
        }
        $eventShape = $this->shape->getMember($event['headers'][':event-type']);

        $parsedEvent = [];
        foreach ($eventShape['members'] as $shape => $details) {
            if (!empty($details['eventpayload'])) {
                $payloadShape = $eventShape->getMember($shape);
                if ($payloadShape['type'] === 'blob') {
                    $parsedEvent[$shape] = $event['payload'];
                } else {
                    $parsedEvent[$shape] = $this->parser->parseMemberFromStream(
                        $event['payload'],
                        $payloadShape,
                        null
                    );
                }
            } else {
                $parsedEvent[$shape] = $event['headers'][$shape];
            }
        }

        return [
            $event['headers'][':event-type'] => $parsedEvent
        ];
    }

    private function parseError(array $event)
    {
        throw new EventStreamDataException(
            $event['headers'][':error-code'],
            $event['headers'][':error-message']
        );
    }
}


<?php
namespace Aws\Api\Parser;

use Aws\Api\DateTimeResult;
use Aws\Api\Shape;

/**
 * @internal Implements standard JSON parsing.
 */
class JsonParser
{
    public function parse(Shape $shape, $value)
    {
        if ($value === null) {
            return $value;
        }

        switch ($shape['type']) {
            case 'structure':
                if (isset($shape['document']) && $shape['document']) {
                    return $value;
                }
                $target = [];
                foreach ($shape->getMembers() as $name => $member) {
                    $locationName = $member['locationName'] ?: $name;
                    if (isset($value[$locationName])) {
                        $target[$name] = $this->parse($member, $value[$locationName]);
                    }
                }
                if (isset($shape['union'])
                    && $shape['union']
                    && is_array($value)
                    && empty($target)
                ) {
                    foreach ($value as $key => $val) {
                        $target['Unknown'][$key] = $val;
                    }
                }
                return $target;

            case 'list':
                $member = $shape->getMember();
                $target = [];
                foreach ($value as $v) {
                    $target[] = $this->parse($member, $v);
                }
                return $target;

            case 'map':
                $values = $shape->getValue();
                $target = [];
                foreach ($value as $k => $v) {
                    $target[$k] = $this->parse($values, $v);
                }
                return $target;

            case 'timestamp':
                return DateTimeResult::fromTimestamp(
                    $value,
                    !empty($shape['timestampFormat']) ? $shape['timestampFormat'] : null
                );

            case 'blob':
                return base64_decode($value);

            default:
                return $value;
        }
    }
}


<?php
namespace Aws\Api\Parser;

use Aws\Api\StructureShape;
use Aws\Api\Service;
use Aws\Result;
use Aws\CommandInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;

/**
 * @internal Implements JSON-RPC parsing (e.g., DynamoDB)
 */
class JsonRpcParser extends AbstractParser
{
    use PayloadParserTrait;

    /**
     * @param Service    $api    Service description
     * @param JsonParser $parser JSON body builder
     */
    public function __construct(Service $api, JsonParser $parser = null)
    {
        parent::__construct($api);
        $this->parser = $parser ?: new JsonParser();
    }

    public function __invoke(
        CommandInterface $command,
        ResponseInterface $response
    ) {
        $operation = $this->api->getOperation($command->getName());
        $result = null === $operation['output']
            ? null
            : $this->parseMemberFromStream(
                $response->getBody(),
                $operation->getOutput(),
                $response
            );

        return new Result($result ?: []);
    }

    public function parseMemberFromStream(
        StreamInterface $stream,
        StructureShape $member,
        $response
    ) {
        return $this->parser->parse($member, $this->parseJson($stream, $response));
    }
}


<?php
namespace Aws\Api\Parser;

use Aws\Api\DateTimeResult;
use Aws\Api\Shape;
use Psr\Http\Message\ResponseInterface;

trait MetadataParserTrait
{
    /**
     * Extract a single header from the response into the result.
     */
    protected function extractHeader(
        $name,
        Shape $shape,
        ResponseInterface $response,
        &$result
    ) {
        $value = $response->getHeaderLine($shape['locationName'] ?: $name);

        switch ($shape->getType()) {
            case 'float':
            case 'double':
                $value = (float) $value;
                break;
            case 'long':
                $value = (int) $value;
                break;
            case 'boolean':
                $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
                break;
            case 'blob':
                $value = base64_decode($value);
                break;
            case 'timestamp':
                try {
                    $value = DateTimeResult::fromTimestamp(
                        $value,
                        !empty($shape['timestampFormat']) ? $shape['timestampFormat'] : null
                    );
                    break;
                } catch (\Exception $e) {
                    // If the value cannot be parsed, then do not add it to the
                    // output structure.
                    return;
                }
            case 'string':
                if ($shape['jsonvalue']) {
                    $value = $this->parseJson(base64_decode($value), $response);
                }
                break;
        }

        $result[$name] = $value;
    }

    /**
     * Extract a map of headers with an optional prefix from the response.
     */
    protected function extractHeaders(
        $name,
        Shape $shape,
        ResponseInterface $response,
        &$result
    ) {
        // Check if the headers are prefixed by a location name
        $result[$name] = [];
        $prefix = $shape['locationName'];
        $prefixLen = strlen($prefix);

        foreach ($response->getHeaders() as $k => $values) {
            if (!$prefixLen) {
                $result[$name][$k] = implode(', ', $values);
            } elseif (stripos($k, $prefix) === 0) {
                $result[$name][substr($k, $prefixLen)] = implode(', ', $values);
            }
        }
    }

    /**
     * Places the status code of the response into the result array.
     */
    protected function extractStatus(
        $name,
        ResponseInterface $response,
        array &$result
    ) {
        $result[$name] = (int) $response->getStatusCode();
    }
}


<?php
namespace Aws\Api\Parser;

use Aws\Api\Parser\Exception\ParserException;
use Psr\Http\Message\ResponseInterface;

trait PayloadParserTrait
{
    /**
     * @param string $json
     *
     * @throws ParserException
     *
     * @return array
     */
    private function parseJson($json, $response)
    {
        $jsonPayload = json_decode($json, true);

        if (JSON_ERROR_NONE !== json_last_error()) {
            throw new ParserException(
                'Error parsing JSON: ' . json_last_error_msg(),
                0,
                null,
                ['response' => $response]
            );
        }

        return $jsonPayload;
    }

    /**
     * @param string $xml
     *
     * @throws ParserException
     *
     * @return \SimpleXMLElement
     */
    protected function parseXml($xml, $response)
    {
        $priorSetting = libxml_use_internal_errors(true);
        try {
            libxml_clear_errors();
            $xmlPayload = new \SimpleXMLElement($xml);
            if ($error = libxml_get_last_error()) {
                throw new \RuntimeException($error->message);
            }
        } catch (\Exception $e) {
            throw new ParserException(
                "Error parsing XML: {$e->getMessage()}",
                0,
                $e,
                ['response' => $response]
            );
        } finally {
            libxml_use_internal_errors($priorSetting);
        }

        return $xmlPayload;
    }
}


<?php
namespace Aws\Api\Parser;

use Aws\Api\Service;
use Aws\Api\StructureShape;
use Aws\Result;
use Aws\CommandInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;

/**
 * @internal Parses query (XML) responses (e.g., EC2, SQS, and many others)
 */
class QueryParser extends AbstractParser
{
    use PayloadParserTrait;

    /** @var bool */
    private $honorResultWrapper;

    /**
     * @param Service   $api                Service description
     * @param XmlParser $xmlParser          Optional XML parser
     * @param bool      $honorResultWrapper Set to false to disable the peeling
     *                                      back of result wrappers from the
     *                                      output structure.
     */
    public function __construct(
        Service $api,
        XmlParser $xmlParser = null,
        $honorResultWrapper = true
    ) {
        parent::__construct($api);
        $this->parser = $xmlParser ?: new XmlParser();
        $this->honorResultWrapper = $honorResultWrapper;
    }

    public function __invoke(
        CommandInterface $command,
        ResponseInterface $response
    ) {
        $output = $this->api->getOperation($command->getName())->getOutput();
        $xml = $this->parseXml($response->getBody(), $response);

        if ($this->honorResultWrapper && $output['resultWrapper']) {
            $xml = $xml->{$output['resultWrapper']};
        }

        return new Result($this->parser->parse($output, $xml));
    }

    public function parseMemberFromStream(
        StreamInterface $stream,
        StructureShape $member,
        $response
    ) {
        $xml = $this->parseXml($stream, $response);
        return $this->parser->parse($member, $xml);
    }
}


<?php
namespace Aws\Api\Parser;

use Aws\Api\Service;
use Aws\Api\StructureShape;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;

/**
 * @internal Implements REST-JSON parsing (e.g., Glacier, Elastic Transcoder)
 */
class RestJsonParser extends AbstractRestParser
{
    use PayloadParserTrait;

    /**
     * @param Service    $api    Service description
     * @param JsonParser $parser JSON body builder
     */
    public function __construct(Service $api, JsonParser $parser = null)
    {
        parent::__construct($api);
        $this->parser = $parser ?: new JsonParser();
    }

    protected function payload(
        ResponseInterface $response,
        StructureShape $member,
        array &$result
    ) {
        $jsonBody = $this->parseJson($response->getBody(), $response);

        if ($jsonBody) {
            $result += $this->parser->parse($member, $jsonBody);
        }
    }

    public function parseMemberFromStream(
        StreamInterface $stream,
        StructureShape $member,
        $response
    ) {
        $jsonBody = $this->parseJson($stream, $response);
        if ($jsonBody) {
            return $this->parser->parse($member, $jsonBody);
        }
        return [];
    }
}


<?php
namespace Aws\Api\Parser;

use Aws\Api\StructureShape;
use Aws\Api\Service;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;

/**
 * @internal Implements REST-XML parsing (e.g., S3, CloudFront, etc...)
 */
class RestXmlParser extends AbstractRestParser
{
    use PayloadParserTrait;

    /**
     * @param Service   $api    Service description
     * @param XmlParser $parser XML body parser
     */
    public function __construct(Service $api, XmlParser $parser = null)
    {
        parent::__construct($api);
        $this->parser = $parser ?: new XmlParser();
    }

    protected function payload(
        ResponseInterface $response,
        StructureShape $member,
        array &$result
    ) {
        $result += $this->parseMemberFromStream($response->getBody(), $member, $response);
    }

    public function parseMemberFromStream(
        StreamInterface $stream,
        StructureShape $member,
        $response
    ) {
        $xml = $this->parseXml($stream, $response);
        return $this->parser->parse($member, $xml);
    }
}


<?php
namespace Aws\Api\Parser;

use Aws\Api\DateTimeResult;
use Aws\Api\ListShape;
use Aws\Api\MapShape;
use Aws\Api\Parser\Exception\ParserException;
use Aws\Api\Shape;
use Aws\Api\StructureShape;

/**
 * @internal Implements standard XML parsing for REST-XML and Query protocols.
 */
class XmlParser
{
    public function parse(StructureShape $shape, \SimpleXMLElement $value)
    {
        return $this->dispatch($shape, $value);
    }

    private function dispatch($shape, \SimpleXMLElement $value)
    {
        static $methods = [
            'structure' => 'parse_structure',
            'list'      => 'parse_list',
            'map'       => 'parse_map',
            'blob'      => 'parse_blob',
            'boolean'   => 'parse_boolean',
            'integer'   => 'parse_integer',
            'float'     => 'parse_float',
            'double'    => 'parse_float',
            'timestamp' => 'parse_timestamp',
        ];

        $type = $shape['type'];
        if (isset($methods[$type])) {
            return $this->{$methods[$type]}($shape, $value);
        }

        return (string) $value;
    }

    private function parse_structure(
        StructureShape $shape,
        \SimpleXMLElement $value
    ) {
        $target = [];

        foreach ($shape->getMembers() as $name => $member) {
            // Extract the name of the XML node
            $node = $this->memberKey($member, $name);
            if (isset($value->{$node})) {
                $target[$name] = $this->dispatch($member, $value->{$node});
            } else {
                $memberShape = $shape->getMember($name);
                if (!empty($memberShape['xmlAttribute'])) {
                    $target[$name] = $this->parse_xml_attribute(
                        $shape,
                        $memberShape,
                        $value
                    );
                }
            }
        }
        if (isset($shape['union'])
            && $shape['union']
            && empty($target)
        ) {
            foreach ($value as $key => $val) {
                $name = $val->children()->getName();
                $target['Unknown'][$name] = $val->$name;
            }
        }
        return $target;
    }

    private function memberKey(Shape $shape, $name)
    {
        if (null !== $shape['locationName']) {
            return $shape['locationName'];
        }

        if ($shape instanceof ListShape && $shape['flattened']) {
            return $shape->getMember()['locationName'] ?: $name;
        }

        return $name;
    }

    private function parse_list(ListShape $shape, \SimpleXMLElement  $value)
    {
        $target = [];
        $member = $shape->getMember();

        if (!$shape['flattened']) {
            $value = $value->{$member['locationName'] ?: 'member'};
        }

        foreach ($value as $v) {
            $target[] = $this->dispatch($member, $v);
        }

        return $target;
    }

    private function parse_map(MapShape $shape, \SimpleXMLElement $value)
    {
        $target = [];

        if (!$shape['flattened']) {
            $value = $value->entry;
        }

        $mapKey = $shape->getKey();
        $mapValue = $shape->getValue();
        $keyName = $shape->getKey()['locationName'] ?: 'key';
        $valueName = $shape->getValue()['locationName'] ?: 'value';

        foreach ($value as $node) {
            $key = $this->dispatch($mapKey, $node->{$keyName});
            $value = $this->dispatch($mapValue, $node->{$valueName});
            $target[$key] = $value;
        }

        return $target;
    }

    private function parse_blob(Shape $shape, $value)
    {
        return base64_decode((string) $value);
    }

    private function parse_float(Shape $shape, $value)
    {
        return (float) (string) $value;
    }

    private function parse_integer(Shape $shape, $value)
    {
        return (int) (string) $value;
    }

    private function parse_boolean(Shape $shape, $value)
    {
        return $value == 'true';
    }

    private function parse_timestamp(Shape $shape, $value)
    {
        if (is_string($value)
            || is_int($value)
            || (is_object($value)
                && method_exists($value, '__toString'))
        ) {
            return DateTimeResult::fromTimestamp(
                (string) $value,
                !empty($shape['timestampFormat']) ? $shape['timestampFormat'] : null
            );
        }
        throw new ParserException('Invalid timestamp value passed to XmlParser::parse_timestamp');
    }

    private function parse_xml_attribute(Shape $shape, Shape $memberShape, $value)
    {
        $namespace = $shape['xmlNamespace']['uri']
            ? $shape['xmlNamespace']['uri']
            : '';
        $prefix = $shape['xmlNamespace']['prefix']
            ? $shape['xmlNamespace']['prefix']
            : '';
        if (!empty($prefix)) {
            $prefix .= ':';
        }
        $key = str_replace($prefix, '', $memberShape['locationName']);

        $attributes = $value->attributes($namespace);
        return isset($attributes[$key]) ? (string) $attributes[$key] : null;
    }
}


<?php
namespace Aws\Api\Parser\Exception;

use Aws\HasMonitoringEventsTrait;
use Aws\MonitoringEventsInterface;
use Aws\ResponseContainerInterface;
use Psr\Http\Message\ResponseInterface;

class ParserException extends \RuntimeException implements
    MonitoringEventsInterface,
    ResponseContainerInterface
{
    use HasMonitoringEventsTrait;

    private $errorCode;
    private $requestId;
    private $response;

    public function __construct($message = '', $code = 0, $previous = null, array $context = [])
    {
        $this->errorCode = isset($context['error_code']) ? $context['error_code'] : null;
        $this->requestId = isset($context['request_id']) ? $context['request_id'] : null;
        $this->response = isset($context['response']) ? $context['response'] : null;
        parent::__construct($message, $code, $previous);
    }

    /**
     * Get the error code, if any.
     *
     * @return string|null
     */
    public function getErrorCode()
    {
        return $this->errorCode;
    }

    /**
     * Get the request ID, if any.
     *
     * @return string|null
     */
    public function getRequestId()
    {
        return $this->requestId;
    }

    /**
     * Get the received HTTP response if any.
     *
     * @return ResponseInterface|null
     */
    public function getResponse()
    {
        return $this->response;
    }
}


<?php
namespace Aws\Api\Serializer;

use Aws\Api\Shape;
use Aws\Api\ListShape;

/**
 * @internal
 */
class Ec2ParamBuilder extends QueryParamBuilder
{
    protected function queryName(Shape $shape, $default = null)
    {
        return ($shape['queryName']
            ?: ucfirst(@$shape['locationName'] ?: ""))
                ?: $default;
    }

    protected function isFlat(Shape $shape)
    {
        return false;
    }

    protected function format_list(
        ListShape $shape,
        array $value,
        $prefix,
        &$query
    ) {
        // Handle empty list serialization
        if (!$value) {
            $query[$prefix] = false;
        } else {
            $items = $shape->getMember();
            foreach ($value as $k => $v) {
                $this->format($items, $v, $prefix . '.' . ($k + 1), $query);
            }
        }
    }
}


<?php
namespace Aws\Api\Serializer;

use Aws\Api\Service;
use Aws\Api\Shape;
use Aws\Api\TimestampShape;
use Aws\Exception\InvalidJsonException;

/**
 * Formats the JSON body of a JSON-REST or JSON-RPC operation.
 * @internal
 */
class JsonBody
{
    private $api;

    public function __construct(Service $api)
    {
        $this->api = $api;
    }

    /**
     * Gets the JSON Content-Type header for a service API
     *
     * @param Service $service
     *
     * @return string
     */
    public static function getContentType(Service $service)
    {
        if ($service->getMetadata('protocol') === 'rest-json') {
            return 'application/json';
        }

        $jsonVersion = $service->getMetadata('jsonVersion');
        if (empty($jsonVersion)) {
            throw new \InvalidArgumentException('invalid json');
        } else {
            return 'application/x-amz-json-'
                . @number_format($service->getMetadata('jsonVersion'), 1);
        }
    }

    /**
     * Builds the JSON body based on an array of arguments.
     *
     * @param Shape $shape Operation being constructed
     * @param array $args  Associative array of arguments
     *
     * @return string
     */
    public function build(Shape $shape, array $args)
    {
        $result = json_encode($this->format($shape, $args));
        return $result == '[]' ? '{}' : $result;
    }

    private function format(Shape $shape, $value)
    {
        switch ($shape['type']) {
            case 'structure':
                $data = [];
                if (isset($shape['document']) && $shape['document']) {
                    return $value;
                }
                foreach ($value as $k => $v) {
                    if ($v !== null && $shape->hasMember($k)) {
                        $valueShape = $shape->getMember($k);
                        $data[$valueShape['locationName'] ?: $k]
                            = $this->format($valueShape, $v);
                    }
                }
                if (empty($data)) {
                    return new \stdClass;
                }
                return $data;

            case 'list':
                $items = $shape->getMember();
                foreach ($value as $k => $v) {
                    $value[$k] = $this->format($items, $v);
                }
                return $value;

            case 'map':
                if (empty($value)) {
                    return new \stdClass;
                }
                $values = $shape->getValue();
                foreach ($value as $k => $v) {
                    $value[$k] = $this->format($values, $v);
                }
                return $value;

            case 'blob':
                return base64_encode($value);

            case 'timestamp':
                $timestampFormat = !empty($shape['timestampFormat'])
                    ? $shape['timestampFormat']
                    : 'unixTimestamp';
                return TimestampShape::format($value, $timestampFormat);

            default:
                return $value;
        }
    }
}


<?php
namespace Aws\Api\Serializer;

use Aws\Api\Service;
use Aws\CommandInterface;
use Aws\EndpointV2\EndpointProviderV2;
use Aws\EndpointV2\EndpointV2SerializerTrait;
use GuzzleHttp\Psr7\Request;
use Psr\Http\Message\RequestInterface;

/**
 * Prepares a JSON-RPC request for transfer.
 * @internal
 */
class JsonRpcSerializer
{
    use EndpointV2SerializerTrait;

    /** @var JsonBody */
    private $jsonFormatter;

    /** @var string */
    private $endpoint;

    /** @var Service */
    private $api;

    /** @var string */
    private $contentType;

    /**
     * @param Service  $api           Service description
     * @param string   $endpoint      Endpoint to connect to
     * @param JsonBody $jsonFormatter Optional JSON formatter to use
     */
    public function __construct(
        Service $api,
        $endpoint,
        JsonBody $jsonFormatter = null
    ) {
        $this->endpoint = $endpoint;
        $this->api = $api;
        $this->jsonFormatter = $jsonFormatter ?: new JsonBody($this->api);
        $this->contentType = JsonBody::getContentType($api);
    }

    /**
     * When invoked with an AWS command, returns a serialization array
     * containing "method", "uri", "headers", and "body" key value pairs.
     *
     * @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
    )
    {
        $operationName = $command->getName();
        $operation = $this->api->getOperation($operationName);
        $commandArgs = $command->toArray();
        $headers = [
                'X-Amz-Target' => $this->api->getMetadata('targetPrefix') . '.' . $operationName,
                'Content-Type' => $this->contentType
            ];

        if ($endpointProvider instanceof EndpointProviderV2) {
            $this->setRequestOptions(
                $endpointProvider,
                $command,
                $operation,
                $commandArgs,
                $clientArgs,
                $headers
            );
        }

        return new Request(
            $operation['http']['method'],
            $this->endpoint,
            $headers,
            $this->jsonFormatter->build(
                $operation->getInput(),
                $commandArgs
            )
        );
    }
}


<?php
namespace Aws\Api\Serializer;

use Aws\Api\StructureShape;
use Aws\Api\ListShape;
use Aws\Api\MapShape;
use Aws\Api\Shape;
use Aws\Api\TimestampShape;

/**
 * @internal
 */
class QueryParamBuilder
{
    private $methods;

    protected function queryName(Shape $shape, $default = null)
    {
        if (null !== $shape['queryName']) {
            return $shape['queryName'];
        }

        if (null !== $shape['locationName']) {
            return $shape['locationName'];
        }

        if ($this->isFlat($shape) && !empty($shape['member']['locationName'])) {
            return $shape['member']['locationName'];
        }

        return $default;
    }

    protected function isFlat(Shape $shape)
    {
        return $shape['flattened'] === true;
    }

    public function __invoke(StructureShape $shape, array $params)
    {
        if (!$this->methods) {
            $this->methods = array_fill_keys(get_class_methods($this), true);
        }

        $query = [];
        $this->format_structure($shape, $params, '', $query);

        return $query;
    }

    protected function format(Shape $shape, $value, $prefix, array &$query)
    {
        $type = 'format_' . $shape['type'];
        if (isset($this->methods[$type])) {
            $this->{$type}($shape, $value, $prefix, $query);
        } else {
            $query[$prefix] = (string) $value;
        }
    }

    protected function format_structure(
        StructureShape $shape,
        array $value,
        $prefix,
        &$query
    ) {
        if ($prefix) {
            $prefix .= '.';
        }

        foreach ($value as $k => $v) {
            if ($shape->hasMember($k)) {
                $member = $shape->getMember($k);
                $this->format(
                    $member,
                    $v,
                    $prefix . $this->queryName($member, $k),
                    $query
                );
            }
        }
    }

    protected function format_list(
        ListShape $shape,
        array $value,
        $prefix,
        &$query
    ) {
        // Handle empty list serialization
        if (!$value) {
            $query[$prefix] = '';
            return;
        }

        $items = $shape->getMember();

        if (!$this->isFlat($shape)) {
            $locationName = $shape->getMember()['locationName'] ?: 'member';
            $prefix .= ".$locationName";
        } elseif ($name = $this->queryName($items)) {
            $parts = explode('.', $prefix);
            $parts[count($parts) - 1] = $name;
            $prefix = implode('.', $parts);
        }

        foreach ($value as $k => $v) {
            $this->format($items, $v, $prefix . '.' . ($k + 1), $query);
        }
    }

    protected function format_map(
        MapShape $shape,
        array $value,
        $prefix,
        array &$query
    ) {
        $vals = $shape->getValue();
        $keys = $shape->getKey();

        if (!$this->isFlat($shape)) {
            $prefix .= '.entry';
        }

        $i = 0;
        $keyName = '%s.%d.' . $this->queryName($keys, 'key');
        $valueName = '%s.%s.' . $this->queryName($vals, 'value');

        foreach ($value as $k => $v) {
            $i++;
            $this->format($keys, $k, sprintf($keyName, $prefix, $i), $query);
            $this->format($vals, $v, sprintf($valueName, $prefix, $i), $query);
        }
    }

    protected function format_blob(Shape $shape, $value, $prefix, array &$query)
    {
        $query[$prefix] = base64_encode($value);
    }

    protected function format_timestamp(
        TimestampShape $shape,
        $value,
        $prefix,
        array &$query
    ) {
        $timestampFormat = !empty($shape['timestampFormat'])
            ? $shape['timestampFormat']
            : 'iso8601';
        $query[$prefix] = TimestampShape::format($value, $timestampFormat);
    }

    protected function format_boolean(Shape $shape, $value, $prefix, array &$query)
    {
        $query[$prefix] = ($value) ? 'true' : 'false';
    }
}


<?php
namespace Aws\Api\Serializer;

use Aws\Api\Service;
use Aws\CommandInterface;
use Aws\EndpointV2\EndpointProviderV2;
use Aws\EndpointV2\EndpointV2SerializerTrait;
use GuzzleHttp\Psr7\Request;
use Psr\Http\Message\RequestInterface;

/**
 * Serializes a query protocol request.
 * @internal
 */
class QuerySerializer
{
    use EndpointV2SerializerTrait;

    private $endpoint;
    private $api;
    private $paramBuilder;

    public function __construct(
        Service $api,
        $endpoint,
        callable $paramBuilder = null
    ) {
        $this->api = $api;
        $this->endpoint = $endpoint;
        $this->paramBuilder = $paramBuilder ?: new QueryParamBuilder();
    }

    /**
     * When invoked with an AWS command, returns a serialization array
     * containing "method", "uri", "headers", and "body" key value pairs.
     *
     * @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());
        $body = [
            'Action'  => $command->getName(),
            'Version' => $this->api->getMetadata('apiVersion')
        ];
        $commandArgs = $command->toArray();

        // Only build up the parameters when there are parameters to build
        if ($commandArgs) {
            $body += call_user_func(
                $this->paramBuilder,
                $operation->getInput(),
                $commandArgs
            );
        }
        $body = http_build_query($body, '', '&', PHP_QUERY_RFC3986);
        $headers = [
            'Content-Length' => strlen($body),
            'Content-Type'   => 'application/x-www-form-urlencoded'
        ];

        if ($endpointProvider instanceof EndpointProviderV2) {
            $this->setRequestOptions(
                $endpointProvider,
                $command,
                $operation,
                $commandArgs,
                $clientArgs,
                $headers
            );
        }

        return new Request(
            'POST',
            $this->endpoint,
            $headers,
            $body
        );
    }
}


<?php
namespace Aws\Api\Serializer;

use Aws\Api\Service;
use Aws\Api\StructureShape;

/**
 * Serializes requests for the REST-JSON protocol.
 * @internal
 */
class RestJsonSerializer extends RestSerializer
{
    /** @var JsonBody */
    private $jsonFormatter;

    /** @var string */
    private $contentType;

    /**
     * @param Service  $api           Service API description
     * @param string   $endpoint      Endpoint to connect to
     * @param JsonBody $jsonFormatter Optional JSON formatter to use
     */
    public function __construct(
        Service $api,
        $endpoint,
        JsonBody $jsonFormatter = null
    ) {
        parent::__construct($api, $endpoint);
        $this->contentType = JsonBody::getContentType($api);
        $this->jsonFormatter = $jsonFormatter ?: new JsonBody($api);
    }

    protected function payload(StructureShape $member, array $value, array &$opts)
    {
        $body = isset($value) ?
            ((string) $this->jsonFormatter->build($member, $value))
            : "{}";
        $opts['headers']['Content-Type'] = $this->contentType;
        $opts['body'] = $body;
    }
}


<?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 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 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 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;
    }
}


<?php
namespace Aws\Api\Serializer;

use Aws\Api\StructureShape;
use Aws\Api\Service;

/**
 * @internal
 */
class RestXmlSerializer extends RestSerializer
{
    /** @var XmlBody */
    private $xmlBody;

    /**
     * @param Service $api      Service API description
     * @param string  $endpoint Endpoint to connect to
     * @param XmlBody $xmlBody  Optional XML formatter to use
     */
    public function __construct(
        Service $api,
        $endpoint,
        XmlBody $xmlBody = null
    ) {
        parent::__construct($api, $endpoint);
        $this->xmlBody = $xmlBody ?: new XmlBody($api);
    }

    protected function payload(StructureShape $member, array $value, array &$opts)
    {
        $opts['headers']['Content-Type'] = 'application/xml';
        $opts['body'] = $this->getXmlBody($member, $value);
    }

    /**
     * @param StructureShape $member
     * @param array $value
     * @return string
     */
    private function getXmlBody(StructureShape $member, array $value)
    {
        $xmlBody = (string)$this->xmlBody->build($member, $value);
        $xmlBody = str_replace("'", "'", $xmlBody);
        $xmlBody = str_replace('\r', "
", $xmlBody);
        $xmlBody = str_replace('\n', "
", $xmlBody);
        return $xmlBody;
    }
}


<?php
namespace Aws\Api\Serializer;

use Aws\Api\MapShape;
use Aws\Api\Service;
use Aws\Api\Shape;
use Aws\Api\StructureShape;
use Aws\Api\ListShape;
use Aws\Api\TimestampShape;
use XMLWriter;

/**
 * @internal Formats the XML body of a REST-XML services.
 */
class XmlBody
{
    /** @var \Aws\Api\Service */
    private $api;

    /**
     * @param Service $api API being used to create the XML body.
     */
    public function __construct(Service $api)
    {
        $this->api = $api;
    }

    /**
     * Builds the XML body based on an array of arguments.
     *
     * @param Shape $shape Operation being constructed
     * @param array $args  Associative array of arguments
     *
     * @return string
     */
    public function build(Shape $shape, array $args)
    {
        $xml = new XMLWriter();
        $xml->openMemory();
        $xml->startDocument('1.0', 'UTF-8');
        $this->format($shape, $shape['locationName'] ?: $shape['name'], $args, $xml);
        $xml->endDocument();

        return $xml->outputMemory();
    }

    private function startElement(Shape $shape, $name, XMLWriter $xml)
    {
        $xml->startElement($name);

        if ($ns = $shape['xmlNamespace']) {
            $xml->writeAttribute(
                isset($ns['prefix']) ? "xmlns:{$ns['prefix']}" : 'xmlns',
                $shape['xmlNamespace']['uri']
            );
        }
    }

    private function format(Shape $shape, $name, $value, XMLWriter $xml)
    {
        // Any method mentioned here has a custom serialization handler.
        static $methods = [
            'add_structure' => true,
            'add_list'      => true,
            'add_blob'      => true,
            'add_timestamp' => true,
            'add_boolean'   => true,
            'add_map'       => true,
            'add_string'    => true
        ];

        $type = 'add_' . $shape['type'];
        if (isset($methods[$type])) {
            $this->{$type}($shape, $name, $value, $xml);
        } else {
            $this->defaultShape($shape, $name, $value, $xml);
        }
    }

    private function defaultShape(Shape $shape, $name, $value, XMLWriter $xml)
    {
        $this->startElement($shape, $name, $xml);
        $xml->text($value);
        $xml->endElement();
    }

    private function add_structure(
        StructureShape $shape,
        $name,
        array $value,
        \XMLWriter $xml
    ) {
        $this->startElement($shape, $name, $xml);

        foreach ($this->getStructureMembers($shape, $value) as $k => $definition) {
            $this->format(
                $definition['member'],
                $definition['member']['locationName'] ?: $k,
                $definition['value'],
                $xml
            );
        }

        $xml->endElement();
    }

    private function getStructureMembers(StructureShape $shape, array $value)
    {
        $members = [];

        foreach ($value as $k => $v) {
            if ($v !== null && $shape->hasMember($k)) {
                $definition = [
                    'member' => $shape->getMember($k),
                    'value'  => $v,
                ];

                if ($definition['member']['xmlAttribute']) {
                    // array_unshift_associative
                    $members = [$k => $definition] + $members;
                } else {
                    $members[$k] = $definition;
                }
            }
        }

        return $members;
    }

    private function add_list(
        ListShape $shape,
        $name,
        array $value,
        XMLWriter $xml
    ) {
        $items = $shape->getMember();

        if ($shape['flattened']) {
            $elementName = $name;
        } else {
            $this->startElement($shape, $name, $xml);
            $elementName = $items['locationName'] ?: 'member';
        }

        foreach ($value as $v) {
            $this->format($items, $elementName, $v, $xml);
        }

        if (!$shape['flattened']) {
            $xml->endElement();
        }
    }

    private function add_map(
        MapShape $shape,
        $name,
        array $value,
        XMLWriter $xml
    ) {
        $xmlEntry = $shape['flattened'] ? $shape['locationName'] : 'entry';
        $xmlKey = $shape->getKey()['locationName'] ?: 'key';
        $xmlValue = $shape->getValue()['locationName'] ?: 'value';

        $this->startElement($shape, $name, $xml);

        foreach ($value as $key => $v) {
            $this->startElement($shape, $xmlEntry, $xml);
            $this->format($shape->getKey(), $xmlKey, $key, $xml);
            $this->format($shape->getValue(), $xmlValue, $v, $xml);
            $xml->endElement();
        }

        $xml->endElement();
    }

    private function add_blob(Shape $shape, $name, $value, XMLWriter $xml)
    {
        $this->startElement($shape, $name, $xml);
        $xml->writeRaw(base64_encode($value));
        $xml->endElement();
    }

    private function add_timestamp(
        TimestampShape $shape,
        $name,
        $value,
        XMLWriter $xml
    ) {
        $this->startElement($shape, $name, $xml);
        $timestampFormat = !empty($shape['timestampFormat'])
            ? $shape['timestampFormat']
            : 'iso8601';
        $xml->writeRaw(TimestampShape::format($value, $timestampFormat));
        $xml->endElement();
    }

    private function add_boolean(
        Shape $shape,
        $name,
        $value,
        XMLWriter $xml
    ) {
        $this->startElement($shape, $name, $xml);
        $xml->writeRaw($value ? 'true' : 'false');
        $xml->endElement();
    }

    private function add_string(
        Shape $shape,
        $name,
        $value,
        XMLWriter $xml
    ) {
        if ($shape['xmlAttribute']) {
            $xml->writeAttribute($shape['locationName'] ?: $name, $value);
        } else {
            $this->defaultShape($shape, $name, $value, $xml);
        }
    }
}


<?php
namespace Aws\ApiGateway;

use Aws\AwsClient;
use Aws\CommandInterface;
use Psr\Http\Message\RequestInterface;

/**
 * This client is used to interact with the **AWS API Gateway** service.
 *
 * @method \Aws\Result createApiKey(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createApiKeyAsync(array $args = [])
 * @method \Aws\Result createAuthorizer(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createAuthorizerAsync(array $args = [])
 * @method \Aws\Result createBasePathMapping(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createBasePathMappingAsync(array $args = [])
 * @method \Aws\Result createDeployment(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createDeploymentAsync(array $args = [])
 * @method \Aws\Result createDocumentationPart(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createDocumentationPartAsync(array $args = [])
 * @method \Aws\Result createDocumentationVersion(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createDocumentationVersionAsync(array $args = [])
 * @method \Aws\Result createDomainName(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createDomainNameAsync(array $args = [])
 * @method \Aws\Result createModel(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createModelAsync(array $args = [])
 * @method \Aws\Result createRequestValidator(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createRequestValidatorAsync(array $args = [])
 * @method \Aws\Result createResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createResourceAsync(array $args = [])
 * @method \Aws\Result createRestApi(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createRestApiAsync(array $args = [])
 * @method \Aws\Result createStage(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createStageAsync(array $args = [])
 * @method \Aws\Result createUsagePlan(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createUsagePlanAsync(array $args = [])
 * @method \Aws\Result createUsagePlanKey(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createUsagePlanKeyAsync(array $args = [])
 * @method \Aws\Result createVpcLink(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createVpcLinkAsync(array $args = [])
 * @method \Aws\Result deleteApiKey(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteApiKeyAsync(array $args = [])
 * @method \Aws\Result deleteAuthorizer(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteAuthorizerAsync(array $args = [])
 * @method \Aws\Result deleteBasePathMapping(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteBasePathMappingAsync(array $args = [])
 * @method \Aws\Result deleteClientCertificate(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteClientCertificateAsync(array $args = [])
 * @method \Aws\Result deleteDeployment(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteDeploymentAsync(array $args = [])
 * @method \Aws\Result deleteDocumentationPart(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteDocumentationPartAsync(array $args = [])
 * @method \Aws\Result deleteDocumentationVersion(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteDocumentationVersionAsync(array $args = [])
 * @method \Aws\Result deleteDomainName(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteDomainNameAsync(array $args = [])
 * @method \Aws\Result deleteGatewayResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteGatewayResponseAsync(array $args = [])
 * @method \Aws\Result deleteIntegration(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteIntegrationAsync(array $args = [])
 * @method \Aws\Result deleteIntegrationResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteIntegrationResponseAsync(array $args = [])
 * @method \Aws\Result deleteMethod(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteMethodAsync(array $args = [])
 * @method \Aws\Result deleteMethodResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteMethodResponseAsync(array $args = [])
 * @method \Aws\Result deleteModel(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteModelAsync(array $args = [])
 * @method \Aws\Result deleteRequestValidator(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteRequestValidatorAsync(array $args = [])
 * @method \Aws\Result deleteResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteResourceAsync(array $args = [])
 * @method \Aws\Result deleteRestApi(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteRestApiAsync(array $args = [])
 * @method \Aws\Result deleteStage(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteStageAsync(array $args = [])
 * @method \Aws\Result deleteUsagePlan(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteUsagePlanAsync(array $args = [])
 * @method \Aws\Result deleteUsagePlanKey(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteUsagePlanKeyAsync(array $args = [])
 * @method \Aws\Result deleteVpcLink(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteVpcLinkAsync(array $args = [])
 * @method \Aws\Result flushStageAuthorizersCache(array $args = [])
 * @method \GuzzleHttp\Promise\Promise flushStageAuthorizersCacheAsync(array $args = [])
 * @method \Aws\Result flushStageCache(array $args = [])
 * @method \GuzzleHttp\Promise\Promise flushStageCacheAsync(array $args = [])
 * @method \Aws\Result generateClientCertificate(array $args = [])
 * @method \GuzzleHttp\Promise\Promise generateClientCertificateAsync(array $args = [])
 * @method \Aws\Result getAccount(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getAccountAsync(array $args = [])
 * @method \Aws\Result getApiKey(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getApiKeyAsync(array $args = [])
 * @method \Aws\Result getApiKeys(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getApiKeysAsync(array $args = [])
 * @method \Aws\Result getAuthorizer(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getAuthorizerAsync(array $args = [])
 * @method \Aws\Result getAuthorizers(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getAuthorizersAsync(array $args = [])
 * @method \Aws\Result getBasePathMapping(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getBasePathMappingAsync(array $args = [])
 * @method \Aws\Result getBasePathMappings(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getBasePathMappingsAsync(array $args = [])
 * @method \Aws\Result getClientCertificate(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getClientCertificateAsync(array $args = [])
 * @method \Aws\Result getClientCertificates(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getClientCertificatesAsync(array $args = [])
 * @method \Aws\Result getDeployment(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getDeploymentAsync(array $args = [])
 * @method \Aws\Result getDeployments(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getDeploymentsAsync(array $args = [])
 * @method \Aws\Result getDocumentationPart(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getDocumentationPartAsync(array $args = [])
 * @method \Aws\Result getDocumentationParts(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getDocumentationPartsAsync(array $args = [])
 * @method \Aws\Result getDocumentationVersion(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getDocumentationVersionAsync(array $args = [])
 * @method \Aws\Result getDocumentationVersions(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getDocumentationVersionsAsync(array $args = [])
 * @method \Aws\Result getDomainName(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getDomainNameAsync(array $args = [])
 * @method \Aws\Result getDomainNames(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getDomainNamesAsync(array $args = [])
 * @method \Aws\Result getExport(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getExportAsync(array $args = [])
 * @method \Aws\Result getGatewayResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getGatewayResponseAsync(array $args = [])
 * @method \Aws\Result getGatewayResponses(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getGatewayResponsesAsync(array $args = [])
 * @method \Aws\Result getIntegration(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getIntegrationAsync(array $args = [])
 * @method \Aws\Result getIntegrationResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getIntegrationResponseAsync(array $args = [])
 * @method \Aws\Result getMethod(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getMethodAsync(array $args = [])
 * @method \Aws\Result getMethodResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getMethodResponseAsync(array $args = [])
 * @method \Aws\Result getModel(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getModelAsync(array $args = [])
 * @method \Aws\Result getModelTemplate(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getModelTemplateAsync(array $args = [])
 * @method \Aws\Result getModels(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getModelsAsync(array $args = [])
 * @method \Aws\Result getRequestValidator(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getRequestValidatorAsync(array $args = [])
 * @method \Aws\Result getRequestValidators(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getRequestValidatorsAsync(array $args = [])
 * @method \Aws\Result getResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResourceAsync(array $args = [])
 * @method \Aws\Result getResources(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResourcesAsync(array $args = [])
 * @method \Aws\Result getRestApi(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getRestApiAsync(array $args = [])
 * @method \Aws\Result getRestApis(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getRestApisAsync(array $args = [])
 * @method \Aws\Result getSdk(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getSdkAsync(array $args = [])
 * @method \Aws\Result getSdkType(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getSdkTypeAsync(array $args = [])
 * @method \Aws\Result getSdkTypes(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getSdkTypesAsync(array $args = [])
 * @method \Aws\Result getStage(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getStageAsync(array $args = [])
 * @method \Aws\Result getStages(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getStagesAsync(array $args = [])
 * @method \Aws\Result getTags(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getTagsAsync(array $args = [])
 * @method \Aws\Result getUsage(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getUsageAsync(array $args = [])
 * @method \Aws\Result getUsagePlan(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getUsagePlanAsync(array $args = [])
 * @method \Aws\Result getUsagePlanKey(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getUsagePlanKeyAsync(array $args = [])
 * @method \Aws\Result getUsagePlanKeys(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getUsagePlanKeysAsync(array $args = [])
 * @method \Aws\Result getUsagePlans(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getUsagePlansAsync(array $args = [])
 * @method \Aws\Result getVpcLink(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getVpcLinkAsync(array $args = [])
 * @method \Aws\Result getVpcLinks(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getVpcLinksAsync(array $args = [])
 * @method \Aws\Result importApiKeys(array $args = [])
 * @method \GuzzleHttp\Promise\Promise importApiKeysAsync(array $args = [])
 * @method \Aws\Result importDocumentationParts(array $args = [])
 * @method \GuzzleHttp\Promise\Promise importDocumentationPartsAsync(array $args = [])
 * @method \Aws\Result importRestApi(array $args = [])
 * @method \GuzzleHttp\Promise\Promise importRestApiAsync(array $args = [])
 * @method \Aws\Result putGatewayResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise putGatewayResponseAsync(array $args = [])
 * @method \Aws\Result putIntegration(array $args = [])
 * @method \GuzzleHttp\Promise\Promise putIntegrationAsync(array $args = [])
 * @method \Aws\Result putIntegrationResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise putIntegrationResponseAsync(array $args = [])
 * @method \Aws\Result putMethod(array $args = [])
 * @method \GuzzleHttp\Promise\Promise putMethodAsync(array $args = [])
 * @method \Aws\Result putMethodResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise putMethodResponseAsync(array $args = [])
 * @method \Aws\Result putRestApi(array $args = [])
 * @method \GuzzleHttp\Promise\Promise putRestApiAsync(array $args = [])
 * @method \Aws\Result tagResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
 * @method \Aws\Result testInvokeAuthorizer(array $args = [])
 * @method \GuzzleHttp\Promise\Promise testInvokeAuthorizerAsync(array $args = [])
 * @method \Aws\Result testInvokeMethod(array $args = [])
 * @method \GuzzleHttp\Promise\Promise testInvokeMethodAsync(array $args = [])
 * @method \Aws\Result untagResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
 * @method \Aws\Result updateAccount(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateAccountAsync(array $args = [])
 * @method \Aws\Result updateApiKey(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateApiKeyAsync(array $args = [])
 * @method \Aws\Result updateAuthorizer(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateAuthorizerAsync(array $args = [])
 * @method \Aws\Result updateBasePathMapping(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateBasePathMappingAsync(array $args = [])
 * @method \Aws\Result updateClientCertificate(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateClientCertificateAsync(array $args = [])
 * @method \Aws\Result updateDeployment(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateDeploymentAsync(array $args = [])
 * @method \Aws\Result updateDocumentationPart(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateDocumentationPartAsync(array $args = [])
 * @method \Aws\Result updateDocumentationVersion(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateDocumentationVersionAsync(array $args = [])
 * @method \Aws\Result updateDomainName(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateDomainNameAsync(array $args = [])
 * @method \Aws\Result updateGatewayResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateGatewayResponseAsync(array $args = [])
 * @method \Aws\Result updateIntegration(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateIntegrationAsync(array $args = [])
 * @method \Aws\Result updateIntegrationResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateIntegrationResponseAsync(array $args = [])
 * @method \Aws\Result updateMethod(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateMethodAsync(array $args = [])
 * @method \Aws\Result updateMethodResponse(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateMethodResponseAsync(array $args = [])
 * @method \Aws\Result updateModel(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateModelAsync(array $args = [])
 * @method \Aws\Result updateRequestValidator(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateRequestValidatorAsync(array $args = [])
 * @method \Aws\Result updateResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateResourceAsync(array $args = [])
 * @method \Aws\Result updateRestApi(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateRestApiAsync(array $args = [])
 * @method \Aws\Result updateStage(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateStageAsync(array $args = [])
 * @method \Aws\Result updateUsage(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateUsageAsync(array $args = [])
 * @method \Aws\Result updateUsagePlan(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateUsagePlanAsync(array $args = [])
 * @method \Aws\Result updateVpcLink(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateVpcLinkAsync(array $args = [])
 */
class ApiGatewayClient extends AwsClient
{
    public function __construct(array $args)
    {
        parent::__construct($args);
        $stack = $this->getHandlerList();
        $stack->appendBuild([__CLASS__, '_add_accept_header']);
    }

    public static function _add_accept_header(callable $handler)
    {
        return function (
            CommandInterface $command,
            RequestInterface $request
        ) use ($handler) {
            $request = $request->withHeader('Accept', 'application/json');

            return $handler($command, $request);
        };
    }
}


<?php
namespace Aws\ApiGateway\Exception;

use Aws\Exception\AwsException;

/**
 * Represents an error interacting with the **AWS API Gateway** service.
 */
class ApiGatewayException extends AwsException {}


<?php
namespace Aws\ApiGatewayManagementApi;

use Aws\AwsClient;

/**
 * This client is used to interact with the **AmazonApiGatewayManagementApi** service.
 * @method \Aws\Result deleteConnection(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteConnectionAsync(array $args = [])
 * @method \Aws\Result getConnection(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getConnectionAsync(array $args = [])
 * @method \Aws\Result postToConnection(array $args = [])
 * @method \GuzzleHttp\Promise\Promise postToConnectionAsync(array $args = [])
 */
class ApiGatewayManagementApiClient extends AwsClient {}


<?php
namespace Aws\ApiGatewayManagementApi\Exception;

use Aws\Exception\AwsException;

/**
 * Represents an error interacting with the **AmazonApiGatewayManagementApi** service.
 */
class ApiGatewayManagementApiException extends AwsException {}