PluginProbe
StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more / 2.6.0
StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more v2.6.0
3.7.2 3.7.1 3.7.0 3.6.0 3.6.1 3.5.2 trunk 2.0 2.1 2.2 2.3 2.4 2.4.1 2.4.2 2.4.3 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.6.0 2.6.1 All 73 releases
stockpack / vendor / guzzlehttp / guzzle / src / Client.php

Client.php in StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more 2.6.0, at vendor/guzzlehttp/guzzle/src/Client.php

496 lines 18.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace GuzzleHttp;
3
4 use GuzzleHttp\Cookie\CookieJar;
5 use GuzzleHttp\Promise;
6 use GuzzleHttp\Psr7;
7 use Psr\Http\Message\RequestInterface;
8 use Psr\Http\Message\ResponseInterface;
9 use Psr\Http\Message\UriInterface;
10
11 /**
12 * @method ResponseInterface get(string|UriInterface $uri, array $options = [])
13 * @method ResponseInterface head(string|UriInterface $uri, array $options = [])
14 * @method ResponseInterface put(string|UriInterface $uri, array $options = [])
15 * @method ResponseInterface post(string|UriInterface $uri, array $options = [])
16 * @method ResponseInterface patch(string|UriInterface $uri, array $options = [])
17 * @method ResponseInterface delete(string|UriInterface $uri, array $options = [])
18 * @method Promise\PromiseInterface getAsync(string|UriInterface $uri, array $options = [])
19 * @method Promise\PromiseInterface headAsync(string|UriInterface $uri, array $options = [])
20 * @method Promise\PromiseInterface putAsync(string|UriInterface $uri, array $options = [])
21 * @method Promise\PromiseInterface postAsync(string|UriInterface $uri, array $options = [])
22 * @method Promise\PromiseInterface patchAsync(string|UriInterface $uri, array $options = [])
23 * @method Promise\PromiseInterface deleteAsync(string|UriInterface $uri, array $options = [])
24 */
25 class Client implements ClientInterface
26 {
27 /** @var array Default request options */
28 private $config;
29
30 /**
31 * Clients accept an array of constructor parameters.
32 *
33 * Here's an example of creating a client using a base_uri and an array of
34 * default request options to apply to each request:
35 *
36 * $client = new Client([
37 * 'base_uri' => 'http://www.foo.com/1.0/',
38 * 'timeout' => 0,
39 * 'allow_redirects' => false,
40 * 'proxy' => '192.168.16.1:10'
41 * ]);
42 *
43 * Client configuration settings include the following options:
44 *
45 * - handler: (callable) Function that transfers HTTP requests over the
46 * wire. The function is called with a Psr7\Http\Message\RequestInterface
47 * and array of transfer options, and must return a
48 * GuzzleHttp\Promise\PromiseInterface that is fulfilled with a
49 * Psr7\Http\Message\ResponseInterface on success. "handler" is a
50 * constructor only option that cannot be overridden in per/request
51 * options. If no handler is provided, a default handler will be created
52 * that enables all of the request options below by attaching all of the
53 * default middleware to the handler.
54 * - base_uri: (string|UriInterface) Base URI of the client that is merged
55 * into relative URIs. Can be a string or instance of UriInterface.
56 * - **: any request option
57 *
58 * @param array $config Client configuration settings.
59 *
60 * @see \GuzzleHttp\RequestOptions for a list of available request options.
61 */
62 public function __construct(array $config = [])
63 {
64 if (!isset($config['handler'])) {
65 $config['handler'] = HandlerStack::create();
66 } elseif (!is_callable($config['handler'])) {
67 throw new \InvalidArgumentException('handler must be a callable');
68 }
69
70 // Convert the base_uri to a UriInterface
71 if (isset($config['base_uri'])) {
72 $config['base_uri'] = Psr7\uri_for($config['base_uri']);
73 }
74
75 $this->configureDefaults($config);
76 }
77
78 /**
79 * @param string $method
80 * @param array $args
81 *
82 * @return Promise\PromiseInterface
83 */
84 public function __call($method, $args)
85 {
86 if (count($args) < 1) {
87 throw new \InvalidArgumentException('Magic request methods require a URI and optional options array');
88 }
89
90 $uri = $args[0];
91 $opts = isset($args[1]) ? $args[1] : [];
92
93 return substr($method, -5) === 'Async'
94 ? $this->requestAsync(substr($method, 0, -5), $uri, $opts)
95 : $this->request($method, $uri, $opts);
96 }
97
98 /**
99 * Asynchronously send an HTTP request.
100 *
101 * @param array $options Request options to apply to the given
102 * request and to the transfer. See \GuzzleHttp\RequestOptions.
103 *
104 * @return PromiseInterface
105 */
106 public function sendAsync(RequestInterface $request, array $options = [])
107 {
108 // Merge the base URI into the request URI if needed.
109 $options = $this->prepareDefaults($options);
110
111 return $this->transfer(
112 $request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')),
113 $options
114 );
115 }
116
117 /**
118 * Send an HTTP request.
119 *
120 * @param array $options Request options to apply to the given
121 * request and to the transfer. See \GuzzleHttp\RequestOptions.
122 *
123 * @return ResponseInterface
124 * @throws GuzzleException
125 */
126 public function send(RequestInterface $request, array $options = [])
127 {
128 $options[RequestOptions::SYNCHRONOUS] = true;
129 return $this->sendAsync($request, $options)->wait();
130 }
131
132 /**
133 * Create and send an asynchronous HTTP request.
134 *
135 * Use an absolute path to override the base path of the client, or a
136 * relative path to append to the base path of the client. The URL can
137 * contain the query string as well. Use an array to provide a URL
138 * template and additional variables to use in the URL template expansion.
139 *
140 * @param string $method HTTP method
141 * @param string|UriInterface $uri URI object or string.
142 * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions.
143 *
144 * @return PromiseInterface
145 */
146 public function requestAsync($method, $uri = '', array $options = [])
147 {
148 $options = $this->prepareDefaults($options);
149 // Remove request modifying parameter because it can be done up-front.
150 $headers = isset($options['headers']) ? $options['headers'] : [];
151 $body = isset($options['body']) ? $options['body'] : null;
152 $version = isset($options['version']) ? $options['version'] : '1.1';
153 // Merge the URI into the base URI.
154 $uri = $this->buildUri($uri, $options);
155 if (is_array($body)) {
156 $this->invalidBody();
157 }
158 $request = new Psr7\Request($method, $uri, $headers, $body, $version);
159 // Remove the option so that they are not doubly-applied.
160 unset($options['headers'], $options['body'], $options['version']);
161
162 return $this->transfer($request, $options);
163 }
164
165 /**
166 * Create and send an HTTP request.
167 *
168 * Use an absolute path to override the base path of the client, or a
169 * relative path to append to the base path of the client. The URL can
170 * contain the query string as well.
171 *
172 * @param string $method HTTP method.
173 * @param string|UriInterface $uri URI object or string.
174 * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions.
175 *
176 * @return ResponseInterface
177 * @throws GuzzleException
178 */
179 public function request($method, $uri = '', array $options = [])
180 {
181 $options[RequestOptions::SYNCHRONOUS] = true;
182 return $this->requestAsync($method, $uri, $options)->wait();
183 }
184
185 /**
186 * Get a client configuration option.
187 *
188 * These options include default request options of the client, a "handler"
189 * (if utilized by the concrete client), and a "base_uri" if utilized by
190 * the concrete client.
191 *
192 * @param string|null $option The config option to retrieve.
193 *
194 * @return mixed
195 */
196 public function getConfig($option = null)
197 {
198 return $option === null
199 ? $this->config
200 : (isset($this->config[$option]) ? $this->config[$option] : null);
201 }
202
203 /**
204 * @param string|null $uri
205 *
206 * @return UriInterface
207 */
208 private function buildUri($uri, array $config)
209 {
210 // for BC we accept null which would otherwise fail in uri_for
211 $uri = Psr7\uri_for($uri === null ? '' : $uri);
212
213 if (isset($config['base_uri'])) {
214 $uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri);
215 }
216
217 return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;
218 }
219
220 /**
221 * Configures the default options for a client.
222 *
223 * @param array $config
224 * @return void
225 */
226 private function configureDefaults(array $config)
227 {
228 $defaults = [
229 'allow_redirects' => RedirectMiddleware::$defaultSettings,
230 'http_errors' => true,
231 'decode_content' => true,
232 'verify' => true,
233 'cookies' => false
234 ];
235
236 // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
237
238 // We can only trust the HTTP_PROXY environment variable in a CLI
239 // process due to the fact that PHP has no reliable mechanism to
240 // get environment variables that start with "HTTP_".
241 if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {
242 $defaults['proxy']['http'] = getenv('HTTP_PROXY');
243 }
244
245 if ($proxy = getenv('HTTPS_PROXY')) {
246 $defaults['proxy']['https'] = $proxy;
247 }
248
249 if ($noProxy = getenv('NO_PROXY')) {
250 $cleanedNoProxy = str_replace(' ', '', $noProxy);
251 $defaults['proxy']['no'] = explode(',', $cleanedNoProxy);
252 }
253
254 $this->config = $config + $defaults;
255
256 if (!empty($config['cookies']) && $config['cookies'] === true) {
257 $this->config['cookies'] = new CookieJar();
258 }
259
260 // Add the default user-agent header.
261 if (!isset($this->config['headers'])) {
262 $this->config['headers'] = ['User-Agent' => default_user_agent()];
263 } else {
264 // Add the User-Agent header if one was not already set.
265 foreach (array_keys($this->config['headers']) as $name) {
266 if (strtolower($name) === 'user-agent') {
267 return;
268 }
269 }
270 $this->config['headers']['User-Agent'] = default_user_agent();
271 }
272 }
273
274 /**
275 * Merges default options into the array.
276 *
277 * @param array $options Options to modify by reference
278 *
279 * @return array
280 */
281 private function prepareDefaults(array $options)
282 {
283 $defaults = $this->config;
284
285 if (!empty($defaults['headers'])) {
286 // Default headers are only added if they are not present.
287 $defaults['_conditional'] = $defaults['headers'];
288 unset($defaults['headers']);
289 }
290
291 // Special handling for headers is required as they are added as
292 // conditional headers and as headers passed to a request ctor.
293 if (array_key_exists('headers', $options)) {
294 // Allows default headers to be unset.
295 if ($options['headers'] === null) {
296 $defaults['_conditional'] = [];
297 unset($options['headers']);
298 } elseif (!is_array($options['headers'])) {
299 throw new \InvalidArgumentException('headers must be an array');
300 }
301 }
302
303 // Shallow merge defaults underneath options.
304 $result = $options + $defaults;
305
306 // Remove null values.
307 foreach ($result as $k => $v) {
308 if ($v === null) {
309 unset($result[$k]);
310 }
311 }
312
313 return $result;
314 }
315
316 /**
317 * Transfers the given request and applies request options.
318 *
319 * The URI of the request is not modified and the request options are used
320 * as-is without merging in default options.
321 *
322 * @param array $options See \GuzzleHttp\RequestOptions.
323 *
324 * @return Promise\PromiseInterface
325 */
326 private function transfer(RequestInterface $request, array $options)
327 {
328 // save_to -> sink
329 if (isset($options['save_to'])) {
330 $options['sink'] = $options['save_to'];
331 unset($options['save_to']);
332 }
333
334 // exceptions -> http_errors
335 if (isset($options['exceptions'])) {
336 $options['http_errors'] = $options['exceptions'];
337 unset($options['exceptions']);
338 }
339
340 $request = $this->applyOptions($request, $options);
341 /** @var HandlerStack $handler */
342 $handler = $options['handler'];
343
344 try {
345 return Promise\promise_for($handler($request, $options));
346 } catch (\Exception $e) {
347 return Promise\rejection_for($e);
348 }
349 }
350
351 /**
352 * Applies the array of request options to a request.
353 *
354 * @param RequestInterface $request
355 * @param array $options
356 *
357 * @return RequestInterface
358 */
359 private function applyOptions(RequestInterface $request, array &$options)
360 {
361 $modify = [
362 'set_headers' => [],
363 ];
364
365 if (isset($options['headers'])) {
366 $modify['set_headers'] = $options['headers'];
367 unset($options['headers']);
368 }
369
370 if (isset($options['form_params'])) {
371 if (isset($options['multipart'])) {
372 throw new \InvalidArgumentException('You cannot use '
373 . 'form_params and multipart at the same time. Use the '
374 . 'form_params option if you want to send application/'
375 . 'x-www-form-urlencoded requests, and the multipart '
376 . 'option to send multipart/form-data requests.');
377 }
378 $options['body'] = http_build_query($options['form_params'], '', '&');
379 unset($options['form_params']);
380 // Ensure that we don't have the header in different case and set the new value.
381 $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
382 $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
383 }
384
385 if (isset($options['multipart'])) {
386 $options['body'] = new Psr7\MultipartStream($options['multipart']);
387 unset($options['multipart']);
388 }
389
390 if (isset($options['json'])) {
391 $options['body'] = \GuzzleHttp\json_encode($options['json']);
392 unset($options['json']);
393 // Ensure that we don't have the header in different case and set the new value.
394 $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
395 $options['_conditional']['Content-Type'] = 'application/json';
396 }
397
398 if (!empty($options['decode_content'])
399 && $options['decode_content'] !== true
400 ) {
401 // Ensure that we don't have the header in different case and set the new value.
402 $options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $options['_conditional']);
403 $modify['set_headers']['Accept-Encoding'] = $options['decode_content'];
404 }
405
406 if (isset($options['body'])) {
407 if (is_array($options['body'])) {
408 $this->invalidBody();
409 }
410 $modify['body'] = Psr7\stream_for($options['body']);
411 unset($options['body']);
412 }
413
414 if (!empty($options['auth']) && is_array($options['auth'])) {
415 $value = $options['auth'];
416 $type = isset($value[2]) ? strtolower($value[2]) : 'basic';
417 switch ($type) {
418 case 'basic':
419 // Ensure that we don't have the header in different case and set the new value.
420 $modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']);
421 $modify['set_headers']['Authorization'] = 'Basic '
422 . base64_encode("$value[0]:$value[1]");
423 break;
424 case 'digest':
425 // @todo: Do not rely on curl
426 $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
427 $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
428 break;
429 case 'ntlm':
430 $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM;
431 $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
432 break;
433 }
434 }
435
436 if (isset($options['query'])) {
437 $value = $options['query'];
438 if (is_array($value)) {
439 $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);
440 }
441 if (!is_string($value)) {
442 throw new \InvalidArgumentException('query must be a string or array');
443 }
444 $modify['query'] = $value;
445 unset($options['query']);
446 }
447
448 // Ensure that sink is not an invalid value.
449 if (isset($options['sink'])) {
450 // TODO: Add more sink validation?
451 if (is_bool($options['sink'])) {
452 throw new \InvalidArgumentException('sink must not be a boolean');
453 }
454 }
455
456 $request = Psr7\modify_request($request, $modify);
457 if ($request->getBody() instanceof Psr7\MultipartStream) {
458 // Use a multipart/form-data POST if a Content-Type is not set.
459 // Ensure that we don't have the header in different case and set the new value.
460 $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
461 $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
462 . $request->getBody()->getBoundary();
463 }
464
465 // Merge in conditional headers if they are not present.
466 if (isset($options['_conditional'])) {
467 // Build up the changes so it's in a single clone of the message.
468 $modify = [];
469 foreach ($options['_conditional'] as $k => $v) {
470 if (!$request->hasHeader($k)) {
471 $modify['set_headers'][$k] = $v;
472 }
473 }
474 $request = Psr7\modify_request($request, $modify);
475 // Don't pass this internal value along to middleware/handlers.
476 unset($options['_conditional']);
477 }
478
479 return $request;
480 }
481
482 /**
483 * Throw Exception with pre-set message.
484 * @return void
485 * @throws InvalidArgumentException Invalid body.
486 */
487 private function invalidBody()
488 {
489 throw new \InvalidArgumentException('Passing in the "body" request '
490 . 'option as an array to send a POST request has been deprecated. '
491 . 'Please use the "form_params" request option to send a '
492 . 'application/x-www-form-urlencoded request, or the "multipart" '
493 . 'request option to send a multipart/form-data request.');
494 }
495 }
496