PluginProbe ʕ •ᴥ•ʔ
Transferito: WP Migration / trunk
Transferito: WP Migration vtrunk
trunk 11.4.0 12.0.0 13.1.0 14.0.0 14.0.11 14.0.7 14.1.0 14.1.1 14.1.2 14.1.3 14.1.4
transferito / vendor / aws / aws-sdk-php / src / Api / Serializer / RestSerializer.php
transferito / vendor / aws / aws-sdk-php / src / Api / Serializer Last commit date
Ec2ParamBuilder.php 11 months ago JsonBody.php 11 months ago JsonRpcSerializer.php 11 months ago QueryParamBuilder.php 11 months ago QuerySerializer.php 11 months ago RestJsonSerializer.php 11 months ago RestSerializer.php 11 months ago RestXmlSerializer.php 11 months ago XmlBody.php 11 months ago
RestSerializer.php
308 lines
1 <?php
2 namespace Aws\Api\Serializer;
3
4 use Aws\Api\MapShape;
5 use Aws\Api\Service;
6 use Aws\Api\Operation;
7 use Aws\Api\Shape;
8 use Aws\Api\StructureShape;
9 use Aws\Api\TimestampShape;
10 use Aws\CommandInterface;
11 use Aws\EndpointV2\EndpointProviderV2;
12 use Aws\EndpointV2\EndpointV2SerializerTrait;
13 use Aws\EndpointV2\Ruleset\RulesetEndpoint;
14 use GuzzleHttp\Psr7;
15 use GuzzleHttp\Psr7\Request;
16 use GuzzleHttp\Psr7\Uri;
17 use GuzzleHttp\Psr7\UriResolver;
18 use Psr\Http\Message\RequestInterface;
19
20 /**
21 * Serializes HTTP locations like header, uri, payload, etc...
22 * @internal
23 */
24 abstract class RestSerializer
25 {
26 use EndpointV2SerializerTrait;
27
28 /** @var Service */
29 private $api;
30
31 /** @var Uri */
32 private $endpoint;
33
34 /**
35 * @param Service $api Service API description
36 * @param string $endpoint Endpoint to connect to
37 */
38 public function __construct(Service $api, $endpoint)
39 {
40 $this->api = $api;
41 $this->endpoint = Psr7\Utils::uriFor($endpoint);
42 }
43
44 /**
45 * @param CommandInterface $command Command to serialize into a request.
46 * @param $endpointProvider Provider used for dynamic endpoint resolution.
47 * @param $clientArgs Client arguments used for dynamic endpoint resolution.
48 *
49 * @return RequestInterface
50 */
51 public function __invoke(
52 CommandInterface $command,
53 $endpoint = null
54 )
55 {
56 $operation = $this->api->getOperation($command->getName());
57 $commandArgs = $command->toArray();
58 $opts = $this->serialize($operation, $commandArgs);
59 $headers = isset($opts['headers']) ? $opts['headers'] : [];
60
61 if ($endpoint instanceof RulesetEndpoint) {
62 $this->setEndpointV2RequestOptions($endpoint, $headers);
63 }
64
65 $uri = $this->buildEndpoint($operation, $commandArgs, $opts);
66
67 return new Request(
68 $operation['http']['method'],
69 $uri,
70 $headers,
71 isset($opts['body']) ? $opts['body'] : null
72 );
73 }
74
75 /**
76 * Modifies a hash of request options for a payload body.
77 *
78 * @param StructureShape $member Member to serialize
79 * @param array $value Value to serialize
80 * @param array $opts Request options to modify.
81 */
82 abstract protected function payload(
83 StructureShape $member,
84 array $value,
85 array &$opts
86 );
87
88 private function serialize(Operation $operation, array $args)
89 {
90 $opts = [];
91 $input = $operation->getInput();
92
93 // Apply the payload trait if present
94 if ($payload = $input['payload']) {
95 $this->applyPayload($input, $payload, $args, $opts);
96 }
97
98 foreach ($args as $name => $value) {
99 if ($input->hasMember($name)) {
100 $member = $input->getMember($name);
101 $location = $member['location'];
102 if (!$payload && !$location) {
103 $bodyMembers[$name] = $value;
104 } elseif ($location == 'header') {
105 $this->applyHeader($name, $member, $value, $opts);
106 } elseif ($location == 'querystring') {
107 $this->applyQuery($name, $member, $value, $opts);
108 } elseif ($location == 'headers') {
109 $this->applyHeaderMap($name, $member, $value, $opts);
110 }
111 }
112 }
113
114 if (isset($bodyMembers)) {
115 $this->payload($operation->getInput(), $bodyMembers, $opts);
116 } else if (!isset($opts['body']) && $this->hasPayloadParam($input, $payload)) {
117 $this->payload($operation->getInput(), [], $opts);
118 }
119
120 return $opts;
121 }
122
123 private function applyPayload(StructureShape $input, $name, array $args, array &$opts)
124 {
125 if (!isset($args[$name])) {
126 return;
127 }
128
129 $m = $input->getMember($name);
130
131 if ($m['streaming'] ||
132 ($m['type'] == 'string' || $m['type'] == 'blob')
133 ) {
134 // Streaming bodies or payloads that are strings are
135 // always just a stream of data.
136 $opts['body'] = Psr7\Utils::streamFor($args[$name]);
137 return;
138 }
139
140 $this->payload($m, $args[$name], $opts);
141 }
142
143 private function applyHeader($name, Shape $member, $value, array &$opts)
144 {
145 if ($member->getType() === 'timestamp') {
146 $timestampFormat = !empty($member['timestampFormat'])
147 ? $member['timestampFormat']
148 : 'rfc822';
149 $value = TimestampShape::format($value, $timestampFormat);
150 } elseif ($member->getType() === 'boolean') {
151 $value = $value ? 'true' : 'false';
152 }
153
154 if ($member['jsonvalue']) {
155 $value = json_encode($value);
156 if (empty($value) && JSON_ERROR_NONE !== json_last_error()) {
157 throw new \InvalidArgumentException('Unable to encode the provided value'
158 . ' with \'json_encode\'. ' . json_last_error_msg());
159 }
160
161 $value = base64_encode($value);
162 }
163
164 $opts['headers'][$member['locationName'] ?: $name] = $value;
165 }
166
167 /**
168 * Note: This is currently only present in the Amazon S3 model.
169 */
170 private function applyHeaderMap($name, Shape $member, array $value, array &$opts)
171 {
172 $prefix = $member['locationName'];
173 foreach ($value as $k => $v) {
174 $opts['headers'][$prefix . $k] = $v;
175 }
176 }
177
178 private function applyQuery($name, Shape $member, $value, array &$opts)
179 {
180 if ($member instanceof MapShape) {
181 $opts['query'] = isset($opts['query']) && is_array($opts['query'])
182 ? $opts['query'] + $value
183 : $value;
184 } elseif ($value !== null) {
185 $type = $member->getType();
186 if ($type === 'boolean') {
187 $value = $value ? 'true' : 'false';
188 } elseif ($type === 'timestamp') {
189 $timestampFormat = !empty($member['timestampFormat'])
190 ? $member['timestampFormat']
191 : 'iso8601';
192 $value = TimestampShape::format($value, $timestampFormat);
193 }
194
195 $opts['query'][$member['locationName'] ?: $name] = $value;
196 }
197 }
198
199 private function buildEndpoint(Operation $operation, array $args, array $opts)
200 {
201 // Create an associative array of variable definitions used in expansions
202 $varDefinitions = $this->getVarDefinitions($operation, $args);
203
204 $relative = preg_replace_callback(
205 '/\{([^\}]+)\}/',
206 function (array $matches) use ($varDefinitions) {
207 $isGreedy = substr($matches[1], -1, 1) == '+';
208 $k = $isGreedy ? substr($matches[1], 0, -1) : $matches[1];
209 if (!isset($varDefinitions[$k])) {
210 return '';
211 }
212
213 if ($isGreedy) {
214 return str_replace('%2F', '/', rawurlencode($varDefinitions[$k]));
215 }
216
217 return rawurlencode($varDefinitions[$k]);
218 },
219 $operation['http']['requestUri']
220 );
221
222 // Add the query string variables or appending to one if needed.
223 if (!empty($opts['query'])) {
224 $relative = $this->appendQuery($opts['query'], $relative);
225 }
226
227 $path = $this->endpoint->getPath();
228
229 //Accounts for trailing '/' in path when custom endpoint
230 //is provided to endpointProviderV2
231 if ($this->api->isModifiedModel()
232 && $this->api->getServiceName() === 's3'
233 ) {
234 if (substr($path, -1) === '/' && $relative[0] === '/') {
235 $path = rtrim($path, '/');
236 }
237 $relative = $path . $relative;
238
239 if (strpos($relative, '../') !== false) {
240 if ($relative[0] !== '/') {
241 $relative = '/' . $relative;
242 }
243 return new Uri($this->endpoint . $relative);
244 }
245 }
246 // If endpoint has path, remove leading '/' to preserve URI resolution.
247 if ($path && $relative[0] === '/') {
248 $relative = substr($relative, 1);
249 }
250
251 //Append path to endpoint when leading '//...'
252 // present as uri cannot be properly resolved
253 if ($this->api->isModifiedModel()
254 && strpos($relative, '//') === 0
255 ) {
256 return new Uri($this->endpoint . $relative);
257 }
258
259 // Expand path place holders using Amazon's slightly different URI
260 // template syntax.
261 return UriResolver::resolve($this->endpoint, new Uri($relative));
262 }
263
264 /**
265 * @param StructureShape $input
266 */
267 private function hasPayloadParam(StructureShape $input, $payload)
268 {
269 if ($payload) {
270 $potentiallyEmptyTypes = ['blob','string'];
271 if ($this->api->getMetadata('protocol') == 'rest-xml') {
272 $potentiallyEmptyTypes[] = 'structure';
273 }
274 $payloadMember = $input->getMember($payload);
275 if (in_array($payloadMember['type'], $potentiallyEmptyTypes)) {
276 return false;
277 }
278 }
279 foreach ($input->getMembers() as $member) {
280 if (!isset($member['location'])) {
281 return true;
282 }
283 }
284 return false;
285 }
286
287 private function appendQuery($query, $endpoint)
288 {
289 $append = Psr7\Query::build($query);
290 return $endpoint .= strpos($endpoint, '?') !== false ? "&{$append}" : "?{$append}";
291 }
292
293 private function getVarDefinitions($command, $args)
294 {
295 $varDefinitions = [];
296
297 foreach ($command->getInput()->getMembers() as $name => $member) {
298 if ($member['location'] == 'uri') {
299 $varDefinitions[$member['locationName'] ?: $name] =
300 isset($args[$name])
301 ? $args[$name]
302 : null;
303 }
304 }
305 return $varDefinitions;
306 }
307 }
308