diff --git a/composer.json b/composer.json index 8784cc910..0723ad27c 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "egulias/email-validator": "^4.0.2", "fusonic/opengraph": "^2.3.0", "giggsey/libphonenumber-for-php-lite": "^8.13.12", - "guzzlehttp/guzzle": "^7.8.1", + "guzzlehttp/guzzle": "^7.9.2", "icewind/searchdav": "^3.1.0", "icewind/streams": "^0.7.7", "kornrunner/blurhash": "^1.2", diff --git a/composer.lock b/composer.lock index 7a2607334..d4d65b0bc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "77af81de2820f02808ad54a25fc1be22", + "content-hash": "fc7c1a89458a97e883bb9b4226d98297", "packages": [ { "name": "aws/aws-crt-php", @@ -1062,22 +1062,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.8.1", + "version": "7.9.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1088,9 +1088,9 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "guzzle/client-integration-tests": "3.0.2", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -1168,7 +1168,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" }, "funding": [ { @@ -1184,20 +1184,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:35:24+00:00" + "time": "2024-07-24T11:22:20+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", "shasum": "" }, "require": { @@ -1205,7 +1205,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "type": "library", "extra": { @@ -1251,7 +1251,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.2" + "source": "https://github.com/guzzle/promises/tree/2.0.3" }, "funding": [ { @@ -1267,20 +1267,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:19:20+00:00" + "time": "2024-07-18T10:29:17+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.6.2", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", "shasum": "" }, "require": { @@ -1295,8 +1295,8 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1367,7 +1367,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.2" + "source": "https://github.com/guzzle/psr7/tree/2.7.0" }, "funding": [ { @@ -1383,7 +1383,7 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:05:35+00:00" + "time": "2024-07-18T11:15:46+00:00" }, { "name": "guzzlehttp/uri-template", diff --git a/composer/installed.json b/composer/installed.json index bf374c0af..63c654785 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -1101,23 +1101,23 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.8.1", - "version_normalized": "7.8.1.0", + "version": "7.9.2", + "version_normalized": "7.9.2.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1128,9 +1128,9 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "guzzle/client-integration-tests": "3.0.2", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -1138,7 +1138,7 @@ "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, - "time": "2023-12-03T20:35:24+00:00", + "time": "2024-07-24T11:22:20+00:00", "type": "library", "extra": { "bamarni-bin": { @@ -1210,7 +1210,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" }, "funding": [ { @@ -1230,17 +1230,17 @@ }, { "name": "guzzlehttp/promises", - "version": "2.0.2", - "version_normalized": "2.0.2.0", + "version": "2.0.3", + "version_normalized": "2.0.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", "shasum": "" }, "require": { @@ -1248,9 +1248,9 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, - "time": "2023-12-03T20:19:20+00:00", + "time": "2024-07-18T10:29:17+00:00", "type": "library", "extra": { "bamarni-bin": { @@ -1296,7 +1296,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.2" + "source": "https://github.com/guzzle/promises/tree/2.0.3" }, "funding": [ { @@ -1316,17 +1316,17 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.6.2", - "version_normalized": "2.6.2.0", + "version": "2.7.0", + "version_normalized": "2.7.0.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", "shasum": "" }, "require": { @@ -1341,13 +1341,13 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, - "time": "2023-12-03T20:05:35+00:00", + "time": "2024-07-18T11:15:46+00:00", "type": "library", "extra": { "bamarni-bin": { @@ -1415,7 +1415,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.2" + "source": "https://github.com/guzzle/psr7/tree/2.7.0" }, "funding": [ { diff --git a/composer/installed.php b/composer/installed.php index 6295166bd..3dc76fa5c 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -146,27 +146,27 @@ 'dev_requirement' => false, ), 'guzzlehttp/guzzle' => array( - 'pretty_version' => '7.8.1', - 'version' => '7.8.1.0', - 'reference' => '41042bc7ab002487b876a0683fc8dce04ddce104', + 'pretty_version' => '7.9.2', + 'version' => '7.9.2.0', + 'reference' => 'd281ed313b989f213357e3be1a179f02196ac99b', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/guzzle', 'aliases' => array(), 'dev_requirement' => false, ), 'guzzlehttp/promises' => array( - 'pretty_version' => '2.0.2', - 'version' => '2.0.2.0', - 'reference' => 'bbff78d96034045e58e13dedd6ad91b5d1253223', + 'pretty_version' => '2.0.3', + 'version' => '2.0.3.0', + 'reference' => '6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/promises', 'aliases' => array(), 'dev_requirement' => false, ), 'guzzlehttp/psr7' => array( - 'pretty_version' => '2.6.2', - 'version' => '2.6.2.0', - 'reference' => '45b30f99ac27b5ca93cb4831afe16285f57b8221', + 'pretty_version' => '2.7.0', + 'version' => '2.7.0.0', + 'reference' => 'a70f5c95fb43bc83f07c9c948baa0dc1829bf201', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'aliases' => array(), diff --git a/guzzlehttp/guzzle/src/BodySummarizer.php b/guzzlehttp/guzzle/src/BodySummarizer.php index 6eca94ef9..761506dd0 100644 --- a/guzzlehttp/guzzle/src/BodySummarizer.php +++ b/guzzlehttp/guzzle/src/BodySummarizer.php @@ -11,7 +11,7 @@ final class BodySummarizer implements BodySummarizerInterface */ private $truncateAt; - public function __construct(int $truncateAt = null) + public function __construct(?int $truncateAt = null) { $this->truncateAt = $truncateAt; } @@ -22,7 +22,7 @@ public function __construct(int $truncateAt = null) public function summarize(MessageInterface $message): ?string { return $this->truncateAt === null - ? \GuzzleHttp\Psr7\Message::bodySummary($message) - : \GuzzleHttp\Psr7\Message::bodySummary($message, $this->truncateAt); + ? Psr7\Message::bodySummary($message) + : Psr7\Message::bodySummary($message, $this->truncateAt); } } diff --git a/guzzlehttp/guzzle/src/Client.php b/guzzlehttp/guzzle/src/Client.php index bc6efc90f..c78919a4f 100644 --- a/guzzlehttp/guzzle/src/Client.php +++ b/guzzlehttp/guzzle/src/Client.php @@ -52,7 +52,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface * * @param array $config Client configuration settings. * - * @see \GuzzleHttp\RequestOptions for a list of available request options. + * @see RequestOptions for a list of available request options. */ public function __construct(array $config = []) { @@ -202,7 +202,7 @@ public function request(string $method, $uri = '', array $options = []): Respons * * @deprecated Client::getConfig will be removed in guzzlehttp/guzzle:8.0. */ - public function getConfig(string $option = null) + public function getConfig(?string $option = null) { return $option === null ? $this->config diff --git a/guzzlehttp/guzzle/src/ClientInterface.php b/guzzlehttp/guzzle/src/ClientInterface.php index 1788e16ab..6aaee61af 100644 --- a/guzzlehttp/guzzle/src/ClientInterface.php +++ b/guzzlehttp/guzzle/src/ClientInterface.php @@ -80,5 +80,5 @@ public function requestAsync(string $method, $uri, array $options = []): Promise * * @deprecated ClientInterface::getConfig will be removed in guzzlehttp/guzzle:8.0. */ - public function getConfig(string $option = null); + public function getConfig(?string $option = null); } diff --git a/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/guzzlehttp/guzzle/src/Cookie/CookieJar.php index c29b4b7e9..b616cf2ed 100644 --- a/guzzlehttp/guzzle/src/Cookie/CookieJar.php +++ b/guzzlehttp/guzzle/src/Cookie/CookieJar.php @@ -103,7 +103,7 @@ public function toArray(): array }, $this->getIterator()->getArrayCopy()); } - public function clear(string $domain = null, string $path = null, string $name = null): void + public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void { if (!$domain) { $this->cookies = []; diff --git a/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php index 8c55cc6f7..93ada58d2 100644 --- a/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php +++ b/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php @@ -62,7 +62,7 @@ public function setCookie(SetCookie $cookie): bool; * @param string|null $path Clears cookies matching a domain and path * @param string|null $name Clears cookies matching a domain, path, and name */ - public function clear(string $domain = null, string $path = null, string $name = null): void; + public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void; /** * Discard all sessions cookies. diff --git a/guzzlehttp/guzzle/src/Exception/BadResponseException.php b/guzzlehttp/guzzle/src/Exception/BadResponseException.php index a80956c9d..ba67ad498 100644 --- a/guzzlehttp/guzzle/src/Exception/BadResponseException.php +++ b/guzzlehttp/guzzle/src/Exception/BadResponseException.php @@ -14,7 +14,7 @@ public function __construct( string $message, RequestInterface $request, ResponseInterface $response, - \Throwable $previous = null, + ?\Throwable $previous = null, array $handlerContext = [] ) { parent::__construct($message, $request, $response, $previous, $handlerContext); diff --git a/guzzlehttp/guzzle/src/Exception/ConnectException.php b/guzzlehttp/guzzle/src/Exception/ConnectException.php index e1a31519c..eab51ca17 100644 --- a/guzzlehttp/guzzle/src/Exception/ConnectException.php +++ b/guzzlehttp/guzzle/src/Exception/ConnectException.php @@ -25,7 +25,7 @@ class ConnectException extends TransferException implements NetworkExceptionInte public function __construct( string $message, RequestInterface $request, - \Throwable $previous = null, + ?\Throwable $previous = null, array $handlerContext = [] ) { parent::__construct($message, 0, $previous); diff --git a/guzzlehttp/guzzle/src/Exception/RequestException.php b/guzzlehttp/guzzle/src/Exception/RequestException.php index c2d0a9ccc..b42c88abf 100644 --- a/guzzlehttp/guzzle/src/Exception/RequestException.php +++ b/guzzlehttp/guzzle/src/Exception/RequestException.php @@ -7,7 +7,6 @@ use Psr\Http\Client\RequestExceptionInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\UriInterface; /** * HTTP Request exception @@ -32,8 +31,8 @@ class RequestException extends TransferException implements RequestExceptionInte public function __construct( string $message, RequestInterface $request, - ResponseInterface $response = null, - \Throwable $previous = null, + ?ResponseInterface $response = null, + ?\Throwable $previous = null, array $handlerContext = [] ) { // Set the code of the exception if the response is set and not future. @@ -63,10 +62,10 @@ public static function wrapException(RequestInterface $request, \Throwable $e): */ public static function create( RequestInterface $request, - ResponseInterface $response = null, - \Throwable $previous = null, + ?ResponseInterface $response = null, + ?\Throwable $previous = null, array $handlerContext = [], - BodySummarizerInterface $bodySummarizer = null + ?BodySummarizerInterface $bodySummarizer = null ): self { if (!$response) { return new self( @@ -90,8 +89,7 @@ public static function create( $className = __CLASS__; } - $uri = $request->getUri(); - $uri = static::obfuscateUri($uri); + $uri = \GuzzleHttp\Psr7\Utils::redactUserInfo($request->getUri()); // Client Error: `GET /` resulted in a `404 Not Found` response: // ... (truncated) @@ -113,20 +111,6 @@ public static function create( return new $className($message, $request, $response, $previous, $handlerContext); } - /** - * Obfuscates URI if there is a username and a password present - */ - private static function obfuscateUri(UriInterface $uri): UriInterface - { - $userInfo = $uri->getUserInfo(); - - if (false !== ($pos = \strpos($userInfo, ':'))) { - return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***'); - } - - return $uri; - } - /** * Get the request that caused the exception */ diff --git a/guzzlehttp/guzzle/src/Handler/CurlFactory.php b/guzzlehttp/guzzle/src/Handler/CurlFactory.php index 16a942232..fe3613751 100644 --- a/guzzlehttp/guzzle/src/Handler/CurlFactory.php +++ b/guzzlehttp/guzzle/src/Handler/CurlFactory.php @@ -11,6 +11,7 @@ use GuzzleHttp\TransferStats; use GuzzleHttp\Utils; use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\UriInterface; /** * Creates curl resources from a request @@ -46,6 +47,16 @@ public function __construct(int $maxHandles) public function create(RequestInterface $request, array $options): EasyHandle { + $protocolVersion = $request->getProtocolVersion(); + + if ('2' === $protocolVersion || '2.0' === $protocolVersion) { + if (!self::supportsHttp2()) { + throw new ConnectException('HTTP/2 is supported by the cURL handler, however libcurl is built without HTTP/2 support.', $request); + } + } elseif ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) { + throw new ConnectException(sprintf('HTTP/%s is not supported by the cURL handler.', $protocolVersion), $request); + } + if (isset($options['curl']['body_as_string'])) { $options['_body_as_string'] = $options['curl']['body_as_string']; unset($options['curl']['body_as_string']); @@ -72,6 +83,42 @@ public function create(RequestInterface $request, array $options): EasyHandle return $easy; } + private static function supportsHttp2(): bool + { + static $supportsHttp2 = null; + + if (null === $supportsHttp2) { + $supportsHttp2 = self::supportsTls12() + && defined('CURL_VERSION_HTTP2') + && (\CURL_VERSION_HTTP2 & \curl_version()['features']); + } + + return $supportsHttp2; + } + + private static function supportsTls12(): bool + { + static $supportsTls12 = null; + + if (null === $supportsTls12) { + $supportsTls12 = \CURL_SSLVERSION_TLSv1_2 & \curl_version()['features']; + } + + return $supportsTls12; + } + + private static function supportsTls13(): bool + { + static $supportsTls13 = null; + + if (null === $supportsTls13) { + $supportsTls13 = defined('CURL_SSLVERSION_TLSv1_3') + && (\CURL_SSLVERSION_TLSv1_3 & \curl_version()['features']); + } + + return $supportsTls13; + } + public function release(EasyHandle $easy): void { $resource = $easy->handle; @@ -147,7 +194,7 @@ private static function finishError(callable $handler, EasyHandle $easy, CurlFac 'error' => \curl_error($easy->handle), 'appconnect_time' => \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME), ] + \curl_getinfo($easy->handle); - $ctx[self::CURL_VERSION_STR] = \curl_version()['version']; + $ctx[self::CURL_VERSION_STR] = self::getCurlVersion(); $factory->release($easy); // Retry when nothing is present or when curl failed to rewind. @@ -158,6 +205,17 @@ private static function finishError(callable $handler, EasyHandle $easy, CurlFac return self::createRejection($easy, $ctx); } + private static function getCurlVersion(): string + { + static $curlVersion = null; + + if (null === $curlVersion) { + $curlVersion = \curl_version()['version']; + } + + return $curlVersion; + } + private static function createRejection(EasyHandle $easy, array $ctx): PromiseInterface { static $connectionErrors = [ @@ -194,15 +252,22 @@ private static function createRejection(EasyHandle $easy, array $ctx): PromiseIn ); } + $uri = $easy->request->getUri(); + + $sanitizedError = self::sanitizeCurlError($ctx['error'] ?? '', $uri); + $message = \sprintf( 'cURL error %s: %s (%s)', $ctx['errno'], - $ctx['error'], + $sanitizedError, 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html' ); - $uriString = (string) $easy->request->getUri(); - if ($uriString !== '' && false === \strpos($ctx['error'], $uriString)) { - $message .= \sprintf(' for %s', $uriString); + + if ('' !== $sanitizedError) { + $redactedUriString = \GuzzleHttp\Psr7\Utils::redactUserInfo($uri)->__toString(); + if ($redactedUriString !== '' && false === \strpos($sanitizedError, $redactedUriString)) { + $message .= \sprintf(' for %s', $redactedUriString); + } } // Create a connection exception if it was a specific error code. @@ -213,6 +278,24 @@ private static function createRejection(EasyHandle $easy, array $ctx): PromiseIn return P\Create::rejectionFor($error); } + private static function sanitizeCurlError(string $error, UriInterface $uri): string + { + if ('' === $error) { + return $error; + } + + $baseUri = $uri->withQuery('')->withFragment(''); + $baseUriString = $baseUri->__toString(); + + if ('' === $baseUriString) { + return $error; + } + + $redactedUriString = \GuzzleHttp\Psr7\Utils::redactUserInfo($baseUri)->__toString(); + + return str_replace($baseUriString, $redactedUriString, $error); + } + /** * @return array */ @@ -232,10 +315,11 @@ private function getDefaultConf(EasyHandle $easy): array } $version = $easy->request->getProtocolVersion(); - if ($version == 1.1) { - $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1; - } elseif ($version == 2.0) { + + if ('2' === $version || '2.0' === $version) { $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0; + } elseif ('1.1' === $version) { + $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1; } else { $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_0; } @@ -390,8 +474,10 @@ private function applyHandlerOptions(EasyHandle $easy, array &$conf): void // The empty string enables all available decoders and implicitly // sets a matching 'Accept-Encoding' header. $conf[\CURLOPT_ENCODING] = ''; - // But as the user did not specify any acceptable encodings we need - // to overwrite this implicit header with an empty one. + // But as the user did not specify any encoding preference, + // let's leave it up to server by preventing curl from sending + // the header, which will be interpreted as 'Accept-Encoding: *'. + // https://www.rfc-editor.org/rfc/rfc9110#field.accept-encoding $conf[\CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; } } @@ -455,23 +541,35 @@ private function applyHandlerOptions(EasyHandle $easy, array &$conf): void } if (isset($options['crypto_method'])) { - if (\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']) { - if (!defined('CURL_SSLVERSION_TLSv1_0')) { - throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.0 not supported by your version of cURL'); + $protocolVersion = $easy->request->getProtocolVersion(); + + // If HTTP/2, upgrade TLS 1.0 and 1.1 to 1.2 + if ('2' === $protocolVersion || '2.0' === $protocolVersion) { + if ( + \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method'] + || \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method'] + || \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method'] + ) { + $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2; + } elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) { + if (!self::supportsTls13()) { + throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL'); + } + $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3; + } else { + throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); } + } elseif (\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']) { $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_0; } elseif (\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']) { - if (!defined('CURL_SSLVERSION_TLSv1_1')) { - throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.1 not supported by your version of cURL'); - } $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_1; } elseif (\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']) { - if (!defined('CURL_SSLVERSION_TLSv1_2')) { + if (!self::supportsTls12()) { throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.2 not supported by your version of cURL'); } $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2; } elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) { - if (!defined('CURL_SSLVERSION_TLSv1_3')) { + if (!self::supportsTls13()) { throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL'); } $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3; diff --git a/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php b/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php index a64e1821a..73a6abe33 100644 --- a/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php +++ b/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php @@ -2,6 +2,7 @@ namespace GuzzleHttp\Handler; +use Closure; use GuzzleHttp\Promise as P; use GuzzleHttp\Promise\Promise; use GuzzleHttp\Promise\PromiseInterface; @@ -159,6 +160,9 @@ public function tick(): void } } + // Run curl_multi_exec in the queue to enable other async tasks to run + P\Utils::queue()->add(Closure::fromCallable([$this, 'tickInQueue'])); + // Step through the task queue which may add additional requests. P\Utils::queue()->run(); @@ -169,11 +173,24 @@ public function tick(): void } while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) { + // Prevent busy looping for slow HTTP requests. + \curl_multi_select($this->_mh, $this->selectTimeout); } $this->processMessages(); } + /** + * Runs \curl_multi_exec() inside the event loop, to prevent busy looping + */ + private function tickInQueue(): void + { + if (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) { + \curl_multi_select($this->_mh, 0); + P\Utils::queue()->add(Closure::fromCallable([$this, 'tickInQueue'])); + } + } + /** * Runs until all outstanding connections have completed. */ diff --git a/guzzlehttp/guzzle/src/Handler/MockHandler.php b/guzzlehttp/guzzle/src/Handler/MockHandler.php index 77ffed521..3ecd5964d 100644 --- a/guzzlehttp/guzzle/src/Handler/MockHandler.php +++ b/guzzlehttp/guzzle/src/Handler/MockHandler.php @@ -52,21 +52,21 @@ class MockHandler implements \Countable * @param callable|null $onFulfilled Callback to invoke when the return value is fulfilled. * @param callable|null $onRejected Callback to invoke when the return value is rejected. */ - public static function createWithMiddleware(array $queue = null, callable $onFulfilled = null, callable $onRejected = null): HandlerStack + public static function createWithMiddleware(?array $queue = null, ?callable $onFulfilled = null, ?callable $onRejected = null): HandlerStack { return HandlerStack::create(new self($queue, $onFulfilled, $onRejected)); } /** * The passed in value must be an array of - * {@see \Psr\Http\Message\ResponseInterface} objects, Exceptions, + * {@see ResponseInterface} objects, Exceptions, * callables, or Promises. * * @param array|null $queue The parameters to be passed to the append function, as an indexed array. * @param callable|null $onFulfilled Callback to invoke when the return value is fulfilled. * @param callable|null $onRejected Callback to invoke when the return value is rejected. */ - public function __construct(array $queue = null, callable $onFulfilled = null, callable $onRejected = null) + public function __construct(?array $queue = null, ?callable $onFulfilled = null, ?callable $onRejected = null) { $this->onFulfilled = $onFulfilled; $this->onRejected = $onRejected; @@ -200,7 +200,7 @@ public function reset(): void private function invokeStats( RequestInterface $request, array $options, - ResponseInterface $response = null, + ?ResponseInterface $response = null, $reason = null ): void { if (isset($options['on_stats'])) { diff --git a/guzzlehttp/guzzle/src/Handler/StreamHandler.php b/guzzlehttp/guzzle/src/Handler/StreamHandler.php index 61632f564..1d89a8fbc 100644 --- a/guzzlehttp/guzzle/src/Handler/StreamHandler.php +++ b/guzzlehttp/guzzle/src/Handler/StreamHandler.php @@ -40,6 +40,12 @@ public function __invoke(RequestInterface $request, array $options): PromiseInte \usleep($options['delay'] * 1000); } + $protocolVersion = $request->getProtocolVersion(); + + if ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) { + throw new ConnectException(sprintf('HTTP/%s is not supported by the stream handler.', $protocolVersion), $request); + } + $startTime = isset($options['on_stats']) ? Utils::currentTime() : null; try { @@ -83,8 +89,8 @@ private function invokeStats( array $options, RequestInterface $request, ?float $startTime, - ResponseInterface $response = null, - \Throwable $error = null + ?ResponseInterface $response = null, + ?\Throwable $error = null ): void { if (isset($options['on_stats'])) { $stats = new TransferStats($request, $response, Utils::currentTime() - $startTime, $error, []); @@ -273,7 +279,7 @@ private function createStream(RequestInterface $request, array $options) // HTTP/1.1 streams using the PHP stream wrapper require a // Connection: close header - if ($request->getProtocolVersion() == '1.1' + if ($request->getProtocolVersion() === '1.1' && !$request->hasHeader('Connection') ) { $request = $request->withHeader('Connection', 'close'); diff --git a/guzzlehttp/guzzle/src/HandlerStack.php b/guzzlehttp/guzzle/src/HandlerStack.php index 6cb12f07a..03f9a18ff 100644 --- a/guzzlehttp/guzzle/src/HandlerStack.php +++ b/guzzlehttp/guzzle/src/HandlerStack.php @@ -44,7 +44,7 @@ class HandlerStack * handler is provided, the best handler for your * system will be utilized. */ - public static function create(callable $handler = null): self + public static function create(?callable $handler = null): self { $stack = new self($handler ?: Utils::chooseHandler()); $stack->push(Middleware::httpErrors(), 'http_errors'); @@ -58,7 +58,7 @@ public static function create(callable $handler = null): self /** * @param (callable(RequestInterface, array): PromiseInterface)|null $handler Underlying HTTP handler. */ - public function __construct(callable $handler = null) + public function __construct(?callable $handler = null) { $this->handler = $handler; } @@ -131,7 +131,7 @@ public function hasHandler(): bool * @param callable(callable): callable $middleware Middleware function * @param string $name Name to register for this middleware. */ - public function unshift(callable $middleware, string $name = null): void + public function unshift(callable $middleware, ?string $name = null): void { \array_unshift($this->stack, [$middleware, $name]); $this->cached = null; diff --git a/guzzlehttp/guzzle/src/MessageFormatter.php b/guzzlehttp/guzzle/src/MessageFormatter.php index 04e9eb37a..9b77eee83 100644 --- a/guzzlehttp/guzzle/src/MessageFormatter.php +++ b/guzzlehttp/guzzle/src/MessageFormatter.php @@ -68,7 +68,7 @@ public function __construct(?string $template = self::CLF) * @param ResponseInterface|null $response Response that was received * @param \Throwable|null $error Exception that was received */ - public function format(RequestInterface $request, ResponseInterface $response = null, \Throwable $error = null): string + public function format(RequestInterface $request, ?ResponseInterface $response = null, ?\Throwable $error = null): string { $cache = []; diff --git a/guzzlehttp/guzzle/src/MessageFormatterInterface.php b/guzzlehttp/guzzle/src/MessageFormatterInterface.php index 47934614a..a39ac248e 100644 --- a/guzzlehttp/guzzle/src/MessageFormatterInterface.php +++ b/guzzlehttp/guzzle/src/MessageFormatterInterface.php @@ -14,5 +14,5 @@ interface MessageFormatterInterface * @param ResponseInterface|null $response Response that was received * @param \Throwable|null $error Exception that was received */ - public function format(RequestInterface $request, ResponseInterface $response = null, \Throwable $error = null): string; + public function format(RequestInterface $request, ?ResponseInterface $response = null, ?\Throwable $error = null): string; } diff --git a/guzzlehttp/guzzle/src/Middleware.php b/guzzlehttp/guzzle/src/Middleware.php index 7e3eb6b3a..6edbb3fe4 100644 --- a/guzzlehttp/guzzle/src/Middleware.php +++ b/guzzlehttp/guzzle/src/Middleware.php @@ -55,7 +55,7 @@ static function (ResponseInterface $response) use ($cookieJar, $request): Respon * * @return callable(callable): callable Returns a function that accepts the next handler. */ - public static function httpErrors(BodySummarizerInterface $bodySummarizer = null): callable + public static function httpErrors(?BodySummarizerInterface $bodySummarizer = null): callable { return static function (callable $handler) use ($bodySummarizer): callable { return static function ($request, array $options) use ($handler, $bodySummarizer) { @@ -132,7 +132,7 @@ static function ($reason) use ($request, &$container, $options) { * * @return callable Returns a function that accepts the next handler. */ - public static function tap(callable $before = null, callable $after = null): callable + public static function tap(?callable $before = null, ?callable $after = null): callable { return static function (callable $handler) use ($before, $after): callable { return static function (RequestInterface $request, array $options) use ($handler, $before, $after) { @@ -176,7 +176,7 @@ public static function redirect(): callable * * @return callable Returns a function that accepts the next handler. */ - public static function retry(callable $decider, callable $delay = null): callable + public static function retry(callable $decider, ?callable $delay = null): callable { return static function (callable $handler) use ($decider, $delay): RetryMiddleware { return new RetryMiddleware($decider, $handler, $delay); diff --git a/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php b/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php index 0a8de8128..7dde6c5f4 100644 --- a/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php +++ b/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php @@ -76,8 +76,8 @@ private function addExpectHeader(RequestInterface $request, array $options, arra $expect = $options['expect'] ?? null; - // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0 - if ($expect === false || $request->getProtocolVersion() < 1.1) { + // Return if disabled or using HTTP/1.0 + if ($expect === false || $request->getProtocolVersion() === '1.0') { return; } diff --git a/guzzlehttp/guzzle/src/RequestOptions.php b/guzzlehttp/guzzle/src/RequestOptions.php index a38768c0c..84a3500e4 100644 --- a/guzzlehttp/guzzle/src/RequestOptions.php +++ b/guzzlehttp/guzzle/src/RequestOptions.php @@ -61,7 +61,7 @@ final class RequestOptions * Specifies whether or not cookies are used in a request or what cookie * jar to use or what cookies to send. This option only works if your * handler has the `cookie` middleware. Valid values are `false` and - * an instance of {@see \GuzzleHttp\Cookie\CookieJarInterface}. + * an instance of {@see Cookie\CookieJarInterface}. */ public const COOKIES = 'cookies'; diff --git a/guzzlehttp/guzzle/src/RetryMiddleware.php b/guzzlehttp/guzzle/src/RetryMiddleware.php index 8f4d93ac4..65f49cb75 100644 --- a/guzzlehttp/guzzle/src/RetryMiddleware.php +++ b/guzzlehttp/guzzle/src/RetryMiddleware.php @@ -40,7 +40,7 @@ class RetryMiddleware * and returns the number of * milliseconds to delay. */ - public function __construct(callable $decider, callable $nextHandler, callable $delay = null) + public function __construct(callable $decider, callable $nextHandler, ?callable $delay = null) { $this->decider = $decider; $this->nextHandler = $nextHandler; @@ -110,7 +110,7 @@ private function onRejected(RequestInterface $req, array $options): callable }; } - private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null): PromiseInterface + private function doRetry(RequestInterface $request, array $options, ?ResponseInterface $response = null): PromiseInterface { $options['delay'] = ($this->delay)(++$options['retries'], $response, $request); diff --git a/guzzlehttp/guzzle/src/TransferStats.php b/guzzlehttp/guzzle/src/TransferStats.php index 2ce9e38f2..93fa334c8 100644 --- a/guzzlehttp/guzzle/src/TransferStats.php +++ b/guzzlehttp/guzzle/src/TransferStats.php @@ -46,8 +46,8 @@ final class TransferStats */ public function __construct( RequestInterface $request, - ResponseInterface $response = null, - float $transferTime = null, + ?ResponseInterface $response = null, + ?float $transferTime = null, $handlerErrorData = null, array $handlerStats = [] ) { diff --git a/guzzlehttp/guzzle/src/Utils.php b/guzzlehttp/guzzle/src/Utils.php index 93d6d39cd..df529270e 100644 --- a/guzzlehttp/guzzle/src/Utils.php +++ b/guzzlehttp/guzzle/src/Utils.php @@ -71,7 +71,7 @@ public static function debugResource($value = null) return \STDOUT; } - return \GuzzleHttp\Psr7\Utils::tryFopen('php://output', 'w'); + return Psr7\Utils::tryFopen('php://output', 'w'); } /** @@ -87,7 +87,7 @@ public static function chooseHandler(): callable { $handler = null; - if (\defined('CURLOPT_CUSTOMREQUEST')) { + if (\defined('CURLOPT_CUSTOMREQUEST') && \function_exists('curl_version') && version_compare(curl_version()['version'], '7.21.2') >= 0) { if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) { $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler()); } elseif (\function_exists('curl_exec')) { diff --git a/guzzlehttp/promises/src/Coroutine.php b/guzzlehttp/promises/src/Coroutine.php index 0b5b9c0a4..0da022834 100644 --- a/guzzlehttp/promises/src/Coroutine.php +++ b/guzzlehttp/promises/src/Coroutine.php @@ -84,8 +84,8 @@ public static function of(callable $generatorFn): self } public function then( - callable $onFulfilled = null, - callable $onRejected = null + ?callable $onFulfilled = null, + ?callable $onRejected = null ): PromiseInterface { return $this->result->then($onFulfilled, $onRejected); } diff --git a/guzzlehttp/promises/src/Each.php b/guzzlehttp/promises/src/Each.php index c09d23c60..dd72c8310 100644 --- a/guzzlehttp/promises/src/Each.php +++ b/guzzlehttp/promises/src/Each.php @@ -23,8 +23,8 @@ final class Each */ public static function of( $iterable, - callable $onFulfilled = null, - callable $onRejected = null + ?callable $onFulfilled = null, + ?callable $onRejected = null ): PromiseInterface { return (new EachPromise($iterable, [ 'fulfilled' => $onFulfilled, @@ -46,8 +46,8 @@ public static function of( public static function ofLimit( $iterable, $concurrency, - callable $onFulfilled = null, - callable $onRejected = null + ?callable $onFulfilled = null, + ?callable $onRejected = null ): PromiseInterface { return (new EachPromise($iterable, [ 'fulfilled' => $onFulfilled, @@ -67,7 +67,7 @@ public static function ofLimit( public static function ofLimitAll( $iterable, $concurrency, - callable $onFulfilled = null + ?callable $onFulfilled = null ): PromiseInterface { return self::ofLimit( $iterable, diff --git a/guzzlehttp/promises/src/FulfilledPromise.php b/guzzlehttp/promises/src/FulfilledPromise.php index ab7129659..727ec315c 100644 --- a/guzzlehttp/promises/src/FulfilledPromise.php +++ b/guzzlehttp/promises/src/FulfilledPromise.php @@ -31,8 +31,8 @@ public function __construct($value) } public function then( - callable $onFulfilled = null, - callable $onRejected = null + ?callable $onFulfilled = null, + ?callable $onRejected = null ): PromiseInterface { // Return itself if there is no onFulfilled function. if (!$onFulfilled) { diff --git a/guzzlehttp/promises/src/Promise.php b/guzzlehttp/promises/src/Promise.php index 1b07bdc9a..c0c5be2c0 100644 --- a/guzzlehttp/promises/src/Promise.php +++ b/guzzlehttp/promises/src/Promise.php @@ -25,16 +25,16 @@ class Promise implements PromiseInterface * @param callable $cancelFn Fn that when invoked cancels the promise. */ public function __construct( - callable $waitFn = null, - callable $cancelFn = null + ?callable $waitFn = null, + ?callable $cancelFn = null ) { $this->waitFn = $waitFn; $this->cancelFn = $cancelFn; } public function then( - callable $onFulfilled = null, - callable $onRejected = null + ?callable $onFulfilled = null, + ?callable $onRejected = null ): PromiseInterface { if ($this->state === self::PENDING) { $p = new Promise(null, [$this, 'cancel']); diff --git a/guzzlehttp/promises/src/PromiseInterface.php b/guzzlehttp/promises/src/PromiseInterface.php index 2824802bb..c11721e4d 100644 --- a/guzzlehttp/promises/src/PromiseInterface.php +++ b/guzzlehttp/promises/src/PromiseInterface.php @@ -27,8 +27,8 @@ interface PromiseInterface * @param callable $onRejected Invoked when the promise is rejected. */ public function then( - callable $onFulfilled = null, - callable $onRejected = null + ?callable $onFulfilled = null, + ?callable $onRejected = null ): PromiseInterface; /** diff --git a/guzzlehttp/promises/src/RejectedPromise.php b/guzzlehttp/promises/src/RejectedPromise.php index d947da1f5..1ebf0b2a6 100644 --- a/guzzlehttp/promises/src/RejectedPromise.php +++ b/guzzlehttp/promises/src/RejectedPromise.php @@ -31,8 +31,8 @@ public function __construct($reason) } public function then( - callable $onFulfilled = null, - callable $onRejected = null + ?callable $onFulfilled = null, + ?callable $onRejected = null ): PromiseInterface { // If there's no onRejected callback then just return self. if (!$onRejected) { diff --git a/guzzlehttp/promises/src/RejectionException.php b/guzzlehttp/promises/src/RejectionException.php index 72a81ba20..47dca8624 100644 --- a/guzzlehttp/promises/src/RejectionException.php +++ b/guzzlehttp/promises/src/RejectionException.php @@ -18,7 +18,7 @@ class RejectionException extends \RuntimeException * @param mixed $reason Rejection reason. * @param string|null $description Optional description. */ - public function __construct($reason, string $description = null) + public function __construct($reason, ?string $description = null) { $this->reason = $reason; diff --git a/guzzlehttp/promises/src/Utils.php b/guzzlehttp/promises/src/Utils.php index e1570d727..45b0893fc 100644 --- a/guzzlehttp/promises/src/Utils.php +++ b/guzzlehttp/promises/src/Utils.php @@ -21,7 +21,7 @@ final class Utils * * @param TaskQueueInterface|null $assign Optionally specify a new queue instance. */ - public static function queue(TaskQueueInterface $assign = null): TaskQueueInterface + public static function queue(?TaskQueueInterface $assign = null): TaskQueueInterface { static $queue; diff --git a/guzzlehttp/psr7/src/CachingStream.php b/guzzlehttp/psr7/src/CachingStream.php index f34722cff..7e4554d5c 100644 --- a/guzzlehttp/psr7/src/CachingStream.php +++ b/guzzlehttp/psr7/src/CachingStream.php @@ -33,7 +33,7 @@ final class CachingStream implements StreamInterface */ public function __construct( StreamInterface $stream, - StreamInterface $target = null + ?StreamInterface $target = null ) { $this->remoteStream = $stream; $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+')); diff --git a/guzzlehttp/psr7/src/HttpFactory.php b/guzzlehttp/psr7/src/HttpFactory.php index 73d17e337..3ef15103a 100644 --- a/guzzlehttp/psr7/src/HttpFactory.php +++ b/guzzlehttp/psr7/src/HttpFactory.php @@ -27,10 +27,10 @@ final class HttpFactory implements RequestFactoryInterface, ResponseFactoryInter { public function createUploadedFile( StreamInterface $stream, - int $size = null, + ?int $size = null, int $error = \UPLOAD_ERR_OK, - string $clientFilename = null, - string $clientMediaType = null + ?string $clientFilename = null, + ?string $clientMediaType = null ): UploadedFileInterface { if ($size === null) { $size = $stream->getSize(); diff --git a/guzzlehttp/psr7/src/MultipartStream.php b/guzzlehttp/psr7/src/MultipartStream.php index d23fba8a3..43d718f65 100644 --- a/guzzlehttp/psr7/src/MultipartStream.php +++ b/guzzlehttp/psr7/src/MultipartStream.php @@ -32,7 +32,7 @@ final class MultipartStream implements StreamInterface * * @throws \InvalidArgumentException */ - public function __construct(array $elements = [], string $boundary = null) + public function __construct(array $elements = [], ?string $boundary = null) { $this->boundary = $boundary ?: bin2hex(random_bytes(20)); $this->stream = $this->createStream($elements); diff --git a/guzzlehttp/psr7/src/Query.php b/guzzlehttp/psr7/src/Query.php index 8b9492797..ccf867a0b 100644 --- a/guzzlehttp/psr7/src/Query.php +++ b/guzzlehttp/psr7/src/Query.php @@ -63,12 +63,15 @@ public static function parse(string $str, $urlEncoding = true): array * string. This function does not modify the provided keys when an array is * encountered (like `http_build_query()` would). * - * @param array $params Query string parameters. - * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986 - * to encode using RFC3986, or PHP_QUERY_RFC1738 - * to encode using RFC1738. + * @param array $params Query string parameters. + * @param int|false $encoding Set to false to not encode, + * PHP_QUERY_RFC3986 to encode using + * RFC3986, or PHP_QUERY_RFC1738 to + * encode using RFC1738. + * @param bool $treatBoolsAsInts Set to true to encode as 0/1, and + * false as false/true. */ - public static function build(array $params, $encoding = PHP_QUERY_RFC3986): string + public static function build(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string { if (!$params) { return ''; @@ -86,12 +89,14 @@ public static function build(array $params, $encoding = PHP_QUERY_RFC3986): stri throw new \InvalidArgumentException('Invalid type'); } + $castBool = $treatBoolsAsInts ? static function ($v) { return (int) $v; } : static function ($v) { return $v ? 'true' : 'false'; }; + $qs = ''; foreach ($params as $k => $v) { $k = $encoder((string) $k); if (!is_array($v)) { $qs .= $k; - $v = is_bool($v) ? (int) $v : $v; + $v = is_bool($v) ? $castBool($v) : $v; if ($v !== null) { $qs .= '='.$encoder((string) $v); } @@ -99,7 +104,7 @@ public static function build(array $params, $encoding = PHP_QUERY_RFC3986): stri } else { foreach ($v as $vv) { $qs .= $k; - $vv = is_bool($vv) ? (int) $vv : $vv; + $vv = is_bool($vv) ? $castBool($vv) : $vv; if ($vv !== null) { $qs .= '='.$encoder((string) $vv); } diff --git a/guzzlehttp/psr7/src/Response.php b/guzzlehttp/psr7/src/Response.php index 00f16e2d9..34e612fda 100644 --- a/guzzlehttp/psr7/src/Response.php +++ b/guzzlehttp/psr7/src/Response.php @@ -96,7 +96,7 @@ public function __construct( array $headers = [], $body = null, string $version = '1.1', - string $reason = null + ?string $reason = null ) { $this->assertStatusCodeRange($status); diff --git a/guzzlehttp/psr7/src/StreamWrapper.php b/guzzlehttp/psr7/src/StreamWrapper.php index ae8538814..77b04d747 100644 --- a/guzzlehttp/psr7/src/StreamWrapper.php +++ b/guzzlehttp/psr7/src/StreamWrapper.php @@ -69,7 +69,7 @@ public static function register(): void } } - public function stream_open(string $path, string $mode, int $options, string &$opened_path = null): bool + public function stream_open(string $path, string $mode, int $options, ?string &$opened_path = null): bool { $options = stream_context_get_options($this->context); @@ -136,10 +136,14 @@ public function stream_cast(int $cast_as) * ctime: int, * blksize: int, * blocks: int - * } + * }|false */ - public function stream_stat(): array + public function stream_stat() { + if ($this->stream->getSize() === null) { + return false; + } + static $modeMap = [ 'r' => 33060, 'rb' => 33060, diff --git a/guzzlehttp/psr7/src/UploadedFile.php b/guzzlehttp/psr7/src/UploadedFile.php index b26719937..9c9ea49fb 100644 --- a/guzzlehttp/psr7/src/UploadedFile.php +++ b/guzzlehttp/psr7/src/UploadedFile.php @@ -64,8 +64,8 @@ public function __construct( $streamOrFile, ?int $size, int $errorStatus, - string $clientFilename = null, - string $clientMediaType = null + ?string $clientFilename = null, + ?string $clientMediaType = null ) { $this->setError($errorStatus); $this->size = $size; diff --git a/guzzlehttp/psr7/src/Uri.php b/guzzlehttp/psr7/src/Uri.php index f1feee871..481dfca94 100644 --- a/guzzlehttp/psr7/src/Uri.php +++ b/guzzlehttp/psr7/src/Uri.php @@ -279,7 +279,7 @@ public static function isRelativePathReference(UriInterface $uri): bool * * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.4 */ - public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool + public static function isSameDocumentReference(UriInterface $uri, ?UriInterface $base = null): bool { if ($base !== null) { $uri = UriResolver::resolve($base, $uri); diff --git a/guzzlehttp/psr7/src/Utils.php b/guzzlehttp/psr7/src/Utils.php index bf5ea9dba..7682d2cdc 100644 --- a/guzzlehttp/psr7/src/Utils.php +++ b/guzzlehttp/psr7/src/Utils.php @@ -231,7 +231,7 @@ public static function modifyRequest(RequestInterface $request, array $changes): * @param StreamInterface $stream Stream to read from * @param int|null $maxLength Maximum buffer length */ - public static function readLine(StreamInterface $stream, int $maxLength = null): string + public static function readLine(StreamInterface $stream, ?int $maxLength = null): string { $buffer = ''; $size = 0; @@ -250,6 +250,20 @@ public static function readLine(StreamInterface $stream, int $maxLength = null): return $buffer; } + /** + * Redact the password in the user info part of a URI. + */ + public static function redactUserInfo(UriInterface $uri): UriInterface + { + $userInfo = $uri->getUserInfo(); + + if (false !== ($pos = \strpos($userInfo, ':'))) { + return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***'); + } + + return $uri; + } + /** * Create a new stream based on the input type. *