| @@ -36,9 +36,9 @@ | ||
| 36 | 36 | \var_dump($input); |
| 37 | 37 | // normalize float vs double |
| 38 | 38 | /** @var string $varDumpContent */ |
| 39 | 39 | $varDumpContent = \ob_get_clean(); |
| 40 | - return \str_replace('double(', 'float(', \rtrim($varDumpContent)); | |
| 40 | + return \str_replace('double(', 'float(', \rtrim($varDumpContent, " \n\r\t\x00\v")); | |
| 41 | 41 | } |
| 42 | 42 | } |
| 43 | 43 | /** |
| 44 | 44 | * Parses an array of header lines into an associative array of headers. |
| @@ -50,9 +50,9 @@ | ||
| 50 | 50 | { |
| 51 | 51 | $headers = []; |
| 52 | 52 | foreach ($lines as $line) { |
| 53 | 53 | $parts = \explode(':', $line, 2); |
| 54 | - $headers[\trim($parts[0])][] = isset($parts[1]) ? \trim($parts[1]) : null; | |
| 54 | + $headers[\trim($parts[0], " \n\r\t\x00\v")][] = isset($parts[1]) ? \trim($parts[1], " \n\r\t\x00\v") : null; | |
| 55 | 55 | } |
| 56 | 56 | return $headers; |
| 57 | 57 | } |
| 58 | 58 | /** |
| @@ -76,9 +76,9 @@ | ||
| 76 | 76 | * Chooses and creates a default handler to use based on the environment. |
| 77 | 77 | * |
| 78 | 78 | * The returned handler is not wrapped by any default middlewares. |
| 79 | 79 | * |
| 80 | - * @param array{transport_sharing?: mixed} $handlerOptions Handler constructor options. | |
| 80 | + * @param array{transport_sharing?: mixed, max_host_connections?: mixed, max_total_connections?: mixed, multiplex?: mixed} $handlerOptions Handler constructor options. | |
| 81 | 81 | * |
| 82 | 82 | * @return callable(RequestInterface, array): Promise\PromiseInterface Returns the best handler for the given system. |
| 83 | 83 | * |
| 84 | 84 | * @throws \RuntimeException if no viable Handler is available. |
| @@ -84,39 +84,123 @@ | ||
| 84 | 84 | * @throws \RuntimeException if no viable Handler is available. |
| 85 | 85 | */ |
| 86 | 86 | public static function chooseHandler(array $handlerOptions = []) : callable |
| 87 | 87 | { |
| 88 | - $handler = null; | |
| 89 | 88 | $sharingMode = \YoastSEO_Vendor\GuzzleHttp\Handler\CurlShareHandleState::normalizeMode($handlerOptions['transport_sharing'] ?? null, 'transport_sharing'); |
| 90 | - $sharingRequested = $sharingMode !== \YoastSEO_Vendor\GuzzleHttp\TransportSharing::NONE; | |
| 91 | - $sharingRequired = $sharingMode === \YoastSEO_Vendor\GuzzleHttp\TransportSharing::HANDLER_REQUIRE; | |
| 92 | - $curlHandlerOptions = []; | |
| 93 | - $curlSupported = \defined('CURLOPT_CUSTOMREQUEST') && \YoastSEO_Vendor\GuzzleHttp\Handler\CurlVersion::supportsCurlHandler() && (\function_exists('curl_multi_exec') || \function_exists('curl_exec')); | |
| 94 | - if ($sharingRequired && !$curlSupported) { | |
| 89 | + $sharingRequired = self::isTransportSharingRequired($sharingMode); | |
| 90 | + $connectionCapsRequired = self::hasConnectionCapOptions($handlerOptions); | |
| 91 | + $handler = self::createCurlHandler($sharingMode, $handlerOptions); | |
| 92 | + if ($sharingRequired && $handler === null) { | |
| 95 | 93 | throw new \RuntimeException('Required transport sharing requires the PHP cURL extension, curl_exec() or curl_multi_exec(), and libcurl 7.21.2 or higher.'); |
| 96 | 94 | } |
| 97 | - if ($curlSupported) { | |
| 98 | - if ($sharingRequested) { | |
| 99 | - $shareState = \YoastSEO_Vendor\GuzzleHttp\Handler\CurlShareHandleState::fromOption($sharingMode); | |
| 100 | - if ($shareState !== null) { | |
| 101 | - $curlHandlerOptions['transport_sharing'] = $shareState; | |
| 102 | - } | |
| 95 | + if (\ini_get('allow_url_fopen')) { | |
| 96 | + return self::addStreamHandler($handler, $sharingMode, $sharingRequired, self::connectionCapOptions($handlerOptions)); | |
| 97 | + } | |
| 98 | + if ($handler !== null) { | |
| 99 | + return $handler; | |
| 100 | + } | |
| 101 | + if ($connectionCapsRequired) { | |
| 102 | + throw new \RuntimeException('Connection cap options require a cap-capable cURL multi handler or the allow_url_fopen ini setting for stream fallback.'); | |
| 103 | + } | |
| 104 | + throw new \RuntimeException('GuzzleHttp requires cURL, the allow_url_fopen ini setting, or a custom HTTP handler.'); | |
| 105 | + } | |
| 106 | + private static function isTransportSharingRequired(string $sharingMode) : bool | |
| 107 | + { | |
| 108 | + return $sharingMode === \YoastSEO_Vendor\GuzzleHttp\TransportSharing::HANDLER_REQUIRE; | |
| 109 | + } | |
| 110 | + /** | |
| 111 | + * @param array{max_host_connections?: mixed, max_total_connections?: mixed} $handlerOptions | |
| 112 | + */ | |
| 113 | + private static function hasConnectionCapOptions(array $handlerOptions) : bool | |
| 114 | + { | |
| 115 | + return self::connectionCapOptions($handlerOptions) !== []; | |
| 116 | + } | |
| 117 | + /** | |
| 118 | + * @param array{max_host_connections?: mixed, max_total_connections?: mixed, multiplex?: mixed} $handlerOptions | |
| 119 | + * | |
| 120 | + * @return (callable(RequestInterface, array): Promise\PromiseInterface)|null | |
| 121 | + */ | |
| 122 | + private static function createCurlHandler(string $sharingMode, array $handlerOptions) : ?callable | |
| 123 | + { | |
| 124 | + if (!\defined('CURLOPT_CUSTOMREQUEST') || !\YoastSEO_Vendor\GuzzleHttp\Handler\CurlVersion::supportsCurlHandler()) { | |
| 125 | + return null; | |
| 126 | + } | |
| 127 | + $connectionCapOptions = self::connectionCapOptions($handlerOptions); | |
| 128 | + if ($connectionCapOptions !== [] && (!\YoastSEO_Vendor\GuzzleHttp\Handler\CurlVersion::supportsConnectionCaps() || !\function_exists('curl_multi_exec'))) { | |
| 129 | + return null; | |
| 130 | + } | |
| 131 | + $curlHandlerOptions = self::createCurlHandlerOptions($sharingMode); | |
| 132 | + $curlMultiHandlerOptions = $curlHandlerOptions + $connectionCapOptions; | |
| 133 | + if (($handlerOptions['multiplex'] ?? null) === \YoastSEO_Vendor\GuzzleHttp\Multiplexing::NONE) { | |
| 134 | + // Forwarded to the CurlMultiHandler only: CurlHandler and | |
| 135 | + // StreamHandler validate known options, and both satisfy NONE | |
| 136 | + // per-request without a handler option. | |
| 137 | + $curlMultiHandlerOptions['multiplex'] = \YoastSEO_Vendor\GuzzleHttp\Multiplexing::NONE; | |
| 138 | + } | |
| 139 | + if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) { | |
| 140 | + $multiHandler = new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlMultiHandler($curlMultiHandlerOptions); | |
| 141 | + if ($connectionCapOptions !== []) { | |
| 142 | + // Connection caps only govern transfers on the multi handle, so | |
| 143 | + // the synchronous CurlHandler fast path would escape them. | |
| 144 | + return $multiHandler; | |
| 103 | 145 | } |
| 104 | - if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) { | |
| 105 | - $handler = \YoastSEO_Vendor\GuzzleHttp\Handler\Proxy::wrapSync(new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlMultiHandler($curlHandlerOptions), new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlHandler($curlHandlerOptions)); | |
| 106 | - } elseif (\function_exists('curl_exec')) { | |
| 107 | - $handler = new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlHandler($curlHandlerOptions); | |
| 108 | - } elseif (\function_exists('curl_multi_exec')) { | |
| 109 | - $handler = new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlMultiHandler($curlHandlerOptions); | |
| 146 | + return \YoastSEO_Vendor\GuzzleHttp\Handler\Proxy::wrapSync($multiHandler, new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlHandler($curlHandlerOptions)); | |
| 147 | + } | |
| 148 | + if ($connectionCapOptions === [] && \function_exists('curl_exec')) { | |
| 149 | + return new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlHandler($curlHandlerOptions); | |
| 150 | + } | |
| 151 | + if (\function_exists('curl_multi_exec')) { | |
| 152 | + return new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlMultiHandler($curlMultiHandlerOptions); | |
| 153 | + } | |
| 154 | + return null; | |
| 155 | + } | |
| 156 | + /** | |
| 157 | + * @return array<string, mixed> | |
| 158 | + */ | |
| 159 | + private static function createCurlHandlerOptions(string $sharingMode) : array | |
| 160 | + { | |
| 161 | + if ($sharingMode === \YoastSEO_Vendor\GuzzleHttp\TransportSharing::NONE) { | |
| 162 | + return []; | |
| 163 | + } | |
| 164 | + $shareState = \YoastSEO_Vendor\GuzzleHttp\Handler\CurlShareHandleState::fromOption($sharingMode); | |
| 165 | + return $shareState === null ? [] : ['transport_sharing' => $shareState]; | |
| 166 | + } | |
| 167 | + /** | |
| 168 | + * @param array{max_host_connections?: mixed, max_total_connections?: mixed} $handlerOptions | |
| 169 | + * | |
| 170 | + * @return array{max_host_connections?: int, max_total_connections?: int} | |
| 171 | + */ | |
| 172 | + private static function connectionCapOptions(array $handlerOptions) : array | |
| 173 | + { | |
| 174 | + $options = []; | |
| 175 | + foreach (['max_host_connections', 'max_total_connections'] as $capOption) { | |
| 176 | + $value = $handlerOptions[$capOption] ?? null; | |
| 177 | + if ($value === null) { | |
| 178 | + continue; | |
| 110 | 179 | } |
| 180 | + if (!\is_int($value) || $value < 1) { | |
| 181 | + throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException(\sprintf('%s must be a positive integer.', $capOption)); | |
| 182 | + } | |
| 183 | + $options[$capOption] = $value; | |
| 111 | 184 | } |
| 112 | - if (\ini_get('allow_url_fopen')) { | |
| 113 | - $streamHandler = new \YoastSEO_Vendor\GuzzleHttp\Handler\StreamHandler(['transport_sharing' => $sharingMode]); | |
| 114 | - $handler = $handler ? \YoastSEO_Vendor\GuzzleHttp\Handler\Proxy::wrapStreaming($handler, $streamHandler) : $streamHandler; | |
| 115 | - } elseif (!$handler) { | |
| 116 | - throw new \RuntimeException('GuzzleHttp requires cURL, the allow_url_fopen ini setting, or a custom HTTP handler.'); | |
| 185 | + return $options; | |
| 186 | + } | |
| 187 | + /** | |
| 188 | + * @param (callable(RequestInterface, array): Promise\PromiseInterface)|null $handler | |
| 189 | + * @param array{max_host_connections?: int, max_total_connections?: int} $connectionCapOptions | |
| 190 | + * | |
| 191 | + * @return callable(RequestInterface, array): Promise\PromiseInterface | |
| 192 | + */ | |
| 193 | + private static function addStreamHandler(?callable $handler, string $sharingMode, bool $sharingRequired, array $connectionCapOptions) : callable | |
| 194 | + { | |
| 195 | + $streamHandler = new \YoastSEO_Vendor\GuzzleHttp\Handler\StreamHandler(['transport_sharing' => $sharingMode] + $connectionCapOptions); | |
| 196 | + if ($handler === null) { | |
| 197 | + return $streamHandler; | |
| 117 | 198 | } |
| 118 | - return $handler; | |
| 199 | + if (!$sharingRequired) { | |
| 200 | + $handler = \YoastSEO_Vendor\GuzzleHttp\Handler\Proxy::wrapTlsFallback($handler, $streamHandler); | |
| 201 | + } | |
| 202 | + return \YoastSEO_Vendor\GuzzleHttp\Handler\Proxy::wrapStreaming($handler, $streamHandler); | |
| 119 | 203 | } |
| 120 | 204 | /** |
| 121 | 205 | * Get the default User-Agent string to use with Guzzle. |
| 122 | 206 | */ |
| @@ -140,8 +224,9 @@ | ||
| 140 | 224 | * @deprecated Utils::defaultCaBundle will be removed in guzzlehttp/guzzle:8.0. This method is not needed in PHP 5.6+. |
| 141 | 225 | */ |
| 142 | 226 | public static function defaultCaBundle() : string |
| 143 | 227 | { |
| 228 | + \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. This method is not needed in PHP 5.6+.', __METHOD__); | |
| 144 | 229 | static $cached = null; |
| 145 | 230 | static $cafiles = [ |
| 146 | 231 | // Red Hat, CentOS, Fedora (provided by the ca-certificates package) |
| 147 | 232 | '/etc/pki/tls/certs/ca-bundle.crt', |
| @@ -177,9 +262,9 @@ | ||
| 177 | 262 | No system CA bundle could be found in any of the the common system locations. |
| 178 | 263 | PHP versions earlier than 5.6 are not properly configured to use the system's |
| 179 | 264 | CA bundle by default. In order to verify peer certificates, you will need to |
| 180 | 265 | supply the path on disk to a certificate bundle to the 'verify' request option: |
| 181 | -https://github.com/guzzle/guzzle/blob/7.12/docs/request-options.md#verify. If | |
| 266 | +https://github.com/guzzle/guzzle/blob/7.15/docs/request-options.md#verify. If | |
| 182 | 267 | you do not need a specific certificate bundle, then Mozilla provides a commonly |
| 183 | 268 | used CA bundle which can be downloaded here (provided by the maintainer of |
| 184 | 269 | cURL): https://curl.se/ca/cacert.pem. Once you have a CA bundle available on |
| 185 | 270 | disk, you can set the 'openssl.cafile' PHP ini setting to point to the path to |
| @@ -195,9 +280,9 @@ | ||
| 195 | 280 | public static function normalizeHeaderKeys(array $headers) : array |
| 196 | 281 | { |
| 197 | 282 | $result = []; |
| 198 | 283 | foreach (\array_keys($headers) as $key) { |
| 199 | - $result[\strtolower((string) $key)] = $key; | |
| 284 | + $result[\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::asciiToLower((string) $key)] = $key; | |
| 200 | 285 | } |
| 201 | 286 | return $result; |
| 202 | 287 | } |
| 203 | 288 | /** |
| @@ -292,9 +377,9 @@ | ||
| 292 | 377 | foreach ($noProxy as $area) { |
| 293 | 378 | if (!\is_string($area)) { |
| 294 | 379 | continue; |
| 295 | 380 | } |
| 296 | - $area = \trim($area); | |
| 381 | + $area = \trim($area, " \n\r\t\x00\v"); | |
| 297 | 382 | // Always match on wildcards. |
| 298 | 383 | if ($area === '*') { |
| 299 | 384 | return \true; |
| 300 | 385 | } |
| @@ -332,9 +417,9 @@ | ||
| 332 | 417 | * @return array{type: string, value: string, port: int|null, matchesRoot: bool}|array{type: string, value: string, prefix: int}|null |
| 333 | 418 | */ |
| 334 | 419 | private static function parseNoProxyRule(string $area) : ?array |
| 335 | 420 | { |
| 336 | - $area = \trim($area); | |
| 421 | + $area = \trim($area, " \n\r\t\x00\v"); | |
| 337 | 422 | if ($area === '' || $area === '*') { |
| 338 | 423 | return null; |
| 339 | 424 | } |
| 340 | 425 | if (\strpos($area, '/') !== \false) { |
| @@ -390,9 +475,9 @@ | ||
| 390 | 475 | if ($host === '') { |
| 391 | 476 | return null; |
| 392 | 477 | } |
| 393 | 478 | } |
| 394 | - return ['type' => 'domain', 'value' => \strtolower($host), 'port' => $port, 'matchesRoot' => $matchesRoot]; | |
| 479 | + return ['type' => 'domain', 'value' => \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::asciiToLower($host), 'port' => $port, 'matchesRoot' => $matchesRoot]; | |
| 395 | 480 | } |
| 396 | 481 | /** |
| 397 | 482 | * @return array{0: string, 1: int|null}|null |
| 398 | 483 | */ |
| @@ -547,11 +632,13 @@ | ||
| 547 | 632 | * |
| 548 | 633 | * @throws InvalidArgumentException if the JSON cannot be decoded. |
| 549 | 634 | * |
| 550 | 635 | * @see https://www.php.net/manual/en/function.json-decode.php |
| 636 | + * @deprecated Utils::jsonDecode() will be removed in guzzlehttp/guzzle:8.0. Use PHP's json_decode() instead. | |
| 551 | 637 | */ |
| 552 | 638 | public static function jsonDecode(string $json, bool $assoc = \false, int $depth = 512, int $options = 0) |
| 553 | 639 | { |
| 640 | + \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.15', '%s() is deprecated and will be removed in 8.0. Use PHP\'s json_decode() instead.', __METHOD__); | |
| 554 | 641 | if ($depth < 1) { |
| 555 | 642 | throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('json_decode error: Maximum stack depth exceeded'); |
| 556 | 643 | } |
| 557 | 644 | $data = \json_decode($json, $assoc, $depth, $options); |
| @@ -569,11 +656,13 @@ | ||
| 569 | 656 | * |
| 570 | 657 | * @throws InvalidArgumentException if the JSON cannot be encoded. |
| 571 | 658 | * |
| 572 | 659 | * @see https://www.php.net/manual/en/function.json-encode.php |
| 660 | + * @deprecated Utils::jsonEncode() will be removed in guzzlehttp/guzzle:8.0. Use PHP's json_encode() instead. | |
| 573 | 661 | */ |
| 574 | 662 | public static function jsonEncode($value, int $options = 0, int $depth = 512) : string |
| 575 | 663 | { |
| 664 | + \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/guzzle', '7.15', '%s() is deprecated and will be removed in 8.0. Use PHP\'s json_encode() instead.', __METHOD__); | |
| 576 | 665 | $json = \json_encode($value, $options, $depth); |
| 577 | 666 | if (\JSON_ERROR_NONE !== \json_last_error()) { |
| 578 | 667 | throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('json_encode error: ' . \json_last_error_msg()); |
| 579 | 668 | } |