PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.4
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.4
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / vendor_prefixed / guzzlehttp / guzzle / src / Client.php

Client.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.4, at vendor_prefixed/guzzlehttp/guzzle/src/Client.php

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