1 | <?php |
||
2 | |||
3 | namespace AlibabaCloud\Client\Request; |
||
4 | |||
5 | use Exception; |
||
6 | use AlibabaCloud\Client\Support\Stringy; |
||
7 | use RuntimeException; |
||
8 | use AlibabaCloud\Client\SDK; |
||
9 | use AlibabaCloud\Client\Encode; |
||
10 | use AlibabaCloud\Client\Accept; |
||
11 | use AlibabaCloud\Client\Support\Path; |
||
12 | use AlibabaCloud\Client\Support\Sign; |
||
13 | use AlibabaCloud\Client\Filter\Filter; |
||
14 | use AlibabaCloud\Client\Support\Arrays; |
||
15 | use AlibabaCloud\Client\Filter\ApiFilter; |
||
16 | use AlibabaCloud\Client\Credentials\StsCredential; |
||
17 | use AlibabaCloud\Client\Exception\ClientException; |
||
18 | use AlibabaCloud\Client\Exception\ServerException; |
||
19 | use AlibabaCloud\Client\Credentials\AccessKeyCredential; |
||
20 | use AlibabaCloud\Client\Credentials\BearerTokenCredential; |
||
21 | use AlibabaCloud\Client\Request\Traits\DeprecatedRoaTrait; |
||
22 | |||
23 | /** |
||
24 | * RESTful ROA Request. |
||
25 | * |
||
26 | * @package AlibabaCloud\Client\Request |
||
27 | * @method setParameter() |
||
28 | */ |
||
29 | class RoaRequest extends Request |
||
30 | { |
||
31 | use DeprecatedRoaTrait; |
||
32 | |||
33 | /** |
||
34 | * @var string |
||
35 | */ |
||
36 | public $pathPattern = '/'; |
||
37 | |||
38 | /** |
||
39 | * @var array |
||
40 | */ |
||
41 | public $pathParameters = []; |
||
42 | |||
43 | /** |
||
44 | * @var string |
||
45 | */ |
||
46 | private $dateTimeFormat = "D, d M Y H:i:s \G\M\T"; |
||
47 | |||
48 | /** |
||
49 | * Resolve request parameter. |
||
50 | * |
||
51 | * @throws ClientException |
||
52 | * @throws Exception |
||
53 | */ |
||
54 | 18 | public function resolveParameter() |
|
55 | { |
||
56 | 18 | $this->resolveQuery(); |
|
57 | 18 | $this->resolveHeaders(); |
|
58 | 18 | $this->resolveBody(); |
|
59 | 18 | $this->resolveUri(); |
|
60 | 18 | $this->resolveSignature(); |
|
61 | 18 | } |
|
62 | |||
63 | 18 | private function resolveQuery() |
|
64 | { |
||
65 | 18 | if (!isset($this->options['query']['Version'])) { |
|
66 | 18 | $this->options['query']['Version'] = $this->version; |
|
67 | 18 | } |
|
68 | 18 | } |
|
69 | |||
70 | 18 | private function resolveBody() |
|
71 | { |
||
72 | // If the body has already been specified, it will not be resolved. |
||
73 | 18 | if (isset($this->options['body'])) { |
|
74 | 1 | return; |
|
75 | } |
||
76 | |||
77 | 17 | if (!isset($this->options['form_params'])) { |
|
78 | 14 | return; |
|
79 | } |
||
80 | |||
81 | // Merge data, compatible with parameters set from constructor. |
||
82 | 3 | $params = Arrays::merge( |
|
83 | [ |
||
84 | 3 | $this->data, |
|
85 | 3 | $this->options['form_params'] |
|
86 | 3 | ] |
|
87 | 3 | ); |
|
88 | |||
89 | 3 | $this->encodeBody($params); |
|
90 | |||
91 | 3 | unset($this->options['form_params']); |
|
92 | 3 | } |
|
93 | |||
94 | /** |
||
95 | * Determine the body format based on the Content-Type and calculate the MD5 value. |
||
96 | * |
||
97 | * @param array $params |
||
98 | */ |
||
99 | 3 | private function encodeBody(array $params) |
|
100 | { |
||
101 | 3 | if (Stringy::contains($this->options['headers']['Content-Type'], 'application/json', false)) { |
|
0 ignored issues
–
show
|
|||
102 | $this->options['body'] = json_encode($params); |
||
103 | 3 | $this->options['headers']['Content-MD5'] = base64_encode(md5($this->options['body'], true)); |
|
104 | 2 | ||
105 | 2 | return; |
|
106 | } |
||
107 | 2 | ||
108 | $this->options['body'] = Encode::create($params)->ksort()->toString(); |
||
109 | $this->options['headers']['Content-MD5'] = base64_encode(md5($this->options['body'], true)); |
||
110 | 1 | $this->options['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; |
|
111 | 1 | } |
|
112 | 1 | ||
113 | 1 | /** |
|
114 | * @throws ClientException |
||
115 | * @throws ServerException |
||
116 | * @throws Exception |
||
117 | */ |
||
118 | private function resolveHeaders() |
||
119 | { |
||
120 | 18 | $this->options['headers']['x-acs-version'] = $this->version; |
|
121 | $this->options['headers']['x-acs-region-id'] = $this->realRegionId(); |
||
122 | 18 | $this->options['headers']['Date'] = gmdate($this->dateTimeFormat); |
|
123 | 18 | ||
124 | 18 | $signature = $this->httpClient()->getSignature(); |
|
125 | $this->options['headers']['x-acs-signature-method'] = $signature->getMethod(); |
||
126 | 18 | $this->options['headers']['x-acs-signature-nonce'] = Sign::uuid($this->product . $this->action); |
|
127 | 18 | $this->options['headers']['x-acs-signature-version'] = $signature->getVersion(); |
|
128 | 18 | if ($signature->getType()) { |
|
129 | 18 | $this->options['headers']['x-acs-signature-type'] = $signature->getType(); |
|
130 | 18 | } |
|
131 | 4 | ||
132 | 4 | $this->resolveAccept(); |
|
133 | $this->resolveContentType(); |
||
134 | 18 | $this->resolveSecurityToken(); |
|
135 | 18 | $this->resolveBearerToken(); |
|
136 | 18 | } |
|
137 | 18 | ||
138 | 18 | /** |
|
139 | * @throws ClientException |
||
140 | * @throws Exception |
||
141 | */ |
||
142 | private function resolveSignature() |
||
143 | { |
||
144 | 18 | $this->options['headers']['Authorization'] = $this->signature(); |
|
145 | } |
||
146 | 18 | ||
147 | 18 | /** |
|
148 | * If accept is not specified, it is determined by format. |
||
149 | */ |
||
150 | private function resolveAccept() |
||
151 | { |
||
152 | 18 | if (!isset($this->options['headers']['Accept'])) { |
|
153 | $this->options['headers']['Accept'] = Accept::create($this->format)->toString(); |
||
154 | 18 | } |
|
155 | 17 | } |
|
156 | 17 | ||
157 | 18 | /** |
|
158 | * If the Content-Type is not specified, it is determined according to accept. |
||
159 | */ |
||
160 | private function resolveContentType() |
||
161 | { |
||
162 | 18 | if (!isset($this->options['headers']['Content-Type'])) { |
|
163 | $this->options['headers']['Content-Type'] = "{$this->options['headers']['Accept']}; charset=utf-8"; |
||
164 | 18 | } |
|
165 | 17 | } |
|
166 | 17 | ||
167 | 18 | /** |
|
168 | * @throws ClientException |
||
169 | * @throws ServerException |
||
170 | */ |
||
171 | private function resolveSecurityToken() |
||
172 | { |
||
173 | 20 | if (!$this->credential() instanceof StsCredential) { |
|
174 | return; |
||
175 | 20 | } |
|
176 | 18 | ||
177 | if (!$this->credential()->getSecurityToken()) { |
||
178 | return; |
||
179 | 2 | } |
|
180 | 1 | ||
181 | $this->options['headers']['x-acs-security-token'] = $this->credential()->getSecurityToken(); |
||
182 | } |
||
183 | 1 | ||
184 | 1 | /** |
|
185 | * @throws ClientException |
||
186 | * @throws ServerException |
||
187 | */ |
||
188 | private function resolveBearerToken() |
||
189 | { |
||
190 | 18 | if ($this->credential() instanceof BearerTokenCredential) { |
|
191 | $this->options['headers']['x-acs-bearer-token'] = $this->credential()->getBearerToken(); |
||
192 | 18 | } |
|
193 | 4 | } |
|
194 | 4 | ||
195 | 18 | /** |
|
196 | * Sign the request message. |
||
197 | * |
||
198 | * @return string |
||
199 | * @throws ClientException |
||
200 | * @throws ServerException |
||
201 | */ |
||
202 | private function signature() |
||
203 | { |
||
204 | 18 | /** |
|
205 | * @var AccessKeyCredential $credential |
||
206 | */ |
||
207 | $credential = $this->credential(); |
||
208 | $access_key_id = $credential->getAccessKeyId(); |
||
209 | 18 | $signature = $this->httpClient() |
|
210 | 18 | ->getSignature() |
|
211 | 18 | ->sign( |
|
212 | 18 | $this->stringToSign(), |
|
213 | 18 | $credential->getAccessKeySecret() |
|
214 | 18 | ); |
|
215 | 18 | ||
216 | 18 | return "acs $access_key_id:$signature"; |
|
217 | } |
||
218 | 18 | ||
219 | /** |
||
220 | * @return void |
||
221 | */ |
||
222 | private function resolveUri() |
||
223 | { |
||
224 | 18 | $path = Path::assign($this->pathPattern, $this->pathParameters); |
|
225 | |||
226 | 18 | $this->uri = $this->uri->withPath($path) |
|
227 | ->withQuery( |
||
228 | 18 | $this->queryString() |
|
229 | 18 | ); |
|
230 | 18 | } |
|
231 | 18 | ||
232 | 18 | /** |
|
233 | * @return string |
||
234 | */ |
||
235 | public function stringToSign() |
||
236 | { |
||
237 | 18 | $request = new \GuzzleHttp\Psr7\Request( |
|
238 | $this->method, |
||
239 | 18 | $this->uri, |
|
240 | 18 | $this->options['headers'] |
|
241 | 18 | ); |
|
242 | 18 | ||
243 | 18 | return Sign::roaString($request); |
|
244 | } |
||
245 | 18 | ||
246 | /** |
||
247 | * @return bool|string |
||
248 | */ |
||
249 | private function queryString() |
||
250 | { |
||
251 | 18 | $query = isset($this->options['query']) |
|
252 | ? $this->options['query'] |
||
253 | 18 | : []; |
|
254 | 18 | ||
255 | 18 | return Encode::create($query)->ksort()->toString(); |
|
256 | } |
||
257 | 18 | ||
258 | /** |
||
259 | * Set path parameter by name. |
||
260 | * |
||
261 | * @param string $name |
||
262 | * @param string $value |
||
263 | * |
||
264 | * @return RoaRequest |
||
265 | * @throws ClientException |
||
266 | */ |
||
267 | public function pathParameter($name, $value) |
||
268 | { |
||
269 | 11 | Filter::name($name); |
|
270 | |||
271 | 11 | if ($value === '') { |
|
272 | throw new ClientException( |
||
273 | 9 | 'Value cannot be empty', |
|
274 | 1 | SDK::INVALID_ARGUMENT |
|
275 | 1 | ); |
|
276 | } |
||
277 | 1 | ||
278 | $this->pathParameters[$name] = $value; |
||
279 | |||
280 | 8 | return $this; |
|
281 | } |
||
282 | 8 | ||
283 | /** |
||
284 | * Set path pattern. |
||
285 | * |
||
286 | * @param string $pattern |
||
287 | * |
||
288 | * @return self |
||
289 | * @throws ClientException |
||
290 | */ |
||
291 | public function pathPattern($pattern) |
||
292 | { |
||
293 | 10 | ApiFilter::pattern($pattern); |
|
294 | |||
295 | 10 | $this->pathPattern = $pattern; |
|
296 | |||
297 | 8 | return $this; |
|
298 | } |
||
299 | 8 | ||
300 | /** |
||
301 | * Magic method for set or get request parameters. |
||
302 | * |
||
303 | * @param string $name |
||
304 | * @param mixed $arguments |
||
305 | * |
||
306 | * @return $this |
||
307 | */ |
||
308 | public function __call($name, $arguments) |
||
309 | { |
||
310 | 12 | if (strncmp($name, 'get', 3) === 0) { |
|
311 | $parameter_name = \mb_strcut($name, 3); |
||
312 | 12 | ||
313 | 1 | return $this->__get($parameter_name); |
|
314 | } |
||
315 | 1 | ||
316 | if (strncmp($name, 'with', 4) === 0) { |
||
317 | $parameter_name = \mb_strcut($name, 4); |
||
318 | 12 | $this->__set($parameter_name, $arguments[0]); |
|
319 | 10 | $this->pathParameters[$parameter_name] = $arguments[0]; |
|
320 | 10 | ||
321 | 10 | return $this; |
|
322 | } |
||
323 | 10 | ||
324 | if (strncmp($name, 'set', 3) === 0) { |
||
325 | $parameter_name = \mb_strcut($name, 3); |
||
326 | 2 | $with_method = "with$parameter_name"; |
|
327 | 1 | ||
328 | 1 | throw new RuntimeException("Please use $with_method instead of $name"); |
|
329 | } |
||
330 | 1 | ||
331 | throw new RuntimeException('Call to undefined method ' . __CLASS__ . '::' . $name . '()'); |
||
332 | } |
||
333 | } |
||
334 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.