"dragonmantank/cron-expression": "^3.3.3",
"erusev/parsedown": "^1.7.4",
"ezyang/htmlpurifier": "^4.17",
- "guzzlehttp/guzzle": "^7.8.1",
- "guzzlehttp/psr7": "^2.6.2",
+ "guzzlehttp/guzzle": "^7.9.2",
+ "guzzlehttp/psr7": "^2.7.0",
"laminas/laminas-diactoros": "^3.3.1",
"laminas/laminas-httphandlerrunner": "^2.10.0",
"laminas/laminas-progressbar": "^2.13",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "f252359a254921f59dc57b78f2c04059",
+ "content-hash": "2c277af11fe355813745d14d0db85a20",
"packages": [
{
"name": "cuyz/valinor",
},
{
"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"
"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": {
],
"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": [
{
"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": {
},
"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": {
],
"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": [
{
"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": {
},
"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"
],
"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": [
{
"type": "tidelift"
}
],
- "time": "2023-12-03T20:05:35+00:00"
+ "time": "2024-07-18T11:15:46+00:00"
},
{
"name": "laminas/laminas-diactoros",
},
{
"name": "sabberworm/php-css-parser",
- "version": "v8.5.1",
+ "version": "v8.6.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
- "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152"
+ "reference": "d2fb94a9641be84d79c7548c6d39bbebba6e9a70"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/4a3d572b0f8b28bb6fd016ae8bbfc445facef152",
- "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152",
+ "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d2fb94a9641be84d79c7548c6d39bbebba6e9a70",
+ "reference": "d2fb94a9641be84d79c7548c6d39bbebba6e9a70",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
- "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.5.1"
+ "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.6.0"
},
- "time": "2024-02-15T16:41:13+00:00"
+ "time": "2024-07-01T07:33:21+00:00"
},
{
"name": "scssphp/scssphp",
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
'Psr\\Http\\Server\\' => array($vendorDir . '/psr/http-server-handler/src', $vendorDir . '/psr/http-server-middleware/src'),
- 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'),
+ 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'),
'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'),
),
'Psr\\Http\\Message\\' =>
array (
- 0 => __DIR__ . '/..' . '/psr/http-message/src',
- 1 => __DIR__ . '/..' . '/psr/http-factory/src',
+ 0 => __DIR__ . '/..' . '/psr/http-factory/src',
+ 1 => __DIR__ . '/..' . '/psr/http-message/src',
),
'Psr\\Http\\Client\\' =>
array (
},
{
"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"
"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": {
"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": {
],
"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": [
{
},
{
"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": {
},
"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": {
],
"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": [
{
},
{
"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": {
},
"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": {
],
"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": [
{
},
{
"name": "sabberworm/php-css-parser",
- "version": "v8.5.1",
- "version_normalized": "8.5.1.0",
+ "version": "v8.6.0",
+ "version_normalized": "8.6.0.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
- "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152"
+ "reference": "d2fb94a9641be84d79c7548c6d39bbebba6e9a70"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/4a3d572b0f8b28bb6fd016ae8bbfc445facef152",
- "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152",
+ "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d2fb94a9641be84d79c7548c6d39bbebba6e9a70",
+ "reference": "d2fb94a9641be84d79c7548c6d39bbebba6e9a70",
"shasum": ""
},
"require": {
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
},
- "time": "2024-02-15T16:41:13+00:00",
+ "time": "2024-07-01T07:33:21+00:00",
"type": "library",
"extra": {
"branch-alias": {
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
- "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.5.1"
+ "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.6.0"
},
"install-path": "../sabberworm/php-css-parser"
},
'name' => '__root__',
'pretty_version' => '6.0.x-dev',
'version' => '6.0.9999999.9999999-dev',
- 'reference' => '284adc7603f0e4cc0733ad4011c123c12ffa6e3a',
+ 'reference' => '415af88f6254b5e2467702489eb4e03e0e43104c',
'type' => 'project',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
'__root__' => array(
'pretty_version' => '6.0.x-dev',
'version' => '6.0.9999999.9999999-dev',
- 'reference' => '284adc7603f0e4cc0733ad4011c123c12ffa6e3a',
+ 'reference' => '415af88f6254b5e2467702489eb4e03e0e43104c',
'type' => 'project',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
'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(),
'psr/http-factory-implementation' => array(
'dev_requirement' => false,
'provided' => array(
- 0 => '1.0',
- 1 => '^1.1 || ^2.0',
+ 0 => '^1.1 || ^2.0',
+ 1 => '1.0',
),
),
'psr/http-message' => array(
'psr/http-message-implementation' => array(
'dev_requirement' => false,
'provided' => array(
- 0 => '1.0',
- 1 => '^1.1 || ^2.0',
+ 0 => '^1.1 || ^2.0',
+ 1 => '1.0',
),
),
'psr/http-server-handler' => array(
'dev_requirement' => false,
),
'sabberworm/php-css-parser' => array(
- 'pretty_version' => 'v8.5.1',
- 'version' => '8.5.1.0',
- 'reference' => '4a3d572b0f8b28bb6fd016ae8bbfc445facef152',
+ 'pretty_version' => 'v8.6.0',
+ 'version' => '8.6.0.0',
+ 'reference' => 'd2fb94a9641be84d79c7548c6d39bbebba6e9a70',
'type' => 'library',
'install_path' => __DIR__ . '/../sabberworm/php-css-parser',
'aliases' => array(),
Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version.
+## 7.9.2 - 2024-07-24
+
+### Fixed
+
+- Adjusted handler selection to use cURL if its version is 7.21.2 or higher, rather than 7.34.0
+
+
+## 7.9.1 - 2024-07-19
+
+### Fixed
+
+- Fix TLS 1.3 check for HTTP/2 requests
+
+
+## 7.9.0 - 2024-07-18
+
+### Changed
+
+- Improve protocol version checks to provide feedback around unsupported protocols
+- Only select the cURL handler by default if 7.34.0 or higher is linked
+- Improved `CurlMultiHandler` to avoid busy wait if possible
+- Dropped support for EOL `guzzlehttp/psr7` v1
+- Improved URI user info redaction in errors
+
+## 7.8.2 - 2024-07-18
+
+### Added
+
+- Support for PHP 8.4
+
+
## 7.8.1 - 2023-12-03
### Changed
| Version | Status | Packagist | Namespace | Repo | Docs | PSR-7 | PHP Version |
|---------|---------------------|---------------------|--------------|---------------------|---------------------|-------|--------------|
-| 3.x | EOL | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | >=5.3.3,<7.0 |
-| 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >=5.4,<7.0 |
-| 5.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >=5.4,<7.4 |
-| 6.x | Security fixes only | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >=5.5,<8.0 |
-| 7.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes | >=7.2.5,<8.4 |
+| 3.x | EOL (2016-10-31) | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | >=5.3.3,<7.0 |
+| 4.x | EOL (2016-10-31) | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >=5.4,<7.0 |
+| 5.x | EOL (2019-10-31) | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >=5.4,<7.4 |
+| 6.x | EOL (2023-10-31) | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >=5.5,<8.0 |
+| 7.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes | >=7.2.5,<8.5 |
[guzzle-3-repo]: https://github.com/guzzle/guzzle3
[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x
"homepage": "https://github.com/Tobion"
}
],
+ "repositories": [
+ {
+ "type": "package",
+ "package": {
+ "name": "guzzle/client-integration-tests",
+ "version": "v3.0.2",
+ "dist": {
+ "url": "https://codeload.github.com/guzzle/client-integration-tests/zip/2c025848417c1135031fdf9c728ee53d0a7ceaee",
+ "type": "zip"
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.11",
+ "php-http/message": "^1.0 || ^2.0",
+ "guzzlehttp/psr7": "^1.7 || ^2.0",
+ "th3n3rd/cartesian-product": "^0.3"
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Client\\Tests\\": "src/"
+ }
+ },
+ "bin": [
+ "bin/http_test_server"
+ ]
+ }
+ }
+ ],
"require": {
"php": "^7.2.5 || ^8.0",
"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",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
},
"require-dev": {
"ext-curl": "*",
"bamarni/composer-bin-plugin": "^1.8.2",
- "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": {
*/
private $truncateAt;
- public function __construct(int $truncateAt = null)
+ public function __construct(?int $truncateAt = null)
{
$this->truncateAt = $truncateAt;
}
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);
}
}
*
* @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 = [])
{
*
* @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
*
* @deprecated ClientInterface::getConfig will be removed in guzzlehttp/guzzle:8.0.
*/
- public function getConfig(string $option = null);
+ public function getConfig(?string $option = null);
}
}, $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 = [];
* @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.
string $message,
RequestInterface $request,
ResponseInterface $response,
- \Throwable $previous = null,
+ ?\Throwable $previous = null,
array $handlerContext = []
) {
parent::__construct($message, $request, $response, $previous, $handlerContext);
public function __construct(
string $message,
RequestInterface $request,
- \Throwable $previous = null,
+ ?\Throwable $previous = null,
array $handlerContext = []
) {
parent::__construct($message, 0, $previous);
use Psr\Http\Client\RequestExceptionInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\UriInterface;
/**
* HTTP Request exception
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.
*/
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(
$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:
// <html> ... (truncated)
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
*/
use GuzzleHttp\TransferStats;
use GuzzleHttp\Utils;
use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\UriInterface;
/**
* Creates curl resources from a request
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']);
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;
'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.
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 = [
);
}
+ $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.
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<int|string, mixed>
*/
}
$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;
}
// 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:';
}
}
}
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;
namespace GuzzleHttp\Handler;
+use Closure;
use GuzzleHttp\Promise as P;
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\PromiseInterface;
}
}
+ // 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();
}
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.
*/
* @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<int, mixed>|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;
private function invokeStats(
RequestInterface $request,
array $options,
- ResponseInterface $response = null,
+ ?ResponseInterface $response = null,
$reason = null
): void {
if (isset($options['on_stats'])) {
\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 {
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, []);
// 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');
* 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');
/**
* @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;
}
* @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;
* @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 = [];
* @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;
}
*
* @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) {
*
* @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) {
*
* @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);
$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;
}
* 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';
* 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;
};
}
- 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);
*/
public function __construct(
RequestInterface $request,
- ResponseInterface $response = null,
- float $transferTime = null,
+ ?ResponseInterface $response = null,
+ ?float $transferTime = null,
$handlerErrorData = null,
array $handlerStats = []
) {
return \STDOUT;
}
- return \GuzzleHttp\Psr7\Utils::tryFopen('php://output', 'w');
+ return Psr7\Utils::tryFopen('php://output', 'w');
}
/**
{
$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')) {
# CHANGELOG
+## 2.0.3 - 2024-07-18
+
+### Changed
+
+- PHP 8.4 support
+
+
## 2.0.2 - 2023-12-03
### Changed
## Version Guidance
-| Version | Status | PHP Version |
-|---------|------------------------|--------------|
-| 1.x | Bug and security fixes | >=5.5,<8.3 |
-| 2.x | Latest | >=7.2.5,<8.4 |
+| Version | Status | PHP Version |
+|---------|---------------------|--------------|
+| 1.x | Security fixes only | >=5.5,<8.3 |
+| 2.x | Latest | >=7.2.5,<8.5 |
## Quick Start
},
"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"
},
"autoload": {
"psr-4": {
}
public function then(
- callable $onFulfilled = null,
- callable $onRejected = null
+ ?callable $onFulfilled = null,
+ ?callable $onRejected = null
): PromiseInterface {
return $this->result->then($onFulfilled, $onRejected);
}
*/
public static function of(
$iterable,
- callable $onFulfilled = null,
- callable $onRejected = null
+ ?callable $onFulfilled = null,
+ ?callable $onRejected = null
): PromiseInterface {
return (new EachPromise($iterable, [
'fulfilled' => $onFulfilled,
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,
public static function ofLimitAll(
$iterable,
$concurrency,
- callable $onFulfilled = null
+ ?callable $onFulfilled = null
): PromiseInterface {
return self::ofLimit(
$iterable,
}
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) {
* @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']);
* @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;
/**
}
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) {
* @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;
*
* @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;
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## 2.7.0 - 2024-07-18
+
+### Added
+
+- Add `Utils::redactUserInfo()` method
+- Add ability to encode bools as ints in `Query::build`
+
+## 2.6.3 - 2024-07-18
+
+### Fixed
+
+- Make `StreamWrapper::stream_stat()` return `false` if inner stream's size is `null`
+
+### Changed
+
+- PHP 8.4 support
+
## 2.6.2 - 2023-12-03
### Fixed
| Version | Status | PHP Version |
|---------|---------------------|--------------|
-| 1.x | Security fixes only | >=5.4,<8.1 |
-| 2.x | Latest | >=7.2.5,<8.4 |
+| 1.x | EOL (2024-06-30) | >=5.4,<8.2 |
+| 2.x | Latest | >=7.2.5,<8.5 |
## AppendStream
## `GuzzleHttp\Psr7\Query::build`
-`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string`
+`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string`
Build a query string from an array of key value pairs.
## `GuzzleHttp\Psr7\Utils::readLine`
-`public static function readLine(StreamInterface $stream, int $maxLength = null): string`
+`public static function readLine(StreamInterface $stream, ?int $maxLength = null): string`
Read a line from the stream up to the maximum allowed buffer length.
+## `GuzzleHttp\Psr7\Utils::redactUserInfo`
+
+`public static function redactUserInfo(UriInterface $uri): UriInterface`
+
+Redact the password in the user info part of a URI.
+
+
## `GuzzleHttp\Psr7\Utils::streamFor`
`public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface`
### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
-`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`
+`public static function isSameDocumentReference(UriInterface $uri, ?UriInterface $base = null): bool`
Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
},
"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"
*/
public function __construct(
StreamInterface $stream,
- StreamInterface $target = null
+ ?StreamInterface $target = null
) {
$this->remoteStream = $stream;
$this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+'));
{
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();
*
* @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);
* 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 '';
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);
}
} 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);
}
array $headers = [],
$body = null,
string $version = '1.1',
- string $reason = null
+ ?string $reason = null
) {
$this->assertStatusCodeRange($status);
}
}
- 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);
* 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,
$streamOrFile,
?int $size,
int $errorStatus,
- string $clientFilename = null,
- string $clientMediaType = null
+ ?string $clientFilename = null,
+ ?string $clientMediaType = null
) {
$this->setError($errorStatus);
$this->size = $size;
*
* @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);
* @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;
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.
*
### Fixed
+## 8.6.0
+
+### Added
+- Support arithmetic operators in CSS function arguments (#607)
+- Add support for inserting an item in a CSS list (#545)
+- Add support for the `dvh`, `lvh` and `svh` length units (#415)
+
+### Changed
+
+- Improve performance of Value::parseValue with many delimiters by refactoring to remove array_search() (#413)
+
+## 8.5.2
+
+### Changed
+
+- Mark all class constants as `@internal` (#500)
+
+### Fixed
+
+- Fix undefined local variable in `CalcFunction::parse()` (#593)
+
## 8.5.1
### Fixed
+- Fix PHP notice caused by parsing invalid color values having less than 6 characters (#485)
- Fix (regression) failure to parse at-rules with strict parsing (#456)
## 8.5.0
array_splice($this->aContents, $iOffset, $iLength, $mReplacement);
}
+ /**
+ * Inserts an item in the CSS list before its sibling. If the desired sibling cannot be found,
+ * the item is appended at the end.
+ *
+ * @param RuleSet|CSSList|Import|Charset $item
+ * @param RuleSet|CSSList|Import|Charset $sibling
+ */
+ public function insertBefore($item, $sibling)
+ {
+ if (in_array($sibling, $this->aContents, true)) {
+ $this->replace($sibling, [$item, $sibling]);
+ } else {
+ $this->append($item);
+ }
+ }
+
/**
* Removes an item from the CSS list.
*
{
/**
* @var null
+ *
+ * @internal
*/
const EOF = null;
* we’re whitelisting the block rules and have anything else be treated as a set rule.
*
* @var string
+ *
+ * @internal
*/
const BLOCK_RULES = 'media/document/supports/region-style/font-feature-values';
* … and more font-specific ones (to be used inside font-feature-values)
*
* @var string
+ *
+ * @internal
*/
const SET_RULES = 'font-face/counter-style/page/swash/styleset/annotation';
* regexp for specificity calculations
*
* @var string
+ *
+ * @internal
*/
const SELECTOR_VALIDATION_RX = '/
^(
* regexp for specificity calculations
*
* @var string
+ *
+ * @internal
*/
const NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX = '/
(\.[\w]+) # classes
* regexp for specificity calculations
*
* @var string
+ *
+ * @internal
*/
const ELEMENTS_AND_PSEUDO_ELEMENTS_RX = '/
((^|[\s\+\>\~]+)[\w]+ # elements
* regexp for specificity calculations
*
* @var string
+ *
+ * @internal
*/
const SELECTOR_VALIDATION_RX = '/
^(
{
/**
* @var int
+ *
+ * @internal
*/
const T_OPERAND = 1;
/**
* @var int
+ *
+ * @internal
*/
const T_OPERATOR = 2;
sprintf(
'Next token was expected to be an operand of type %s. Instead "%s" was found.',
implode(', ', $aOperators),
- $oVal
+ $oParserState->peek()
),
'',
'custom',
$oParserState->currentLine()
),
];
- } else {
+ } elseif ($oParserState->strlen($sValue) === 6) {
$aColor = [
'r' => new Size(intval($sValue[0] . $sValue[1], 16), null, true, $oParserState->currentLine()),
'g' => new Size(intval($sValue[2] . $sValue[3], 16), null, true, $oParserState->currentLine()),
'b' => new Size(intval($sValue[4] . $sValue[5], 16), null, true, $oParserState->currentLine()),
];
+ } else {
+ throw new UnexpectedTokenException(
+ 'Invalid hex color value',
+ $sValue,
+ 'custom',
+ $oParserState->currentLine()
+ );
}
} else {
$sColorMode = $oParserState->parseIdentifier(true);
* vh/vw/vm(ax)/vmin/rem are absolute insofar as they don’t scale to the immediate parent (only the viewport)
*
* @var array<int, string>
+ *
+ * @internal
*/
- const ABSOLUTE_SIZE_UNITS = ['px', 'cm', 'mm', 'mozmm', 'in', 'pt', 'pc', 'vh', 'vw', 'vmin', 'vmax', 'rem'];
+ const ABSOLUTE_SIZE_UNITS = [
+ 'px', 'pt', 'pc',
+ 'cm', 'mm', 'mozmm', 'in',
+ 'vh', 'dvh', 'svh', 'lvh',
+ 'vw', 'vmin', 'vmax', 'rem',
+ ];
/**
* @var array<int, string>
+ *
+ * @internal
*/
const RELATIVE_SIZE_UNITS = ['%', 'em', 'ex', 'ch', 'fr'];
/**
* @var array<int, string>
+ *
+ * @internal
*/
const NON_SIZE_UNITS = ['deg', 'grad', 'rad', 's', 'ms', 'turn', 'Hz', 'kHz'];
}
// Convert the list to list objects
foreach ($aListDelimiters as $sDelimiter) {
- if (count($aStack) === 1) {
+ $iStackLength = count($aStack);
+ if ($iStackLength === 1) {
return $aStack[0];
}
- $iStartPosition = null;
- while (($iStartPosition = array_search($sDelimiter, $aStack, true)) !== false) {
+ $aNewStack = [];
+ for ($iStartPosition = 0; $iStartPosition < $iStackLength; ++$iStartPosition) {
+ if ($iStartPosition === ($iStackLength - 1) || $sDelimiter !== $aStack[$iStartPosition + 1]) {
+ $aNewStack[] = $aStack[$iStartPosition];
+ continue;
+ }
$iLength = 2; //Number of elements to be joined
- for ($i = $iStartPosition + 2; $i < count($aStack); $i += 2, ++$iLength) {
+ for ($i = $iStartPosition + 3; $i < $iStackLength; $i += 2, ++$iLength) {
if ($sDelimiter !== $aStack[$i]) {
break;
}
}
$oList = new RuleValueList($sDelimiter, $oParserState->currentLine());
- for ($i = $iStartPosition - 1; $i - $iStartPosition + 1 < $iLength * 2; $i += 2) {
+ for ($i = $iStartPosition; $i - $iStartPosition < $iLength * 2; $i += 2) {
$oList->addListComponent($aStack[$i]);
}
- array_splice($aStack, $iStartPosition - 1, $iLength * 2 - 1, [$oList]);
+ $aNewStack[] = $oList;
+ $iStartPosition += $iLength * 2 - 2;
}
+ $aStack = $aNewStack;
}
if (!isset($aStack[0])) {
throw new UnexpectedTokenException(
} elseif ($oParserState->comes("U+")) {
$oValue = self::parseUnicodeRangeValue($oParserState);
} else {
- $oValue = self::parseIdentifierOrFunction($oParserState);
+ $sNextChar = $oParserState->peek(1);
+ try {
+ $oValue = self::parseIdentifierOrFunction($oParserState);
+ } catch (UnexpectedTokenException $e) {
+ if (\in_array($sNextChar, ['+', '-', '*', '/'], true)) {
+ $oValue = $oParserState->consume(1);
+ } else {
+ throw $e;
+ }
+ }
}
$oParserState->consumeWhiteSpace();
return $oValue;