PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.2
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.2
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 28.2, at vendor_prefixed/guzzlehttp/guzzle/src/Client.php

1,075 lines 55.3 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\Cookie\CookieJarInterface;
7 use YoastSEO_Vendor\GuzzleHttp\Exception\GuzzleException;
8 use YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException;
9 use YoastSEO_Vendor\GuzzleHttp\Handler\CurlShareHandleState;
10 use YoastSEO_Vendor\GuzzleHttp\Handler\CurlVersion;
11 use YoastSEO_Vendor\GuzzleHttp\Promise as P;
12 use YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface;
13 use YoastSEO_Vendor\Psr\Http\Message\RequestInterface;
14 use YoastSEO_Vendor\Psr\Http\Message\ResponseInterface;
15 use YoastSEO_Vendor\Psr\Http\Message\StreamInterface;
16 use YoastSEO_Vendor\Psr\Http\Message\UriInterface;
17 /**
18 * @final
19 */
20 class Client implements \YoastSEO_Vendor\GuzzleHttp\ClientInterface, \YoastSEO_Vendor\Psr\Http\Client\ClientInterface
21 {
22 use ClientTrait;
23 /**
24 * @var array Default request options
25 */
26 private $config;
27 /**
28 * Clients accept an array of constructor parameters.
29 *
30 * Here's an example of creating a client using a base_uri and an array of
31 * default request options to apply to each request:
32 *
33 * $client = new Client([
34 * 'base_uri' => 'http://www.foo.com/1.0/',
35 * 'timeout' => 0,
36 * 'allow_redirects' => false,
37 * 'proxy' => '192.168.16.1:10'
38 * ]);
39 *
40 * Client configuration settings include the following options:
41 *
42 * - handler: (callable) Function that transfers HTTP requests over the
43 * wire. The function is called with a Psr7\Http\Message\RequestInterface
44 * and array of transfer options, and must return a
45 * GuzzleHttp\Promise\PromiseInterface that is fulfilled with a
46 * Psr7\Http\Message\ResponseInterface on success.
47 * If no handler is provided, a default handler will be created
48 * that enables all of the request options below by attaching all of the
49 * default middleware to the handler.
50 * - base_uri: (string|UriInterface) Base URI of the client that is merged
51 * into relative URIs. Can be a string or instance of UriInterface.
52 * - transport_sharing: (string|null) Transport sharing mode for the
53 * default handler. Accepts TransportSharing::* or null. Defaults to null.
54 * - max_host_connections: (int|null) Maximum concurrent connections per
55 * host, applied by the default CurlMultiHandler. The default stream
56 * fallback receives the cap as a marker only: it rejects enabled
57 * response streaming ("stream" => true) and does not limit overlapping
58 * buffered calls.
59 * - max_total_connections: (int|null) Maximum concurrent connections
60 * overall, applied by the default CurlMultiHandler. The default stream
61 * fallback receives the cap as a marker only: it rejects enabled
62 * response streaming ("stream" => true) and does not limit overlapping
63 * buffered calls.
64 * - multiplex: (string|null) Multiplexing::NONE to disable multiplexing on
65 * the default CurlMultiHandler; the value also becomes the default
66 * "multiplex" request option. Other Multiplexing::* values act as the
67 * default request option only.
68 * - **: any request option
69 *
70 * @param array $config Client configuration settings.
71 *
72 * @see RequestOptions for a list of available request options.
73 */
74 public function __construct(array $config = [])
75 {
76 $handlerOptions = [];
77 foreach (['max_host_connections', 'max_total_connections'] as $capOption) {
78 if (\array_key_exists($capOption, $config)) {
79 if ($config[$capOption] !== null) {
80 $handlerOptions[$capOption] = $config[$capOption];
81 }
82 unset($config[$capOption]);
83 }
84 }
85 // Deliberately not unset: the value also becomes the default
86 // "multiplex" request option, which the configured handler accepts.
87 $handlerMultiplex = ($config['multiplex'] ?? null) === \YoastSEO_Vendor\GuzzleHttp\Multiplexing::NONE;
88 $transportSharing = \array_key_exists('transport_sharing', $config) ? $config['transport_sharing'] : null;
89 $transportSharingMode = \YoastSEO_Vendor\GuzzleHttp\Handler\CurlShareHandleState::normalizeMode($transportSharing, 'transport_sharing');
90 unset($config['transport_sharing']);
91 if (!isset($config['handler'])) {
92 if ($transportSharingMode !== \YoastSEO_Vendor\GuzzleHttp\TransportSharing::NONE) {
93 $handlerOptions['transport_sharing'] = $transportSharingMode;
94 }
95 if ($handlerMultiplex) {
96 $handlerOptions['multiplex'] = \YoastSEO_Vendor\GuzzleHttp\Multiplexing::NONE;
97 }
98 $config['handler'] = $handlerOptions === [] ? \YoastSEO_Vendor\GuzzleHttp\HandlerStack::create() : \YoastSEO_Vendor\GuzzleHttp\HandlerStack::create(\YoastSEO_Vendor\GuzzleHttp\Utils::chooseHandler($handlerOptions));
99 } elseif (!\is_callable($config['handler'])) {
100 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('handler must be a callable');
101 } elseif ($handlerOptions !== []) {
102 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('The "max_host_connections" and "max_total_connections" client options require Guzzle to create the default handler. Configure the options on the CurlMultiHandler constructor to apply numeric connection caps, or on the StreamHandler constructor to reject enabled response streaming, when providing a custom handler.');
103 } elseif ($transportSharingMode === \YoastSEO_Vendor\GuzzleHttp\TransportSharing::HANDLER_REQUIRE) {
104 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('The "transport_sharing" client option can only require sharing when Guzzle creates the default handler. Configure the "transport_sharing" option on CurlHandler or CurlMultiHandler when providing a custom cURL handler.');
105 }
106 // Convert the base_uri to a UriInterface
107 if (isset($config['base_uri'])) {
108 $config['base_uri'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::uriFor($config['base_uri']);
109 }
110 $this->configureDefaults($config);
111 }
112 /**
113 * @param string $method
114 * @param array $args
115 *
116 * @return PromiseInterface|ResponseInterface
117 *
118 * @deprecated Client::__call will be removed in guzzlehttp/guzzle:8.0.
119 */
120 public function __call($method, $args)
121 {
122 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s::%s() is deprecated and will be removed in 8.0.', __CLASS__, __FUNCTION__);
123 if (\count($args) < 1) {
124 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('Magic request methods require a URI and optional options array');
125 }
126 $uri = $args[0];
127 $opts = $args[1] ?? [];
128 $isAsync = \substr($method, -5) === 'Async';
129 $method = $isAsync ? \substr($method, 0, -5) : $method;
130 $method = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::asciiToUpper($method);
131 return $isAsync ? $this->requestAsync($method, $uri, $opts) : $this->request($method, $uri, $opts);
132 }
133 /**
134 * Asynchronously send an HTTP request.
135 *
136 * @param array $options Request options to apply to the given
137 * request and to the transfer. See {@see RequestOptions}.
138 */
139 public function sendAsync(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface
140 {
141 // Merge the base URI into the request URI if needed.
142 $options = $this->prepareDefaults($options);
143 return $this->transfer($request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')), $options);
144 }
145 /**
146 * Send an HTTP request.
147 *
148 * @param array $options Request options to apply to the given
149 * request and to the transfer. See {@see RequestOptions}.
150 *
151 * @throws GuzzleException
152 */
153 public function send(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface
154 {
155 $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::SYNCHRONOUS] = \true;
156 return $this->sendAsync($request, $options)->wait();
157 }
158 /**
159 * The HttpClient PSR (PSR-18) specify this method.
160 *
161 * {@inheritDoc}
162 */
163 public function sendRequest(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface
164 {
165 $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::SYNCHRONOUS] = \true;
166 $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::ALLOW_REDIRECTS] = \false;
167 $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::HTTP_ERRORS] = \false;
168 return $this->sendAsync($request, $options)->wait();
169 }
170 /**
171 * Create and send an asynchronous HTTP request.
172 *
173 * Use an absolute path to override the base path of the client, or a
174 * relative path to append to the base path of the client. The URL can
175 * contain the query string as well. Use an array to provide a URL
176 * template and additional variables to use in the URL template expansion.
177 *
178 * @param string $method HTTP method
179 * @param string|UriInterface $uri URI object or string.
180 * @param array $options Request options to apply. See {@see RequestOptions}.
181 */
182 public function requestAsync(string $method, $uri = '', array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface
183 {
184 $normalizedMethod = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::asciiToUpper($method);
185 if ($method !== $normalizedMethod) {
186 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Passing a non-uppercase HTTP method to Client::requestAsync() is deprecated; guzzlehttp/guzzle 8.0 will preserve HTTP method casing. Pass an uppercase method explicitly if uppercase is required.');
187 $method = $normalizedMethod;
188 }
189 $options = $this->prepareDefaults($options);
190 // Remove request modifying parameter because it can be done up-front.
191 $headers = $options['headers'] ?? [];
192 $droppedHeaderNames = self::castDeprecatedHeaderOptionValues($headers);
193 if ($droppedHeaderNames !== [] && isset($options['_conditional'])) {
194 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove($droppedHeaderNames, $options['_conditional']);
195 }
196 $body = $options['body'] ?? null;
197 $version = self::normalizeProtocolVersion($options['version'] ?? '1.1');
198 // Merge the URI into the base URI.
199 $uri = $this->buildUri(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::uriFor($uri), $options);
200 if (\is_array($body)) {
201 throw $this->invalidBody();
202 }
203 $body = self::createBodyStream($body);
204 $request = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Request($method, $uri, $headers, $body, $version);
205 // Remove the option so that they are not doubly-applied.
206 unset($options['headers'], $options['body'], $options['version']);
207 return $this->transfer($request, $options);
208 }
209 /**
210 * Create and send an HTTP request.
211 *
212 * Use an absolute path to override the base path of the client, or a
213 * relative path to append to the base path of the client. The URL can
214 * contain the query string as well.
215 *
216 * @param string $method HTTP method.
217 * @param string|UriInterface $uri URI object or string.
218 * @param array $options Request options to apply. See {@see RequestOptions}.
219 *
220 * @throws GuzzleException
221 */
222 public function request(string $method, $uri = '', array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface
223 {
224 $normalizedMethod = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::asciiToUpper($method);
225 if ($method !== $normalizedMethod) {
226 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Passing a non-uppercase HTTP method to Client::request() is deprecated; guzzlehttp/guzzle 8.0 will preserve HTTP method casing. Pass an uppercase method explicitly if uppercase is required.');
227 $method = $normalizedMethod;
228 }
229 $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::SYNCHRONOUS] = \true;
230 return $this->requestAsync($method, $uri, $options)->wait();
231 }
232 /**
233 * Get a client configuration option.
234 *
235 * These options include default request options of the client, a "handler"
236 * (if utilized by the concrete client), and a "base_uri" if utilized by
237 * the concrete client.
238 *
239 * @param string|null $option The config option to retrieve.
240 *
241 * @return mixed
242 */
243 public function getConfig(?string $option = null)
244 {
245 return $option === null ? $this->config : $this->config[$option] ?? null;
246 }
247 private function buildUri(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, array $config) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
248 {
249 if (isset($config['base_uri'])) {
250 $uri = \YoastSEO_Vendor\GuzzleHttp\Psr7\UriResolver::resolve(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::uriFor($config['base_uri']), $uri);
251 }
252 $idnOptions = \YoastSEO_Vendor\GuzzleHttp\Utils::normalizeIdnConversionOption($config['idn_conversion'] ?? null);
253 if ($idnOptions !== null) {
254 $uri = \YoastSEO_Vendor\GuzzleHttp\Utils::idnUriConvert($uri, $idnOptions);
255 }
256 if ($uri->getScheme() === '' && $uri->getHost() !== '') {
257 $uri = $uri->withScheme('http');
258 }
259 return $uri;
260 }
261 /**
262 * Configures the default options for a client.
263 */
264 private function configureDefaults(array $config) : void
265 {
266 $defaults = ['allow_redirects' => \YoastSEO_Vendor\GuzzleHttp\RedirectMiddleware::$defaultSettings, 'http_errors' => \true, 'decode_content' => \true, 'verify' => \true, 'cookies' => \false, 'idn_conversion' => \false, 'protocols' => ['http', 'https']];
267 // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
268 // We can only trust the HTTP_PROXY environment variable in a CLI
269 // process due to the fact that PHP has no reliable mechanism to
270 // get environment variables that start with "HTTP_".
271 if (\PHP_SAPI === 'cli' && ($proxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('HTTP_PROXY'))) {
272 $defaults['proxy']['http'] = $proxy;
273 }
274 if ($proxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('HTTPS_PROXY')) {
275 $defaults['proxy']['https'] = $proxy;
276 }
277 if ($noProxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('NO_PROXY')) {
278 $cleanedNoProxy = \str_replace(' ', '', $noProxy);
279 $defaults['proxy']['no'] = \explode(',', $cleanedNoProxy);
280 }
281 $this->config = $config + $defaults;
282 if (!empty($config['cookies']) && $config['cookies'] === \true) {
283 $this->config['cookies'] = new \YoastSEO_Vendor\GuzzleHttp\Cookie\CookieJar();
284 }
285 // Add the default user-agent header.
286 if (!isset($this->config['headers'])) {
287 $this->config['headers'] = ['User-Agent' => \YoastSEO_Vendor\GuzzleHttp\Utils::defaultUserAgent()];
288 } else {
289 // Add the User-Agent header if one was not already set.
290 $hasUserAgent = \false;
291 foreach (\array_keys($this->config['headers']) as $name) {
292 if (\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::asciiToLower((string) $name) === 'user-agent') {
293 $hasUserAgent = \true;
294 break;
295 }
296 }
297 if (!$hasUserAgent) {
298 $this->config['headers']['User-Agent'] = \YoastSEO_Vendor\GuzzleHttp\Utils::defaultUserAgent();
299 }
300 }
301 if (\is_array($this->config['headers'])) {
302 self::warnAboutInvalidHeaderOptionTypes($this->config['headers']);
303 self::castDeprecatedHeaderOptionValues($this->config['headers']);
304 }
305 }
306 /**
307 * Merges default options into the array.
308 *
309 * @param array $options Options to modify by reference
310 */
311 private function prepareDefaults(array $options) : array
312 {
313 self::warnAboutRequestLevelHandler($options);
314 $defaults = $this->config;
315 if (!empty($defaults['headers'])) {
316 // Default headers are only added if they are not present.
317 $defaults['_conditional'] = $defaults['headers'];
318 unset($defaults['headers']);
319 }
320 // Special handling for headers is required as they are added as
321 // conditional headers and as headers passed to a request ctor.
322 if (\array_key_exists('headers', $options)) {
323 // Allows default headers to be unset.
324 if ($options['headers'] === null) {
325 $defaults['_conditional'] = [];
326 unset($options['headers']);
327 } elseif (!\is_array($options['headers'])) {
328 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('headers must be an array');
329 }
330 }
331 // Shallow merge defaults underneath options.
332 $result = $options + $defaults;
333 // Remove null values.
334 foreach ($result as $k => $v) {
335 if ($v === null) {
336 unset($result[$k]);
337 }
338 }
339 self::warnAboutInvalidRequestOptionTypes($result);
340 return self::normalizeDeprecatedRequestOptionValues($result);
341 }
342 /**
343 * Normalize values that guzzlehttp/guzzle 8.0 rejects only after the
344 * corresponding 7.x deprecation has already been emitted.
345 *
346 * @param array<string, mixed> $options
347 *
348 * @return array<string, mixed>
349 */
350 private static function normalizeDeprecatedRequestOptionValues(array $options) : array
351 {
352 self::normalizeDeprecatedAuthOptionValues($options);
353 self::normalizeDeprecatedTlsFileOptionValues($options, 'cert');
354 self::normalizeDeprecatedTlsFileOptionValues($options, 'ssl_key');
355 self::normalizeDeprecatedStringOptionValues($options);
356 self::normalizeDeprecatedNumericOptionValues($options);
357 self::normalizeDeprecatedIntegerOptionValues($options);
358 return $options;
359 }
360 /**
361 * @param mixed $value
362 */
363 private static function canStringifyDeprecatedValue($value) : bool
364 {
365 return $value === null || \is_scalar($value) || \is_object($value) && \method_exists($value, '__toString');
366 }
367 /**
368 * @param mixed $value
369 */
370 private static function stringifyDeprecatedValue($value) : string
371 {
372 if (\is_float($value) && !\is_finite($value)) {
373 return \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
374 }
375 if ($value === null) {
376 return '';
377 }
378 if (\is_scalar($value)) {
379 return (string) $value;
380 }
381 if (\is_object($value) && \method_exists($value, '__toString')) {
382 return $value->__toString();
383 }
384 throw new \LogicException('Value is not stringable.');
385 }
386 /**
387 * @param array<string, mixed> $options
388 */
389 private static function normalizeDeprecatedAuthOptionValues(array &$options) : void
390 {
391 if (!isset($options['auth']) || !\is_array($options['auth']) || $options['auth'] === []) {
392 return;
393 }
394 foreach ([0, 1] as $index) {
395 if (\array_key_exists($index, $options['auth']) && !\is_string($options['auth'][$index]) && self::canStringifyDeprecatedValue($options['auth'][$index])) {
396 $options['auth'][$index] = self::stringifyDeprecatedValue($options['auth'][$index]);
397 }
398 }
399 if (\array_key_exists(2, $options['auth']) && $options['auth'][2] !== null && !\is_string($options['auth'][2]) && self::canStringifyDeprecatedValue($options['auth'][2])) {
400 $options['auth'][2] = self::stringifyDeprecatedValue($options['auth'][2]);
401 }
402 }
403 /**
404 * @param array<string, mixed> $options
405 */
406 private static function normalizeDeprecatedTlsFileOptionValues(array &$options, string $option) : void
407 {
408 if (!isset($options[$option]) || !\is_array($options[$option])) {
409 return;
410 }
411 foreach ([0, 1] as $index) {
412 if (\array_key_exists($index, $options[$option]) && $options[$option][$index] !== null && !\is_string($options[$option][$index]) && self::canStringifyDeprecatedValue($options[$option][$index])) {
413 $options[$option][$index] = self::stringifyDeprecatedValue($options[$option][$index]);
414 }
415 }
416 }
417 /**
418 * @param array<string, mixed> $options
419 */
420 private static function normalizeDeprecatedStringOptionValues(array &$options) : void
421 {
422 foreach (['cert_type', 'force_ip_resolve', 'ssl_key_type'] as $option) {
423 if (\array_key_exists($option, $options) && !\is_string($options[$option]) && self::canStringifyDeprecatedValue($options[$option])) {
424 $options[$option] = self::stringifyDeprecatedValue($options[$option]);
425 }
426 }
427 }
428 /**
429 * @param array<string, mixed> $options
430 */
431 private static function normalizeDeprecatedNumericOptionValues(array &$options) : void
432 {
433 foreach (['connect_timeout', 'delay', 'read_timeout', 'timeout'] as $option) {
434 if (\array_key_exists($option, $options) && \is_string($options[$option]) && \is_numeric($options[$option])) {
435 $options[$option] = $options[$option] + 0;
436 }
437 }
438 }
439 /**
440 * @param array<string, mixed> $options
441 */
442 private static function normalizeDeprecatedIntegerOptionValues(array &$options) : void
443 {
444 foreach (['crypto_method', 'crypto_method_max', 'retries'] as $option) {
445 if (!\array_key_exists($option, $options)) {
446 continue;
447 }
448 if (\is_string($options[$option]) && \preg_match('/^-?\\d+$/D', $options[$option]) === 1) {
449 $options[$option] = (int) $options[$option];
450 } elseif (\is_float($options[$option]) && \is_finite($options[$option]) && $options[$option] === (float) (int) $options[$option]) {
451 $options[$option] = (int) $options[$option];
452 }
453 }
454 }
455 private static function warnAboutRequestLevelHandler(array $options) : void
456 {
457 if (!\array_key_exists('handler', $options)) {
458 return;
459 }
460 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.12', 'Passing the "handler" request option is deprecated; guzzlehttp/guzzle 8.0 will ignore request-level handlers. Configure the handler when creating the Client, or use a separate Client instance for requests that need a different handler.');
461 }
462 private static function warnAboutInvalidRequestOptionTypes(array $options) : void
463 {
464 if (isset($options['handler']) && !\is_callable($options['handler'])) {
465 self::warnInvalidRequestOptionType('handler', 'callable', $options['handler']);
466 }
467 if (isset($options['allow_redirects']) && \is_array($options['allow_redirects'])) {
468 self::warnAboutInvalidAllowRedirectsOptionTypes($options['allow_redirects']);
469 } elseif (isset($options['allow_redirects']) && !\is_bool($options['allow_redirects'])) {
470 self::warnInvalidRequestOptionType('allow_redirects', 'bool|array', $options['allow_redirects'], '7.13');
471 }
472 if (isset($options['auth'])) {
473 self::warnAboutInvalidAuthOptionTypes($options['auth']);
474 }
475 if (isset($options['body']) && \is_array($options['body'])) {
476 self::warnInvalidRequestOptionType('body', 'resource|string|null|int|float|bool|StreamInterface|(callable&object)|\\Iterator|\\Stringable', $options['body']);
477 }
478 self::warnAboutInvalidTlsFileOptionTypes($options, 'cert');
479 self::warnIfPresentAndNotString($options, 'cert_type');
480 self::warnIfPresentAndNotNumber($options, 'connect_timeout');
481 self::warnIfPresentAndNotInt($options, 'crypto_method');
482 self::warnIfPresentAndNotInt($options, 'crypto_method_max', null, '7.13');
483 self::warnIfPresentAndNotBoolOrResource($options, 'debug');
484 self::warnIfPresentAndNotBoolOrString($options, 'decode_content');
485 self::warnIfPresentAndNotNumber($options, 'delay');
486 if (isset($options['delay']) && \is_numeric($options['delay'])) {
487 $delay = (float) $options['delay'];
488 if (!\is_finite($delay) || $delay < 0.0) {
489 self::warnInvalidRequestOptionType('delay', 'finite int|float greater than or equal to 0', $options['delay'], '7.13');
490 }
491 }
492 self::warnIfPresentAndNotBoolOrInt($options, 'expect');
493 if (isset($options['form_params'])) {
494 self::warnAboutInvalidFormParamTypes($options['form_params']);
495 }
496 if (isset($options['force_ip_resolve']) && !\is_string($options['force_ip_resolve'])) {
497 self::warnInvalidRequestOptionType('force_ip_resolve', 'string', $options['force_ip_resolve']);
498 }
499 if (isset($options['force_ip_resolve']) && \is_string($options['force_ip_resolve']) && $options['force_ip_resolve'] !== 'v4' && $options['force_ip_resolve'] !== 'v6') {
500 self::warnInvalidRequestOptionType('force_ip_resolve', '"v4"|"v6"', $options['force_ip_resolve'], '7.13');
501 }
502 if (isset($options['headers'])) {
503 self::warnAboutInvalidHeaderOptionTypes($options['headers']);
504 }
505 self::warnIfPresentAndNotBool($options, 'http_errors');
506 if (isset($options['multipart'])) {
507 self::warnAboutInvalidMultipartOptionTypes($options['multipart']);
508 }
509 self::warnIfPresentAndNotCallable($options, 'on_headers');
510 self::warnIfPresentAndNotCallable($options, 'on_stats');
511 self::warnIfPresentAndNotCallable($options, 'on_trailers', null, '7.14');
512 self::warnIfPresentAndNotCallable($options, 'progress');
513 self::warnIfPresentAndNotStringArray($options, 'protocols', \true);
514 self::warnAboutInvalidProtocolValues($options, 'protocols');
515 self::warnAboutInvalidProxyOptionTypes($options);
516 self::warnIfPresentAndNotNumber($options, 'read_timeout');
517 self::warnIfPresentAndNotInt($options, 'retries');
518 if (isset($options['sink']) && !\is_bool($options['sink']) && !\is_resource($options['sink']) && !\is_string($options['sink']) && !$options['sink'] instanceof \YoastSEO_Vendor\Psr\Http\Message\StreamInterface) {
519 self::warnInvalidRequestOptionType('sink', 'resource|string|StreamInterface', $options['sink']);
520 }
521 self::warnAboutInvalidTlsFileOptionTypes($options, 'ssl_key');
522 self::warnIfPresentAndNotString($options, 'ssl_key_type');
523 self::warnIfPresentAndNotBool($options, 'stream');
524 self::warnIfPresentAndNotArray($options, 'stream_context', 'array<array-key, mixed>');
525 self::warnIfPresentAndNotBool($options, 'synchronous');
526 self::warnIfPresentAndNotNumber($options, 'timeout');
527 self::warnIfPresentAndNotBoolOrString($options, 'verify');
528 self::warnIfPresentAndNotStringOrNumber($options, 'version');
529 self::warnIfPresentAndNotArray($options, 'curl', 'array<int|string, mixed>');
530 if (isset($options['cookies']) && $options['cookies'] === \true) {
531 self::warnInvalidRequestOptionType('cookies', 'false|CookieJarInterface', $options['cookies']);
532 }
533 if (isset($options['cookies']) && $options['cookies'] !== \false && $options['cookies'] !== \true && !$options['cookies'] instanceof \YoastSEO_Vendor\GuzzleHttp\Cookie\CookieJarInterface) {
534 self::warnInvalidRequestOptionType('cookies', 'false|CookieJarInterface', $options['cookies'], '7.13');
535 }
536 }
537 private static function warnAboutInvalidAllowRedirectsOptionTypes(array $allowRedirects) : void
538 {
539 self::warnIfPresentAndNotInt($allowRedirects, 'max', 'allow_redirects.max');
540 self::warnIfPresentAndNotBool($allowRedirects, 'strict', 'allow_redirects.strict');
541 self::warnIfPresentAndNotBool($allowRedirects, 'referer', 'allow_redirects.referer');
542 self::warnIfPresentAndNotStringArray($allowRedirects, 'protocols', \true, 'allow_redirects.protocols');
543 self::warnAboutInvalidProtocolValues($allowRedirects, 'protocols', 'allow_redirects.protocols');
544 self::warnIfPresentAndNotCallable($allowRedirects, 'on_redirect', 'allow_redirects.on_redirect');
545 self::warnIfPresentAndNotBool($allowRedirects, 'track_redirects', 'allow_redirects.track_redirects');
546 }
547 /**
548 * @param mixed $auth
549 */
550 private static function warnAboutInvalidAuthOptionTypes($auth) : void
551 {
552 if ($auth === \false || \is_string($auth) || $auth === []) {
553 return;
554 }
555 if (!\is_array($auth)) {
556 self::warnInvalidRequestOptionType('auth', 'array{0: string, 1: string, 2?: string|null}|string|false|null', $auth);
557 return;
558 }
559 if (!\array_key_exists(0, $auth) || !\is_string($auth[0])) {
560 self::warnInvalidRequestOptionType('auth.0', 'string', $auth[0] ?? null);
561 }
562 if (!\array_key_exists(1, $auth) || !\is_string($auth[1])) {
563 self::warnInvalidRequestOptionType('auth.1', 'string', $auth[1] ?? null);
564 }
565 if (\array_key_exists(2, $auth) && $auth[2] !== null && !\is_string($auth[2])) {
566 self::warnInvalidRequestOptionType('auth.2', 'string|null', $auth[2]);
567 }
568 }
569 /**
570 * @param mixed $value
571 */
572 private static function warnAboutInvalidFormParamTypes($value) : void
573 {
574 if (!\is_array($value)) {
575 self::warnInvalidRequestOptionType('form_params', 'array<array-key, string|int|float|bool|null|array>', $value);
576 return;
577 }
578 self::warnAboutInvalidFormParamArray($value, 'form_params');
579 }
580 private static function warnAboutInvalidFormParamArray(array $values, string $path) : bool
581 {
582 foreach ($values as $key => $item) {
583 $itemPath = $path . '.' . (string) $key;
584 if (\is_array($item)) {
585 if (!self::warnAboutInvalidFormParamArray($item, $itemPath)) {
586 return \false;
587 }
588 continue;
589 }
590 if ($item !== null && !\is_scalar($item)) {
591 self::warnInvalidRequestOptionType($itemPath, 'string|int|float|bool|null|array', $item);
592 return \false;
593 }
594 }
595 return \true;
596 }
597 /**
598 * @param mixed $headers
599 */
600 private static function warnAboutInvalidHeaderOptionTypes($headers) : void
601 {
602 if (!\is_array($headers)) {
603 self::warnInvalidRequestOptionType('headers', 'array<array-key, string|non-empty-array<array-key, string>>|null', $headers);
604 return;
605 }
606 foreach ($headers as $name => $value) {
607 $path = 'headers.' . (string) $name;
608 if (\is_array($value)) {
609 if ($value === []) {
610 self::warnInvalidRequestOptionType($path, 'string|non-empty-array<array-key, string>', $value);
611 break;
612 }
613 foreach ($value as $index => $item) {
614 if (!\is_string($item)) {
615 self::warnInvalidRequestOptionType($path . '.' . (string) $index, 'string', $item);
616 break 2;
617 }
618 }
619 } elseif (!\is_string($value)) {
620 self::warnInvalidRequestOptionType($path, 'string|non-empty-array<array-key, string>', $value);
621 break;
622 }
623 }
624 }
625 /**
626 * @param mixed $multipart
627 */
628 private static function warnAboutInvalidMultipartOptionTypes($multipart) : void
629 {
630 if (!\is_array($multipart)) {
631 self::warnInvalidRequestOptionType('multipart', 'array<array-key, array{name: string|int, contents: mixed, headers?: array<array-key, string>, filename?: string}>', $multipart);
632 return;
633 }
634 foreach ($multipart as $index => $part) {
635 $path = 'multipart.' . (string) $index;
636 if (!\is_array($part)) {
637 self::warnInvalidRequestOptionType($path, 'array{name: string|int, contents: mixed, headers?: array<array-key, string>, filename?: string}', $part);
638 return;
639 }
640 if (!\array_key_exists('name', $part) || !\is_string($part['name']) && !\is_int($part['name'])) {
641 self::warnInvalidRequestOptionType($path . '.name', 'string|int', $part['name'] ?? null);
642 }
643 if (!\array_key_exists('contents', $part)) {
644 self::warnInvalidRequestOptionType($path, 'array{name: string|int, contents: mixed, headers?: array<array-key, string>, filename?: string}', $part);
645 }
646 if (\array_key_exists('headers', $part)) {
647 if (!\is_array($part['headers'])) {
648 self::warnInvalidRequestOptionType($path . '.headers', 'array<array-key, string>', $part['headers']);
649 } else {
650 foreach ($part['headers'] as $name => $value) {
651 if (!\is_string($value)) {
652 self::warnInvalidRequestOptionType($path . '.headers.' . (string) $name, 'string', $value);
653 break 2;
654 }
655 }
656 }
657 }
658 if (\array_key_exists('filename', $part) && !\is_string($part['filename'])) {
659 self::warnInvalidRequestOptionType($path . '.filename', 'string', $part['filename']);
660 }
661 }
662 }
663 private static function warnAboutInvalidProxyOptionTypes(array $options) : void
664 {
665 if (!isset($options['proxy'])) {
666 return;
667 }
668 if (!\is_string($options['proxy']) && !\is_array($options['proxy'])) {
669 self::warnInvalidRequestOptionType('proxy', 'string|array{http?: string|null, https?: string|null, no?: string|array<array-key, string>|null}', $options['proxy']);
670 return;
671 }
672 if (!\is_array($options['proxy'])) {
673 return;
674 }
675 foreach (['http', 'https'] as $scheme) {
676 if (\array_key_exists($scheme, $options['proxy']) && $options['proxy'][$scheme] !== null && !\is_string($options['proxy'][$scheme])) {
677 self::warnInvalidRequestOptionType('proxy.' . $scheme, 'string|null', $options['proxy'][$scheme]);
678 }
679 }
680 if (!\array_key_exists('no', $options['proxy']) || $options['proxy']['no'] === null) {
681 return;
682 }
683 if (\is_string($options['proxy']['no'])) {
684 return;
685 }
686 if (!\is_array($options['proxy']['no'])) {
687 self::warnInvalidRequestOptionType('proxy.no', 'string|array<array-key, string>|null', $options['proxy']['no']);
688 return;
689 }
690 foreach ($options['proxy']['no'] as $index => $noProxy) {
691 if (!\is_string($noProxy)) {
692 self::warnInvalidRequestOptionType('proxy.no.' . (string) $index, 'string', $noProxy);
693 return;
694 }
695 }
696 }
697 private static function warnAboutInvalidTlsFileOptionTypes(array $options, string $option) : void
698 {
699 if (!isset($options[$option])) {
700 return;
701 }
702 if (\is_string($options[$option])) {
703 return;
704 }
705 if (!\is_array($options[$option])) {
706 self::warnInvalidRequestOptionType($option, 'string|array{0: string, 1?: string}', $options[$option]);
707 return;
708 }
709 if (!\array_key_exists(0, $options[$option]) || !\is_string($options[$option][0])) {
710 self::warnInvalidRequestOptionType($option . '.0', 'string', $options[$option][0] ?? null);
711 }
712 if (\array_key_exists(1, $options[$option]) && $options[$option][1] !== null && !\is_string($options[$option][1])) {
713 self::warnInvalidRequestOptionType($option . '.1', 'string|null', $options[$option][1]);
714 }
715 }
716 private static function warnIfPresentAndNotArray(array $options, string $option, string $expected) : void
717 {
718 if (\array_key_exists($option, $options) && !\is_array($options[$option])) {
719 self::warnInvalidRequestOptionType($option, $expected, $options[$option]);
720 }
721 }
722 private static function warnIfPresentAndNotBool(array $options, string $option, ?string $path = null) : void
723 {
724 if (\array_key_exists($option, $options) && !\is_bool($options[$option])) {
725 self::warnInvalidRequestOptionType($path ?? $option, 'bool', $options[$option]);
726 }
727 }
728 private static function warnIfPresentAndNotBoolOrInt(array $options, string $option) : void
729 {
730 if (\array_key_exists($option, $options) && !\is_bool($options[$option]) && !\is_int($options[$option])) {
731 self::warnInvalidRequestOptionType($option, 'bool|int', $options[$option]);
732 }
733 }
734 private static function warnIfPresentAndNotBoolOrResource(array $options, string $option) : void
735 {
736 if (\array_key_exists($option, $options) && !\is_bool($options[$option]) && !\is_resource($options[$option])) {
737 self::warnInvalidRequestOptionType($option, 'bool|resource', $options[$option]);
738 }
739 }
740 private static function warnIfPresentAndNotBoolOrString(array $options, string $option) : void
741 {
742 if (\array_key_exists($option, $options) && !\is_bool($options[$option]) && !\is_string($options[$option])) {
743 self::warnInvalidRequestOptionType($option, 'bool|string', $options[$option]);
744 }
745 }
746 private static function warnIfPresentAndNotCallable(array $options, string $option, ?string $path = null, string $since = '7.11') : void
747 {
748 if (\array_key_exists($option, $options) && !\is_callable($options[$option])) {
749 self::warnInvalidRequestOptionType($path ?? $option, 'callable', $options[$option], $since);
750 }
751 }
752 private static function warnIfPresentAndNotInt(array $options, string $option, ?string $path = null, string $since = '7.11') : void
753 {
754 if (\array_key_exists($option, $options) && !\is_int($options[$option])) {
755 self::warnInvalidRequestOptionType($path ?? $option, 'int', $options[$option], $since);
756 }
757 }
758 private static function warnIfPresentAndNotNumber(array $options, string $option) : void
759 {
760 if (\array_key_exists($option, $options) && !\is_int($options[$option]) && !\is_float($options[$option])) {
761 self::warnInvalidRequestOptionType($option, 'int|float', $options[$option]);
762 }
763 }
764 private static function warnIfPresentAndNotString(array $options, string $option) : void
765 {
766 if (\array_key_exists($option, $options) && !\is_string($options[$option])) {
767 self::warnInvalidRequestOptionType($option, 'string', $options[$option]);
768 }
769 }
770 private static function warnIfPresentAndNotStringArray(array $options, string $option, bool $nonEmpty, ?string $path = null) : void
771 {
772 if (!\array_key_exists($option, $options)) {
773 return;
774 }
775 $path = $path ?? $option;
776 $expected = ($nonEmpty ? 'non-empty-' : '') . 'array<array-key, string>';
777 if (!\is_array($options[$option]) || $nonEmpty && $options[$option] === []) {
778 self::warnInvalidRequestOptionType($path, $expected, $options[$option]);
779 return;
780 }
781 foreach ($options[$option] as $index => $item) {
782 if (!\is_string($item)) {
783 self::warnInvalidRequestOptionType($path . '.' . (string) $index, 'string', $item);
784 return;
785 }
786 }
787 }
788 /**
789 * @param array<array-key, mixed> $options
790 */
791 private static function warnAboutInvalidProtocolValues(array $options, string $option, ?string $path = null) : void
792 {
793 if (!isset($options[$option]) || !\is_array($options[$option])) {
794 return;
795 }
796 $path = $path ?? $option;
797 foreach ($options[$option] as $index => $protocol) {
798 if (\is_string($protocol) && $protocol !== 'http' && $protocol !== 'https') {
799 self::warnInvalidRequestOptionType($path . '.' . (string) $index, '"http"|"https"', $protocol, '7.13');
800 }
801 }
802 }
803 private static function warnIfPresentAndNotStringOrNumber(array $options, string $option) : void
804 {
805 if (\array_key_exists($option, $options) && !\is_string($options[$option]) && !\is_int($options[$option]) && !\is_float($options[$option])) {
806 self::warnInvalidRequestOptionType($option, 'string|int|float', $options[$option]);
807 }
808 }
809 /**
810 * @param mixed $value
811 */
812 private static function warnInvalidRequestOptionType(string $option, string $expected, $value, string $since = '7.11') : void
813 {
814 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', $since, 'Passing %s to request option "%s" is deprecated; guzzlehttp/guzzle 8.0 requires %s.', \get_debug_type($value), $option, $expected);
815 }
816 /**
817 * Transfers the given request and applies request options.
818 *
819 * The URI of the request is not modified and the request options are used
820 * as-is without merging in default options.
821 *
822 * @param array $options See {@see RequestOptions}.
823 */
824 private function transfer(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface
825 {
826 $request = $this->applyOptions($request, $options);
827 $protocolVersion = $request->getProtocolVersion();
828 if ('' === $protocolVersion) {
829 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Sending a request with an empty protocol version is deprecated; guzzlehttp/guzzle 8.0 will reject empty protocol versions.');
830 $request = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, ['version' => '1.1']);
831 } elseif (!self::isProtocolVersionValid($protocolVersion)) {
832 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Sending a request with a malformed protocol version is deprecated; guzzlehttp/guzzle 8.0 will reject malformed protocol versions.');
833 }
834 /** @var HandlerStack $handler */
835 $handler = $options['handler'];
836 try {
837 return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::promiseFor($handler($request, $options));
838 } catch (\Exception $e) {
839 return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($e);
840 }
841 }
842 /**
843 * Applies the array of request options to a request.
844 */
845 private function applyOptions(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface
846 {
847 $modify = ['set_headers' => []];
848 if (isset($options['headers'])) {
849 if (\array_keys($options['headers']) === \range(0, \count($options['headers']) - 1)) {
850 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('The headers array must have header name as keys.');
851 }
852 $headers = $options['headers'];
853 $droppedHeaderNames = self::castDeprecatedHeaderOptionValues($headers);
854 if ($droppedHeaderNames !== [] && isset($options['_conditional'])) {
855 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove($droppedHeaderNames, $options['_conditional']);
856 }
857 $modify['set_headers'] = $headers;
858 unset($options['headers']);
859 }
860 if (isset($options['form_params'])) {
861 if (isset($options['multipart'])) {
862 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\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.');
863 }
864 $options['body'] = \http_build_query(self::normalizeNonFiniteFloats($options['form_params'], 'form_params'), '', '&');
865 unset($options['form_params']);
866 // Ensure that we don't have the header in different case and set the new value.
867 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
868 $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
869 }
870 if (isset($options['multipart'])) {
871 $options['body'] = new \YoastSEO_Vendor\GuzzleHttp\Psr7\MultipartStream($options['multipart']);
872 unset($options['multipart']);
873 }
874 if (isset($options['json'])) {
875 $json = \json_encode($options['json']);
876 if (\JSON_ERROR_NONE !== \json_last_error()) {
877 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('json_encode error: ' . \json_last_error_msg());
878 }
879 /** @var non-empty-string $json */
880 $options['body'] = $json;
881 unset($options['json']);
882 // Ensure that we don't have the header in different case and set the new value.
883 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
884 $options['_conditional']['Content-Type'] = 'application/json';
885 }
886 if (isset($options['decode_content']) && \is_string($options['decode_content'])) {
887 // Ensure that we don't have the header in different case and set the new value.
888 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Accept-Encoding'], $options['_conditional']);
889 $modify['set_headers']['Accept-Encoding'] = (string) $options['decode_content'];
890 }
891 if (isset($options['body'])) {
892 if (\is_array($options['body'])) {
893 throw $this->invalidBody();
894 }
895 $modify['body'] = self::createBodyStream($options['body']);
896 unset($options['body']);
897 }
898 if (!empty($options['auth']) && \is_array($options['auth'])) {
899 $value = $options['auth'];
900 $type = isset($value[2]) ? \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::asciiToLower($value[2]) : 'basic';
901 switch ($type) {
902 case 'basic':
903 // Ensure that we don't have the header in different case and set the new value.
904 $modify['set_headers'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Authorization'], $modify['set_headers']);
905 $modify['set_headers']['Authorization'] = 'Basic ' . \base64_encode("{$value[0]}:{$value[1]}");
906 break;
907 case 'digest':
908 // @todo: Do not rely on curl
909 $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_DIGEST;
910 $options['curl'][\CURLOPT_USERPWD] = "{$value[0]}:{$value[1]}";
911 break;
912 case 'ntlm':
913 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.12', 'Passing "ntlm" as the built-in auth type is deprecated; guzzlehttp/guzzle 8.0 will no longer apply NTLM through the "auth" request option. NTLM is also deprecated by curl/libcurl and may be unavailable in current or future libcurl builds. Avoid NTLM; if you must use it temporarily, configure cURL HTTP authentication options directly with a libcurl build that still supports NTLM.');
914 if (!\YoastSEO_Vendor\GuzzleHttp\Handler\CurlVersion::supportsNtlm()) {
915 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('NTLM authentication is not available because the installed curl/libcurl build does not provide NTLM support.');
916 }
917 $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_NTLM;
918 $options['curl'][\CURLOPT_USERPWD] = "{$value[0]}:{$value[1]}";
919 break;
920 }
921 }
922 if (isset($options['query'])) {
923 $value = $options['query'];
924 if (\is_array($value)) {
925 $value = \http_build_query(self::normalizeNonFiniteFloats($value, 'query'), '', '&', \PHP_QUERY_RFC3986);
926 }
927 if (!\is_string($value)) {
928 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('query must be a string or array');
929 }
930 $modify['query'] = $value;
931 unset($options['query']);
932 }
933 // Ensure that sink is not an invalid value.
934 if (isset($options['sink'])) {
935 // TODO: Add more sink validation?
936 if (\is_bool($options['sink'])) {
937 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('sink must not be a boolean');
938 }
939 }
940 if (isset($options['version'])) {
941 $modify['version'] = self::normalizeProtocolVersion($options['version']);
942 }
943 $request = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, $modify);
944 if ($request->getBody() instanceof \YoastSEO_Vendor\GuzzleHttp\Psr7\MultipartStream) {
945 // Use a multipart/form-data POST if a Content-Type is not set.
946 // Ensure that we don't have the header in different case and set the new value.
947 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
948 $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary=' . $request->getBody()->getBoundary();
949 }
950 // Merge in conditional headers if they are not present.
951 if (isset($options['_conditional'])) {
952 // Build up the changes so it's in a single clone of the message.
953 $modify = [];
954 foreach ($options['_conditional'] as $k => $v) {
955 $name = (string) $k;
956 if (!$request->hasHeader($name)) {
957 $modify['set_headers'][$name] = $v;
958 }
959 }
960 $request = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, $modify);
961 // Don't pass this internal value along to middleware/handlers.
962 unset($options['_conditional']);
963 }
964 return $request;
965 }
966 /**
967 * @param array<array-key, mixed> $headers
968 *
969 * @return list<string>
970 */
971 private static function castDeprecatedHeaderOptionValues(array &$headers) : array
972 {
973 $droppedHeaderNames = [];
974 foreach ($headers as $name => $value) {
975 if (\is_array($value)) {
976 if ($value === []) {
977 $droppedHeaderNames[] = (string) $name;
978 unset($headers[$name]);
979 continue;
980 }
981 foreach ($value as $index => $item) {
982 if ($item === null || !\is_string($item) && \is_scalar($item)) {
983 if (\is_float($item) && !\is_finite($item)) {
984 $item = \is_nan($item) ? 'NAN' : ($item > 0 ? 'INF' : '-INF');
985 }
986 $value[$index] = (string) $item;
987 }
988 }
989 $headers[$name] = $value;
990 continue;
991 }
992 if ($value === null || !\is_string($value) && \is_scalar($value)) {
993 if (\is_float($value) && !\is_finite($value)) {
994 $value = \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
995 }
996 $headers[$name] = (string) $value;
997 }
998 }
999 return $droppedHeaderNames;
1000 }
1001 /**
1002 * @param mixed $body
1003 */
1004 private static function createBodyStream($body) : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface
1005 {
1006 if ($body instanceof \YoastSEO_Vendor\Psr\Http\Message\StreamInterface) {
1007 return $body;
1008 }
1009 if (\is_resource($body) || $body === null || \is_string($body) || $body instanceof \Iterator) {
1010 return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($body);
1011 }
1012 if (\is_scalar($body)) {
1013 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.12', 'Passing a non-string scalar to the "body" request option is deprecated; guzzlehttp/guzzle 8.0 will reject non-string scalar bodies.');
1014 return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor(self::stringifyScalar($body));
1015 }
1016 if (\is_object($body) && \method_exists($body, '__toString')) {
1017 return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor((string) $body);
1018 }
1019 if (\is_callable($body)) {
1020 return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($body);
1021 }
1022 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException(\sprintf('Passing %s to request option "body" is invalid; expected resource|string|null|int|float|bool|StreamInterface|callable&object|Iterator|Stringable.', \get_debug_type($body)));
1023 }
1024 /**
1025 * @param bool|float|int|string $value
1026 */
1027 private static function stringifyScalar($value) : string
1028 {
1029 // Normalize non-finite floats to dodge PHP 8.5's (string) NAN
1030 // coercion warning while the value is still accepted.
1031 if (\is_float($value) && !\is_finite($value)) {
1032 $value = \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
1033 }
1034 return (string) $value;
1035 }
1036 /**
1037 * Converts non-finite floats in the array to the strings PHP coerces
1038 * them to, as implicit coercion of NAN emits a warning on PHP 8.5.
1039 */
1040 private static function normalizeNonFiniteFloats(array $values, string $option) : array
1041 {
1042 foreach ($values as $key => $value) {
1043 if (\is_array($value)) {
1044 $values[$key] = self::normalizeNonFiniteFloats($value, $option);
1045 } elseif (\is_float($value) && !\is_finite($value)) {
1046 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.12', 'Passing a non-finite float in the "%s" request option is deprecated; guzzlehttp/guzzle 8.0 will reject non-finite floats.', $option);
1047 $values[$key] = \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
1048 }
1049 }
1050 return $values;
1051 }
1052 /**
1053 * @param string|int|float $version
1054 */
1055 private static function normalizeProtocolVersion($version) : string
1056 {
1057 if ('' === $version) {
1058 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Passing an empty "version" request option is deprecated; guzzlehttp/guzzle 8.0 will reject empty protocol versions.');
1059 return '1.1';
1060 }
1061 return \is_float($version) ? \number_format($version, 1, '.', '') : (string) $version;
1062 }
1063 private static function isProtocolVersionValid(string $version) : bool
1064 {
1065 return 1 === \preg_match('/^\\d+(?:\\.\\d+)?$/D', $version);
1066 }
1067 /**
1068 * Return an InvalidArgumentException with pre-set message.
1069 */
1070 private function invalidBody() : \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException
1071 {
1072 return new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('Passing in the "body" request ' . 'option as an array to send a request is not supported. ' . '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.');
1073 }
1074 }
1075