AbstractRestParser.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. <?php
  2. namespace Aws\Api\Parser;
  3. use Aws\Api\DateTimeResult;
  4. use Aws\Api\Shape;
  5. use Aws\Api\StructureShape;
  6. use Aws\Result;
  7. use Aws\CommandInterface;
  8. use Psr\Http\Message\ResponseInterface;
  9. /**
  10. * @internal
  11. */
  12. abstract class AbstractRestParser extends AbstractParser
  13. {
  14. use PayloadParserTrait;
  15. /**
  16. * Parses a payload from a response.
  17. *
  18. * @param ResponseInterface $response Response to parse.
  19. * @param StructureShape $member Member to parse
  20. * @param array $result Result value
  21. *
  22. * @return mixed
  23. */
  24. abstract protected function payload(
  25. ResponseInterface $response,
  26. StructureShape $member,
  27. array &$result
  28. );
  29. public function __invoke(
  30. CommandInterface $command,
  31. ResponseInterface $response
  32. ) {
  33. $output = $this->api->getOperation($command->getName())->getOutput();
  34. $result = [];
  35. if ($payload = $output['payload']) {
  36. $this->extractPayload($payload, $output, $response, $result);
  37. }
  38. foreach ($output->getMembers() as $name => $member) {
  39. switch ($member['location']) {
  40. case 'header':
  41. $this->extractHeader($name, $member, $response, $result);
  42. break;
  43. case 'headers':
  44. $this->extractHeaders($name, $member, $response, $result);
  45. break;
  46. case 'statusCode':
  47. $this->extractStatus($name, $response, $result);
  48. break;
  49. }
  50. }
  51. if (!$payload
  52. && $response->getBody()->getSize() > 0
  53. && count($output->getMembers()) > 0
  54. ) {
  55. // if no payload was found, then parse the contents of the body
  56. $this->payload($response, $output, $result);
  57. }
  58. return new Result($result);
  59. }
  60. private function extractPayload(
  61. $payload,
  62. StructureShape $output,
  63. ResponseInterface $response,
  64. array &$result
  65. ) {
  66. $member = $output->getMember($payload);
  67. if (!empty($member['eventstream'])) {
  68. $result[$payload] = new EventParsingIterator(
  69. $response->getBody(),
  70. $member,
  71. $this
  72. );
  73. } else if ($member instanceof StructureShape) {
  74. // Structure members parse top-level data into a specific key.
  75. $result[$payload] = [];
  76. $this->payload($response, $member, $result[$payload]);
  77. } else {
  78. // Streaming data is just the stream from the response body.
  79. $result[$payload] = $response->getBody();
  80. }
  81. }
  82. /**
  83. * Extract a single header from the response into the result.
  84. */
  85. private function extractHeader(
  86. $name,
  87. Shape $shape,
  88. ResponseInterface $response,
  89. &$result
  90. ) {
  91. $value = $response->getHeaderLine($shape['locationName'] ?: $name);
  92. switch ($shape->getType()) {
  93. case 'float':
  94. case 'double':
  95. $value = (float) $value;
  96. break;
  97. case 'long':
  98. $value = (int) $value;
  99. break;
  100. case 'boolean':
  101. $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
  102. break;
  103. case 'blob':
  104. $value = base64_decode($value);
  105. break;
  106. case 'timestamp':
  107. try {
  108. $value = DateTimeResult::fromTimestamp(
  109. $value,
  110. !empty($shape['timestampFormat']) ? $shape['timestampFormat'] : null
  111. );
  112. break;
  113. } catch (\Exception $e) {
  114. // If the value cannot be parsed, then do not add it to the
  115. // output structure.
  116. return;
  117. }
  118. case 'string':
  119. try {
  120. if ($shape['jsonvalue']) {
  121. $value = $this->parseJson(base64_decode($value), $response);
  122. }
  123. // If value is not set, do not add to output structure.
  124. if (!isset($value)) {
  125. return;
  126. }
  127. break;
  128. } catch (\Exception $e) {
  129. //If the value cannot be parsed, then do not add it to the
  130. //output structure.
  131. return;
  132. }
  133. }
  134. $result[$name] = $value;
  135. }
  136. /**
  137. * Extract a map of headers with an optional prefix from the response.
  138. */
  139. private function extractHeaders(
  140. $name,
  141. Shape $shape,
  142. ResponseInterface $response,
  143. &$result
  144. ) {
  145. // Check if the headers are prefixed by a location name
  146. $result[$name] = [];
  147. $prefix = $shape['locationName'];
  148. $prefixLen = $prefix !== null ? strlen($prefix) : 0;
  149. foreach ($response->getHeaders() as $k => $values) {
  150. if (!$prefixLen) {
  151. $result[$name][$k] = implode(', ', $values);
  152. } elseif (stripos($k, $prefix) === 0) {
  153. $result[$name][substr($k, $prefixLen)] = implode(', ', $values);
  154. }
  155. }
  156. }
  157. /**
  158. * Places the status code of the response into the result array.
  159. */
  160. private function extractStatus(
  161. $name,
  162. ResponseInterface $response,
  163. array &$result
  164. ) {
  165. $result[$name] = (int) $response->getStatusCode();
  166. }
  167. }