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

1,095 lines 56.0 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), self::shouldPreserveHost($request)), $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 * Whether to preserve an existing Host header when the URI changes.
263 *
264 * A header matching the current URI carries no explicit override and is
265 * regenerated after base URI resolution or IDN conversion. Other values
266 * are preserved as deliberate overrides, as PSR-7 requires.
267 */
268 private static function shouldPreserveHost(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) : bool
269 {
270 if (!$request->hasHeader('Host')) {
271 return \false;
272 }
273 $uri = $request->getUri();
274 $host = $uri->getHost();
275 $port = $uri->getPort();
276 if ($port !== null) {
277 $host .= ':' . $port;
278 }
279 return $host !== $request->getHeaderLine('Host');
280 }
281 /**
282 * Configures the default options for a client.
283 */
284 private function configureDefaults(array $config) : void
285 {
286 $defaults = ['allow_redirects' => \YoastSEO_Vendor\GuzzleHttp\RedirectMiddleware::$defaultSettings, 'http_errors' => \true, 'decode_content' => \true, 'verify' => \true, 'cookies' => \false, 'idn_conversion' => \false, 'protocols' => ['http', 'https']];
287 // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
288 // We can only trust the HTTP_PROXY environment variable in a CLI
289 // process due to the fact that PHP has no reliable mechanism to
290 // get environment variables that start with "HTTP_".
291 if (\PHP_SAPI === 'cli' && ($proxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('HTTP_PROXY'))) {
292 $defaults['proxy']['http'] = $proxy;
293 }
294 if ($proxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('HTTPS_PROXY')) {
295 $defaults['proxy']['https'] = $proxy;
296 }
297 if ($noProxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('NO_PROXY')) {
298 $cleanedNoProxy = \str_replace(' ', '', $noProxy);
299 $defaults['proxy']['no'] = \explode(',', $cleanedNoProxy);
300 }
301 $this->config = $config + $defaults;
302 if (!empty($config['cookies']) && $config['cookies'] === \true) {
303 $this->config['cookies'] = new \YoastSEO_Vendor\GuzzleHttp\Cookie\CookieJar();
304 }
305 // Add the default user-agent header.
306 if (!isset($this->config['headers'])) {
307 $this->config['headers'] = ['User-Agent' => \YoastSEO_Vendor\GuzzleHttp\Utils::defaultUserAgent()];
308 } else {
309 // Add the User-Agent header if one was not already set.
310 $hasUserAgent = \false;
311 foreach (\array_keys($this->config['headers']) as $name) {
312 if (\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::asciiToLower((string) $name) === 'user-agent') {
313 $hasUserAgent = \true;
314 break;
315 }
316 }
317 if (!$hasUserAgent) {
318 $this->config['headers']['User-Agent'] = \YoastSEO_Vendor\GuzzleHttp\Utils::defaultUserAgent();
319 }
320 }
321 if (\is_array($this->config['headers'])) {
322 self::warnAboutInvalidHeaderOptionTypes($this->config['headers']);
323 self::castDeprecatedHeaderOptionValues($this->config['headers']);
324 }
325 }
326 /**
327 * Merges default options into the array.
328 *
329 * @param array $options Options to modify by reference
330 */
331 private function prepareDefaults(array $options) : array
332 {
333 self::warnAboutRequestLevelHandler($options);
334 $defaults = $this->config;
335 if (!empty($defaults['headers'])) {
336 // Default headers are only added if they are not present.
337 $defaults['_conditional'] = $defaults['headers'];
338 unset($defaults['headers']);
339 }
340 // Special handling for headers is required as they are added as
341 // conditional headers and as headers passed to a request ctor.
342 if (\array_key_exists('headers', $options)) {
343 // Allows default headers to be unset.
344 if ($options['headers'] === null) {
345 $defaults['_conditional'] = [];
346 unset($options['headers']);
347 } elseif (!\is_array($options['headers'])) {
348 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('headers must be an array');
349 }
350 }
351 // Shallow merge defaults underneath options.
352 $result = $options + $defaults;
353 // Remove null values.
354 foreach ($result as $k => $v) {
355 if ($v === null) {
356 unset($result[$k]);
357 }
358 }
359 self::warnAboutInvalidRequestOptionTypes($result);
360 return self::normalizeDeprecatedRequestOptionValues($result);
361 }
362 /**
363 * Normalize values that guzzlehttp/guzzle 8.0 rejects only after the
364 * corresponding 7.x deprecation has already been emitted.
365 *
366 * @param array<string, mixed> $options
367 *
368 * @return array<string, mixed>
369 */
370 private static function normalizeDeprecatedRequestOptionValues(array $options) : array
371 {
372 self::normalizeDeprecatedAuthOptionValues($options);
373 self::normalizeDeprecatedTlsFileOptionValues($options, 'cert');
374 self::normalizeDeprecatedTlsFileOptionValues($options, 'ssl_key');
375 self::normalizeDeprecatedStringOptionValues($options);
376 self::normalizeDeprecatedNumericOptionValues($options);
377 self::normalizeDeprecatedIntegerOptionValues($options);
378 return $options;
379 }
380 /**
381 * @param mixed $value
382 */
383 private static function canStringifyDeprecatedValue($value) : bool
384 {
385 return $value === null || \is_scalar($value) || \is_object($value) && \method_exists($value, '__toString');
386 }
387 /**
388 * @param mixed $value
389 */
390 private static function stringifyDeprecatedValue($value) : string
391 {
392 if (\is_float($value) && !\is_finite($value)) {
393 return \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
394 }
395 if ($value === null) {
396 return '';
397 }
398 if (\is_scalar($value)) {
399 return (string) $value;
400 }
401 if (\is_object($value) && \method_exists($value, '__toString')) {
402 return $value->__toString();
403 }
404 throw new \LogicException('Value is not stringable.');
405 }
406 /**
407 * @param array<string, mixed> $options
408 */
409 private static function normalizeDeprecatedAuthOptionValues(array &$options) : void
410 {
411 if (!isset($options['auth']) || !\is_array($options['auth']) || $options['auth'] === []) {
412 return;
413 }
414 foreach ([0, 1] as $index) {
415 if (\array_key_exists($index, $options['auth']) && !\is_string($options['auth'][$index]) && self::canStringifyDeprecatedValue($options['auth'][$index])) {
416 $options['auth'][$index] = self::stringifyDeprecatedValue($options['auth'][$index]);
417 }
418 }
419 if (\array_key_exists(2, $options['auth']) && $options['auth'][2] !== null && !\is_string($options['auth'][2]) && self::canStringifyDeprecatedValue($options['auth'][2])) {
420 $options['auth'][2] = self::stringifyDeprecatedValue($options['auth'][2]);
421 }
422 }
423 /**
424 * @param array<string, mixed> $options
425 */
426 private static function normalizeDeprecatedTlsFileOptionValues(array &$options, string $option) : void
427 {
428 if (!isset($options[$option]) || !\is_array($options[$option])) {
429 return;
430 }
431 foreach ([0, 1] as $index) {
432 if (\array_key_exists($index, $options[$option]) && $options[$option][$index] !== null && !\is_string($options[$option][$index]) && self::canStringifyDeprecatedValue($options[$option][$index])) {
433 $options[$option][$index] = self::stringifyDeprecatedValue($options[$option][$index]);
434 }
435 }
436 }
437 /**
438 * @param array<string, mixed> $options
439 */
440 private static function normalizeDeprecatedStringOptionValues(array &$options) : void
441 {
442 foreach (['cert_type', 'force_ip_resolve', 'ssl_key_type'] as $option) {
443 if (\array_key_exists($option, $options) && !\is_string($options[$option]) && self::canStringifyDeprecatedValue($options[$option])) {
444 $options[$option] = self::stringifyDeprecatedValue($options[$option]);
445 }
446 }
447 }
448 /**
449 * @param array<string, mixed> $options
450 */
451 private static function normalizeDeprecatedNumericOptionValues(array &$options) : void
452 {
453 foreach (['connect_timeout', 'delay', 'read_timeout', 'timeout'] as $option) {
454 if (\array_key_exists($option, $options) && \is_string($options[$option]) && \is_numeric($options[$option])) {
455 $options[$option] = $options[$option] + 0;
456 }
457 }
458 }
459 /**
460 * @param array<string, mixed> $options
461 */
462 private static function normalizeDeprecatedIntegerOptionValues(array &$options) : void
463 {
464 foreach (['crypto_method', 'crypto_method_max', 'retries'] as $option) {
465 if (!\array_key_exists($option, $options)) {
466 continue;
467 }
468 if (\is_string($options[$option]) && \preg_match('/^-?\\d+$/D', $options[$option]) === 1) {
469 $options[$option] = (int) $options[$option];
470 } elseif (\is_float($options[$option]) && \is_finite($options[$option]) && $options[$option] === (float) (int) $options[$option]) {
471 $options[$option] = (int) $options[$option];
472 }
473 }
474 }
475 private static function warnAboutRequestLevelHandler(array $options) : void
476 {
477 if (!\array_key_exists('handler', $options)) {
478 return;
479 }
480 \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.');
481 }
482 private static function warnAboutInvalidRequestOptionTypes(array $options) : void
483 {
484 if (isset($options['handler']) && !\is_callable($options['handler'])) {
485 self::warnInvalidRequestOptionType('handler', 'callable', $options['handler']);
486 }
487 if (isset($options['allow_redirects']) && \is_array($options['allow_redirects'])) {
488 self::warnAboutInvalidAllowRedirectsOptionTypes($options['allow_redirects']);
489 } elseif (isset($options['allow_redirects']) && !\is_bool($options['allow_redirects'])) {
490 self::warnInvalidRequestOptionType('allow_redirects', 'bool|array', $options['allow_redirects'], '7.13');
491 }
492 if (isset($options['auth'])) {
493 self::warnAboutInvalidAuthOptionTypes($options['auth']);
494 }
495 if (isset($options['body']) && \is_array($options['body'])) {
496 self::warnInvalidRequestOptionType('body', 'resource|string|null|int|float|bool|StreamInterface|(callable&object)|\\Iterator|\\Stringable', $options['body']);
497 }
498 self::warnAboutInvalidTlsFileOptionTypes($options, 'cert');
499 self::warnIfPresentAndNotString($options, 'cert_type');
500 self::warnIfPresentAndNotNumber($options, 'connect_timeout');
501 self::warnIfPresentAndNotInt($options, 'crypto_method');
502 self::warnIfPresentAndNotInt($options, 'crypto_method_max', null, '7.13');
503 self::warnIfPresentAndNotBoolOrResource($options, 'debug');
504 self::warnIfPresentAndNotBoolOrString($options, 'decode_content');
505 self::warnIfPresentAndNotNumber($options, 'delay');
506 if (isset($options['delay']) && \is_numeric($options['delay'])) {
507 $delay = (float) $options['delay'];
508 if (!\is_finite($delay) || $delay < 0.0) {
509 self::warnInvalidRequestOptionType('delay', 'finite int|float greater than or equal to 0', $options['delay'], '7.13');
510 }
511 }
512 self::warnIfPresentAndNotBoolOrInt($options, 'expect');
513 if (isset($options['form_params'])) {
514 self::warnAboutInvalidFormParamTypes($options['form_params']);
515 }
516 if (isset($options['force_ip_resolve']) && !\is_string($options['force_ip_resolve'])) {
517 self::warnInvalidRequestOptionType('force_ip_resolve', 'string', $options['force_ip_resolve']);
518 }
519 if (isset($options['force_ip_resolve']) && \is_string($options['force_ip_resolve']) && $options['force_ip_resolve'] !== 'v4' && $options['force_ip_resolve'] !== 'v6') {
520 self::warnInvalidRequestOptionType('force_ip_resolve', '"v4"|"v6"', $options['force_ip_resolve'], '7.13');
521 }
522 if (isset($options['headers'])) {
523 self::warnAboutInvalidHeaderOptionTypes($options['headers']);
524 }
525 self::warnIfPresentAndNotBool($options, 'http_errors');
526 if (isset($options['multipart'])) {
527 self::warnAboutInvalidMultipartOptionTypes($options['multipart']);
528 }
529 self::warnIfPresentAndNotCallable($options, 'on_headers');
530 self::warnIfPresentAndNotCallable($options, 'on_stats');
531 self::warnIfPresentAndNotCallable($options, 'on_trailers', null, '7.14');
532 self::warnIfPresentAndNotCallable($options, 'progress');
533 self::warnIfPresentAndNotStringArray($options, 'protocols', \true);
534 self::warnAboutInvalidProtocolValues($options, 'protocols');
535 self::warnAboutInvalidProxyOptionTypes($options);
536 self::warnIfPresentAndNotNumber($options, 'read_timeout');
537 self::warnIfPresentAndNotInt($options, 'retries');
538 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) {
539 self::warnInvalidRequestOptionType('sink', 'resource|string|StreamInterface', $options['sink']);
540 }
541 self::warnAboutInvalidTlsFileOptionTypes($options, 'ssl_key');
542 self::warnIfPresentAndNotString($options, 'ssl_key_type');
543 self::warnIfPresentAndNotBool($options, 'stream');
544 self::warnIfPresentAndNotArray($options, 'stream_context', 'array<array-key, mixed>');
545 self::warnIfPresentAndNotBool($options, 'synchronous');
546 self::warnIfPresentAndNotNumber($options, 'timeout');
547 self::warnIfPresentAndNotBoolOrString($options, 'verify');
548 self::warnIfPresentAndNotStringOrNumber($options, 'version');
549 self::warnIfPresentAndNotArray($options, 'curl', 'array<int|string, mixed>');
550 if (isset($options['cookies']) && $options['cookies'] === \true) {
551 self::warnInvalidRequestOptionType('cookies', 'false|CookieJarInterface', $options['cookies']);
552 }
553 if (isset($options['cookies']) && $options['cookies'] !== \false && $options['cookies'] !== \true && !$options['cookies'] instanceof \YoastSEO_Vendor\GuzzleHttp\Cookie\CookieJarInterface) {
554 self::warnInvalidRequestOptionType('cookies', 'false|CookieJarInterface', $options['cookies'], '7.13');
555 }
556 }
557 private static function warnAboutInvalidAllowRedirectsOptionTypes(array $allowRedirects) : void
558 {
559 self::warnIfPresentAndNotInt($allowRedirects, 'max', 'allow_redirects.max');
560 self::warnIfPresentAndNotBool($allowRedirects, 'strict', 'allow_redirects.strict');
561 self::warnIfPresentAndNotBool($allowRedirects, 'referer', 'allow_redirects.referer');
562 self::warnIfPresentAndNotStringArray($allowRedirects, 'protocols', \true, 'allow_redirects.protocols');
563 self::warnAboutInvalidProtocolValues($allowRedirects, 'protocols', 'allow_redirects.protocols');
564 self::warnIfPresentAndNotCallable($allowRedirects, 'on_redirect', 'allow_redirects.on_redirect');
565 self::warnIfPresentAndNotBool($allowRedirects, 'track_redirects', 'allow_redirects.track_redirects');
566 }
567 /**
568 * @param mixed $auth
569 */
570 private static function warnAboutInvalidAuthOptionTypes($auth) : void
571 {
572 if ($auth === \false || \is_string($auth) || $auth === []) {
573 return;
574 }
575 if (!\is_array($auth)) {
576 self::warnInvalidRequestOptionType('auth', 'array{0: string, 1: string, 2?: string|null}|string|false|null', $auth);
577 return;
578 }
579 if (!\array_key_exists(0, $auth) || !\is_string($auth[0])) {
580 self::warnInvalidRequestOptionType('auth.0', 'string', $auth[0] ?? null);
581 }
582 if (!\array_key_exists(1, $auth) || !\is_string($auth[1])) {
583 self::warnInvalidRequestOptionType('auth.1', 'string', $auth[1] ?? null);
584 }
585 if (\array_key_exists(2, $auth) && $auth[2] !== null && !\is_string($auth[2])) {
586 self::warnInvalidRequestOptionType('auth.2', 'string|null', $auth[2]);
587 }
588 }
589 /**
590 * @param mixed $value
591 */
592 private static function warnAboutInvalidFormParamTypes($value) : void
593 {
594 if (!\is_array($value)) {
595 self::warnInvalidRequestOptionType('form_params', 'array<array-key, string|int|float|bool|null|array>', $value);
596 return;
597 }
598 self::warnAboutInvalidFormParamArray($value, 'form_params');
599 }
600 private static function warnAboutInvalidFormParamArray(array $values, string $path) : bool
601 {
602 foreach ($values as $key => $item) {
603 $itemPath = $path . '.' . (string) $key;
604 if (\is_array($item)) {
605 if (!self::warnAboutInvalidFormParamArray($item, $itemPath)) {
606 return \false;
607 }
608 continue;
609 }
610 if ($item !== null && !\is_scalar($item)) {
611 self::warnInvalidRequestOptionType($itemPath, 'string|int|float|bool|null|array', $item);
612 return \false;
613 }
614 }
615 return \true;
616 }
617 /**
618 * @param mixed $headers
619 */
620 private static function warnAboutInvalidHeaderOptionTypes($headers) : void
621 {
622 if (!\is_array($headers)) {
623 self::warnInvalidRequestOptionType('headers', 'array<array-key, string|non-empty-array<array-key, string>>|null', $headers);
624 return;
625 }
626 foreach ($headers as $name => $value) {
627 $path = 'headers.' . (string) $name;
628 if (\is_array($value)) {
629 if ($value === []) {
630 self::warnInvalidRequestOptionType($path, 'string|non-empty-array<array-key, string>', $value);
631 break;
632 }
633 foreach ($value as $index => $item) {
634 if (!\is_string($item)) {
635 self::warnInvalidRequestOptionType($path . '.' . (string) $index, 'string', $item);
636 break 2;
637 }
638 }
639 } elseif (!\is_string($value)) {
640 self::warnInvalidRequestOptionType($path, 'string|non-empty-array<array-key, string>', $value);
641 break;
642 }
643 }
644 }
645 /**
646 * @param mixed $multipart
647 */
648 private static function warnAboutInvalidMultipartOptionTypes($multipart) : void
649 {
650 if (!\is_array($multipart)) {
651 self::warnInvalidRequestOptionType('multipart', 'array<array-key, array{name: string|int, contents: mixed, headers?: array<array-key, string>, filename?: string}>', $multipart);
652 return;
653 }
654 foreach ($multipart as $index => $part) {
655 $path = 'multipart.' . (string) $index;
656 if (!\is_array($part)) {
657 self::warnInvalidRequestOptionType($path, 'array{name: string|int, contents: mixed, headers?: array<array-key, string>, filename?: string}', $part);
658 return;
659 }
660 if (!\array_key_exists('name', $part) || !\is_string($part['name']) && !\is_int($part['name'])) {
661 self::warnInvalidRequestOptionType($path . '.name', 'string|int', $part['name'] ?? null);
662 }
663 if (!\array_key_exists('contents', $part)) {
664 self::warnInvalidRequestOptionType($path, 'array{name: string|int, contents: mixed, headers?: array<array-key, string>, filename?: string}', $part);
665 }
666 if (\array_key_exists('headers', $part)) {
667 if (!\is_array($part['headers'])) {
668 self::warnInvalidRequestOptionType($path . '.headers', 'array<array-key, string>', $part['headers']);
669 } else {
670 foreach ($part['headers'] as $name => $value) {
671 if (!\is_string($value)) {
672 self::warnInvalidRequestOptionType($path . '.headers.' . (string) $name, 'string', $value);
673 break 2;
674 }
675 }
676 }
677 }
678 if (\array_key_exists('filename', $part) && !\is_string($part['filename'])) {
679 self::warnInvalidRequestOptionType($path . '.filename', 'string', $part['filename']);
680 }
681 }
682 }
683 private static function warnAboutInvalidProxyOptionTypes(array $options) : void
684 {
685 if (!isset($options['proxy'])) {
686 return;
687 }
688 if (!\is_string($options['proxy']) && !\is_array($options['proxy'])) {
689 self::warnInvalidRequestOptionType('proxy', 'string|array{http?: string|null, https?: string|null, no?: string|array<array-key, string>|null}', $options['proxy']);
690 return;
691 }
692 if (!\is_array($options['proxy'])) {
693 return;
694 }
695 foreach (['http', 'https'] as $scheme) {
696 if (\array_key_exists($scheme, $options['proxy']) && $options['proxy'][$scheme] !== null && !\is_string($options['proxy'][$scheme])) {
697 self::warnInvalidRequestOptionType('proxy.' . $scheme, 'string|null', $options['proxy'][$scheme]);
698 }
699 }
700 if (!\array_key_exists('no', $options['proxy']) || $options['proxy']['no'] === null) {
701 return;
702 }
703 if (\is_string($options['proxy']['no'])) {
704 return;
705 }
706 if (!\is_array($options['proxy']['no'])) {
707 self::warnInvalidRequestOptionType('proxy.no', 'string|array<array-key, string>|null', $options['proxy']['no']);
708 return;
709 }
710 foreach ($options['proxy']['no'] as $index => $noProxy) {
711 if (!\is_string($noProxy)) {
712 self::warnInvalidRequestOptionType('proxy.no.' . (string) $index, 'string', $noProxy);
713 return;
714 }
715 }
716 }
717 private static function warnAboutInvalidTlsFileOptionTypes(array $options, string $option) : void
718 {
719 if (!isset($options[$option])) {
720 return;
721 }
722 if (\is_string($options[$option])) {
723 return;
724 }
725 if (!\is_array($options[$option])) {
726 self::warnInvalidRequestOptionType($option, 'string|array{0: string, 1?: string}', $options[$option]);
727 return;
728 }
729 if (!\array_key_exists(0, $options[$option]) || !\is_string($options[$option][0])) {
730 self::warnInvalidRequestOptionType($option . '.0', 'string', $options[$option][0] ?? null);
731 }
732 if (\array_key_exists(1, $options[$option]) && $options[$option][1] !== null && !\is_string($options[$option][1])) {
733 self::warnInvalidRequestOptionType($option . '.1', 'string|null', $options[$option][1]);
734 }
735 }
736 private static function warnIfPresentAndNotArray(array $options, string $option, string $expected) : void
737 {
738 if (\array_key_exists($option, $options) && !\is_array($options[$option])) {
739 self::warnInvalidRequestOptionType($option, $expected, $options[$option]);
740 }
741 }
742 private static function warnIfPresentAndNotBool(array $options, string $option, ?string $path = null) : void
743 {
744 if (\array_key_exists($option, $options) && !\is_bool($options[$option])) {
745 self::warnInvalidRequestOptionType($path ?? $option, 'bool', $options[$option]);
746 }
747 }
748 private static function warnIfPresentAndNotBoolOrInt(array $options, string $option) : void
749 {
750 if (\array_key_exists($option, $options) && !\is_bool($options[$option]) && !\is_int($options[$option])) {
751 self::warnInvalidRequestOptionType($option, 'bool|int', $options[$option]);
752 }
753 }
754 private static function warnIfPresentAndNotBoolOrResource(array $options, string $option) : void
755 {
756 if (\array_key_exists($option, $options) && !\is_bool($options[$option]) && !\is_resource($options[$option])) {
757 self::warnInvalidRequestOptionType($option, 'bool|resource', $options[$option]);
758 }
759 }
760 private static function warnIfPresentAndNotBoolOrString(array $options, string $option) : void
761 {
762 if (\array_key_exists($option, $options) && !\is_bool($options[$option]) && !\is_string($options[$option])) {
763 self::warnInvalidRequestOptionType($option, 'bool|string', $options[$option]);
764 }
765 }
766 private static function warnIfPresentAndNotCallable(array $options, string $option, ?string $path = null, string $since = '7.11') : void
767 {
768 if (\array_key_exists($option, $options) && !\is_callable($options[$option])) {
769 self::warnInvalidRequestOptionType($path ?? $option, 'callable', $options[$option], $since);
770 }
771 }
772 private static function warnIfPresentAndNotInt(array $options, string $option, ?string $path = null, string $since = '7.11') : void
773 {
774 if (\array_key_exists($option, $options) && !\is_int($options[$option])) {
775 self::warnInvalidRequestOptionType($path ?? $option, 'int', $options[$option], $since);
776 }
777 }
778 private static function warnIfPresentAndNotNumber(array $options, string $option) : void
779 {
780 if (\array_key_exists($option, $options) && !\is_int($options[$option]) && !\is_float($options[$option])) {
781 self::warnInvalidRequestOptionType($option, 'int|float', $options[$option]);
782 }
783 }
784 private static function warnIfPresentAndNotString(array $options, string $option) : void
785 {
786 if (\array_key_exists($option, $options) && !\is_string($options[$option])) {
787 self::warnInvalidRequestOptionType($option, 'string', $options[$option]);
788 }
789 }
790 private static function warnIfPresentAndNotStringArray(array $options, string $option, bool $nonEmpty, ?string $path = null) : void
791 {
792 if (!\array_key_exists($option, $options)) {
793 return;
794 }
795 $path = $path ?? $option;
796 $expected = ($nonEmpty ? 'non-empty-' : '') . 'array<array-key, string>';
797 if (!\is_array($options[$option]) || $nonEmpty && $options[$option] === []) {
798 self::warnInvalidRequestOptionType($path, $expected, $options[$option]);
799 return;
800 }
801 foreach ($options[$option] as $index => $item) {
802 if (!\is_string($item)) {
803 self::warnInvalidRequestOptionType($path . '.' . (string) $index, 'string', $item);
804 return;
805 }
806 }
807 }
808 /**
809 * @param array<array-key, mixed> $options
810 */
811 private static function warnAboutInvalidProtocolValues(array $options, string $option, ?string $path = null) : void
812 {
813 if (!isset($options[$option]) || !\is_array($options[$option])) {
814 return;
815 }
816 $path = $path ?? $option;
817 foreach ($options[$option] as $index => $protocol) {
818 if (\is_string($protocol) && $protocol !== 'http' && $protocol !== 'https') {
819 self::warnInvalidRequestOptionType($path . '.' . (string) $index, '"http"|"https"', $protocol, '7.13');
820 }
821 }
822 }
823 private static function warnIfPresentAndNotStringOrNumber(array $options, string $option) : void
824 {
825 if (\array_key_exists($option, $options) && !\is_string($options[$option]) && !\is_int($options[$option]) && !\is_float($options[$option])) {
826 self::warnInvalidRequestOptionType($option, 'string|int|float', $options[$option]);
827 }
828 }
829 /**
830 * @param mixed $value
831 */
832 private static function warnInvalidRequestOptionType(string $option, string $expected, $value, string $since = '7.11') : void
833 {
834 \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);
835 }
836 /**
837 * Transfers the given request and applies request options.
838 *
839 * The URI of the request is not modified and the request options are used
840 * as-is without merging in default options.
841 *
842 * @param array $options See {@see RequestOptions}.
843 */
844 private function transfer(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface
845 {
846 $request = $this->applyOptions($request, $options);
847 $protocolVersion = $request->getProtocolVersion();
848 if ('' === $protocolVersion) {
849 \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.');
850 $request = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, ['version' => '1.1']);
851 } elseif (!self::isProtocolVersionValid($protocolVersion)) {
852 \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.');
853 }
854 /** @var HandlerStack $handler */
855 $handler = $options['handler'];
856 try {
857 return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::promiseFor($handler($request, $options));
858 } catch (\Exception $e) {
859 return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($e);
860 }
861 }
862 /**
863 * Applies the array of request options to a request.
864 */
865 private function applyOptions(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface
866 {
867 $modify = ['set_headers' => []];
868 if (isset($options['headers'])) {
869 if (\array_keys($options['headers']) === \range(0, \count($options['headers']) - 1)) {
870 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('The headers array must have header name as keys.');
871 }
872 $headers = $options['headers'];
873 $droppedHeaderNames = self::castDeprecatedHeaderOptionValues($headers);
874 if ($droppedHeaderNames !== [] && isset($options['_conditional'])) {
875 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove($droppedHeaderNames, $options['_conditional']);
876 }
877 $modify['set_headers'] = $headers;
878 unset($options['headers']);
879 }
880 if (isset($options['form_params'])) {
881 if (isset($options['multipart'])) {
882 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.');
883 }
884 $options['body'] = \http_build_query(self::normalizeNonFiniteFloats($options['form_params'], 'form_params'), '', '&');
885 unset($options['form_params']);
886 // Ensure that we don't have the header in different case and set the new value.
887 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
888 $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
889 }
890 if (isset($options['multipart'])) {
891 $options['body'] = new \YoastSEO_Vendor\GuzzleHttp\Psr7\MultipartStream($options['multipart']);
892 unset($options['multipart']);
893 }
894 if (isset($options['json'])) {
895 $json = \json_encode($options['json']);
896 if (\JSON_ERROR_NONE !== \json_last_error()) {
897 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('json_encode error: ' . \json_last_error_msg());
898 }
899 /** @var non-empty-string $json */
900 $options['body'] = $json;
901 unset($options['json']);
902 // Ensure that we don't have the header in different case and set the new value.
903 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
904 $options['_conditional']['Content-Type'] = 'application/json';
905 }
906 if (isset($options['decode_content']) && \is_string($options['decode_content'])) {
907 // Ensure that we don't have the header in different case and set the new value.
908 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Accept-Encoding'], $options['_conditional']);
909 $modify['set_headers']['Accept-Encoding'] = (string) $options['decode_content'];
910 }
911 if (isset($options['body'])) {
912 if (\is_array($options['body'])) {
913 throw $this->invalidBody();
914 }
915 $modify['body'] = self::createBodyStream($options['body']);
916 unset($options['body']);
917 }
918 if (!empty($options['auth']) && \is_array($options['auth'])) {
919 $value = $options['auth'];
920 $type = isset($value[2]) ? \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::asciiToLower($value[2]) : 'basic';
921 switch ($type) {
922 case 'basic':
923 // Ensure that we don't have the header in different case and set the new value.
924 $modify['set_headers'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Authorization'], $modify['set_headers']);
925 $modify['set_headers']['Authorization'] = 'Basic ' . \base64_encode("{$value[0]}:{$value[1]}");
926 break;
927 case 'digest':
928 // @todo: Do not rely on curl
929 $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_DIGEST;
930 $options['curl'][\CURLOPT_USERPWD] = "{$value[0]}:{$value[1]}";
931 break;
932 case 'ntlm':
933 \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.');
934 if (!\YoastSEO_Vendor\GuzzleHttp\Handler\CurlVersion::supportsNtlm()) {
935 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('NTLM authentication is not available because the installed curl/libcurl build does not provide NTLM support.');
936 }
937 $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_NTLM;
938 $options['curl'][\CURLOPT_USERPWD] = "{$value[0]}:{$value[1]}";
939 break;
940 }
941 }
942 if (isset($options['query'])) {
943 $value = $options['query'];
944 if (\is_array($value)) {
945 $value = \http_build_query(self::normalizeNonFiniteFloats($value, 'query'), '', '&', \PHP_QUERY_RFC3986);
946 }
947 if (!\is_string($value)) {
948 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('query must be a string or array');
949 }
950 $modify['query'] = $value;
951 unset($options['query']);
952 }
953 // Ensure that sink is not an invalid value.
954 if (isset($options['sink'])) {
955 // TODO: Add more sink validation?
956 if (\is_bool($options['sink'])) {
957 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('sink must not be a boolean');
958 }
959 }
960 if (isset($options['version'])) {
961 $modify['version'] = self::normalizeProtocolVersion($options['version']);
962 }
963 $request = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, $modify);
964 if ($request->getBody() instanceof \YoastSEO_Vendor\GuzzleHttp\Psr7\MultipartStream) {
965 // Use a multipart/form-data POST if a Content-Type is not set.
966 // Ensure that we don't have the header in different case and set the new value.
967 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
968 $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary=' . $request->getBody()->getBoundary();
969 }
970 // Merge in conditional headers if they are not present.
971 if (isset($options['_conditional'])) {
972 // Build up the changes so it's in a single clone of the message.
973 $modify = [];
974 foreach ($options['_conditional'] as $k => $v) {
975 $name = (string) $k;
976 if (!$request->hasHeader($name)) {
977 $modify['set_headers'][$name] = $v;
978 }
979 }
980 $request = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, $modify);
981 // Don't pass this internal value along to middleware/handlers.
982 unset($options['_conditional']);
983 }
984 return $request;
985 }
986 /**
987 * @param array<array-key, mixed> $headers
988 *
989 * @return list<string>
990 */
991 private static function castDeprecatedHeaderOptionValues(array &$headers) : array
992 {
993 $droppedHeaderNames = [];
994 foreach ($headers as $name => $value) {
995 if (\is_array($value)) {
996 if ($value === []) {
997 $droppedHeaderNames[] = (string) $name;
998 unset($headers[$name]);
999 continue;
1000 }
1001 foreach ($value as $index => $item) {
1002 if ($item === null || !\is_string($item) && \is_scalar($item)) {
1003 if (\is_float($item) && !\is_finite($item)) {
1004 $item = \is_nan($item) ? 'NAN' : ($item > 0 ? 'INF' : '-INF');
1005 }
1006 $value[$index] = (string) $item;
1007 }
1008 }
1009 $headers[$name] = $value;
1010 continue;
1011 }
1012 if ($value === null || !\is_string($value) && \is_scalar($value)) {
1013 if (\is_float($value) && !\is_finite($value)) {
1014 $value = \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
1015 }
1016 $headers[$name] = (string) $value;
1017 }
1018 }
1019 return $droppedHeaderNames;
1020 }
1021 /**
1022 * @param mixed $body
1023 */
1024 private static function createBodyStream($body) : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface
1025 {
1026 if ($body instanceof \YoastSEO_Vendor\Psr\Http\Message\StreamInterface) {
1027 return $body;
1028 }
1029 if (\is_resource($body) || $body === null || \is_string($body) || $body instanceof \Iterator) {
1030 return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($body);
1031 }
1032 if (\is_scalar($body)) {
1033 \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.');
1034 return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor(self::stringifyScalar($body));
1035 }
1036 if (\is_object($body) && \method_exists($body, '__toString')) {
1037 return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor((string) $body);
1038 }
1039 if (\is_callable($body)) {
1040 return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($body);
1041 }
1042 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)));
1043 }
1044 /**
1045 * @param bool|float|int|string $value
1046 */
1047 private static function stringifyScalar($value) : string
1048 {
1049 // Normalize non-finite floats to dodge PHP 8.5's (string) NAN
1050 // coercion warning while the value is still accepted.
1051 if (\is_float($value) && !\is_finite($value)) {
1052 $value = \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
1053 }
1054 return (string) $value;
1055 }
1056 /**
1057 * Converts non-finite floats in the array to the strings PHP coerces
1058 * them to, as implicit coercion of NAN emits a warning on PHP 8.5.
1059 */
1060 private static function normalizeNonFiniteFloats(array $values, string $option) : array
1061 {
1062 foreach ($values as $key => $value) {
1063 if (\is_array($value)) {
1064 $values[$key] = self::normalizeNonFiniteFloats($value, $option);
1065 } elseif (\is_float($value) && !\is_finite($value)) {
1066 \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);
1067 $values[$key] = \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
1068 }
1069 }
1070 return $values;
1071 }
1072 /**
1073 * @param string|int|float $version
1074 */
1075 private static function normalizeProtocolVersion($version) : string
1076 {
1077 if ('' === $version) {
1078 \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.');
1079 return '1.1';
1080 }
1081 return \is_float($version) ? \number_format($version, 1, '.', '') : (string) $version;
1082 }
1083 private static function isProtocolVersionValid(string $version) : bool
1084 {
1085 return 1 === \preg_match('/^\\d+(?:\\.\\d+)?$/D', $version);
1086 }
1087 /**
1088 * Return an InvalidArgumentException with pre-set message.
1089 */
1090 private function invalidBody() : \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException
1091 {
1092 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.');
1093 }
1094 }
1095