From 69fdcf2e7167833c38e8df15eb1d0be48c5a8273 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 6 Apr 2021 14:57:48 +0200 Subject: [PATCH] Update to Guzzle 7.3 --- .../files/lib/system/api/composer.json | 11 +--- .../files/lib/system/api/composer.lock | 58 ++++++++++++------- .../system/api/composer/InstalledVersions.php | 7 +-- .../lib/system/api/composer/installed.json | 54 +++++++++++------ .../lib/system/api/composer/installed.php | 7 +-- .../system/api/guzzlehttp/guzzle/CHANGELOG.md | 12 ++++ .../api/guzzlehttp/guzzle/composer.json | 5 +- .../api/guzzlehttp/guzzle/src/Client.php | 2 +- .../guzzle/src/Handler/CurlFactory.php | 10 +++- .../guzzle/src/Handler/EasyHandle.php | 18 ++---- .../guzzle/src/Handler/HeaderProcessor.php | 42 ++++++++++++++ .../guzzle/src/Handler/StreamHandler.php | 32 ++++++---- .../guzzlehttp/guzzle/src/TransferStats.php | 2 +- .../api/guzzlehttp/guzzle/src/Utils.php | 7 +-- .../vendor-bin/php-cs-fixer/composer.json | 9 +++ .../guzzle/vendor-bin/phpstan/composer.json | 10 ++++ .../guzzle/vendor-bin/psalm/composer.json | 9 +++ 17 files changed, 204 insertions(+), 91 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php create mode 100644 wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/php-cs-fixer/composer.json create mode 100644 wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/phpstan/composer.json create mode 100644 wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/psalm/composer.json diff --git a/wcfsetup/install/files/lib/system/api/composer.json b/wcfsetup/install/files/lib/system/api/composer.json index f25f9928f1..4049cbf11e 100644 --- a/wcfsetup/install/files/lib/system/api/composer.json +++ b/wcfsetup/install/files/lib/system/api/composer.json @@ -15,14 +15,7 @@ "true/punycode": "~2.0", "pear/net_idna2": "^0.2.0", "scssphp/scssphp": "^1.4", - "guzzlehttp/guzzle": "dev-master", + "guzzlehttp/guzzle": "^7.3.0", "paragonie/constant_time_encoding": "^2.3" - }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/guzzle/guzzle", - "no-api": true - } - ] + } } diff --git a/wcfsetup/install/files/lib/system/api/composer.lock b/wcfsetup/install/files/lib/system/api/composer.lock index 8368a0491a..66e5937b04 100644 --- a/wcfsetup/install/files/lib/system/api/composer.lock +++ b/wcfsetup/install/files/lib/system/api/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": "e216be3206bab83be6ce15cd5f057810", + "content-hash": "daad7b81e20fb213695b7775be98f7fc", "packages": [ { "name": "chrisjean/php-ico", @@ -159,22 +159,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "dev-master", + "version": "7.3.0", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle", - "reference": "da94ef2e433d77af516d69c0f7d3c1b8ecd566dc" + "url": "https://github.com/guzzle/guzzle.git", + "reference": "7008573787b430c1c1f650e3722d9bba59967628" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/da94ef2e433d77af516d69c0f7d3c1b8ecd566dc", - "reference": "da94ef2e433d77af516d69c0f7d3c1b8ecd566dc", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7008573787b430c1c1f650e3722d9bba59967628", + "reference": "7008573787b430c1c1f650e3722d9bba59967628", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^1.4", - "guzzlehttp/psr7": "^1.7", + "guzzlehttp/psr7": "^1.7 || ^2.0", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0" }, @@ -182,6 +182,7 @@ "psr/http-client-implementation": "1.0" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", "ext-curl": "*", "php-http/client-integration-tests": "^3.0", "phpunit/phpunit": "^8.5.5 || ^9.3.5", @@ -192,11 +193,10 @@ "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { - "dev-master": "7.1-dev" + "dev-master": "7.3-dev" } }, "autoload": { @@ -207,11 +207,7 @@ "src/functions_include.php" ] }, - "autoload-dev": { - "psr-4": { - "GuzzleHttp\\Tests\\": "tests/" - } - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -230,17 +226,39 @@ "description": "Guzzle is a PHP HTTP client library", "homepage": "http://guzzlephp.org/", "keywords": [ - "HTTP client", - "PSR-18", - "PSR-7", "client", "curl", "framework", "http", + "http client", + "psr-18", + "psr-7", "rest", "web service" ], - "time": "2021-03-07T12:07:08+00:00" + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://github.com/alexeyshockov", + "type": "github" + }, + { + "url": "https://github.com/gmponos", + "type": "github" + } + ], + "time": "2021-03-23T11:33:13+00:00" }, { "name": "guzzlehttp/promises", @@ -1042,9 +1060,7 @@ "packages-dev": [], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "guzzlehttp/guzzle": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": [], diff --git a/wcfsetup/install/files/lib/system/api/composer/InstalledVersions.php b/wcfsetup/install/files/lib/system/api/composer/InstalledVersions.php index 52a1cb4b1b..929a0a2f8b 100644 --- a/wcfsetup/install/files/lib/system/api/composer/InstalledVersions.php +++ b/wcfsetup/install/files/lib/system/api/composer/InstalledVersions.php @@ -73,13 +73,12 @@ private static $installed = array ( ), 'guzzlehttp/guzzle' => array ( - 'pretty_version' => 'dev-master', - 'version' => 'dev-master', + 'pretty_version' => '7.3.0', + 'version' => '7.3.0.0', 'aliases' => array ( - 0 => '7.1.x-dev', ), - 'reference' => 'da94ef2e433d77af516d69c0f7d3c1b8ecd566dc', + 'reference' => '7008573787b430c1c1f650e3722d9bba59967628', ), 'guzzlehttp/promises' => array ( diff --git a/wcfsetup/install/files/lib/system/api/composer/installed.json b/wcfsetup/install/files/lib/system/api/composer/installed.json index 068a48f094..6ba605c44f 100644 --- a/wcfsetup/install/files/lib/system/api/composer/installed.json +++ b/wcfsetup/install/files/lib/system/api/composer/installed.json @@ -150,23 +150,23 @@ }, { "name": "guzzlehttp/guzzle", - "version": "dev-master", - "version_normalized": "dev-master", + "version": "7.3.0", + "version_normalized": "7.3.0.0", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle", - "reference": "da94ef2e433d77af516d69c0f7d3c1b8ecd566dc" + "url": "https://github.com/guzzle/guzzle.git", + "reference": "7008573787b430c1c1f650e3722d9bba59967628" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/da94ef2e433d77af516d69c0f7d3c1b8ecd566dc", - "reference": "da94ef2e433d77af516d69c0f7d3c1b8ecd566dc", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7008573787b430c1c1f650e3722d9bba59967628", + "reference": "7008573787b430c1c1f650e3722d9bba59967628", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^1.4", - "guzzlehttp/psr7": "^1.7", + "guzzlehttp/psr7": "^1.7 || ^2.0", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0" }, @@ -174,6 +174,7 @@ "psr/http-client-implementation": "1.0" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", "ext-curl": "*", "php-http/client-integration-tests": "^3.0", "phpunit/phpunit": "^8.5.5 || ^9.3.5", @@ -184,12 +185,11 @@ "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, - "time": "2021-03-07T12:07:08+00:00", - "default-branch": true, + "time": "2021-03-23T11:33:13+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "7.1-dev" + "dev-master": "7.3-dev" } }, "installation-source": "dist", @@ -201,11 +201,7 @@ "src/functions_include.php" ] }, - "autoload-dev": { - "psr-4": { - "GuzzleHttp\\Tests\\": "tests/" - } - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -224,16 +220,38 @@ "description": "Guzzle is a PHP HTTP client library", "homepage": "http://guzzlephp.org/", "keywords": [ - "HTTP client", - "PSR-18", - "PSR-7", "client", "curl", "framework", "http", + "http client", + "psr-18", + "psr-7", "rest", "web service" ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://github.com/alexeyshockov", + "type": "github" + }, + { + "url": "https://github.com/gmponos", + "type": "github" + } + ], "install-path": "../guzzlehttp/guzzle" }, { diff --git a/wcfsetup/install/files/lib/system/api/composer/installed.php b/wcfsetup/install/files/lib/system/api/composer/installed.php index daec9b0e63..e2eb7b01ae 100644 --- a/wcfsetup/install/files/lib/system/api/composer/installed.php +++ b/wcfsetup/install/files/lib/system/api/composer/installed.php @@ -49,13 +49,12 @@ ), 'guzzlehttp/guzzle' => array ( - 'pretty_version' => 'dev-master', - 'version' => 'dev-master', + 'pretty_version' => '7.3.0', + 'version' => '7.3.0.0', 'aliases' => array ( - 0 => '7.1.x-dev', ), - 'reference' => 'da94ef2e433d77af516d69c0f7d3c1b8ecd566dc', + 'reference' => '7008573787b430c1c1f650e3722d9bba59967628', ), 'guzzlehttp/promises' => array ( diff --git a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/CHANGELOG.md b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/CHANGELOG.md index d2092c0d13..e303af2814 100644 --- a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/CHANGELOG.md +++ b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/CHANGELOG.md @@ -2,6 +2,18 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version. +## 7.3.0 - 2021-03-23 + +### Added + +- Support for DER and P12 certificates [#2413](https://github.com/guzzle/guzzle/pull/2413) +- Support the cURL (http://) scheme for StreamHandler proxies [#2850](https://github.com/guzzle/guzzle/pull/2850) +- Support for `guzzlehttp/psr7:^2.0` [#2878](https://github.com/guzzle/guzzle/pull/2878) + +### Fixed + +- Handle exceptions on invalid header consistently between PHP versions and handlers [#2872](https://github.com/guzzle/guzzle/pull/2872) + ## 7.2.0 - 2020-10-10 ### Added diff --git a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/composer.json b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/composer.json index 11a366c59d..5da35a5bc2 100644 --- a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/composer.json +++ b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/composer.json @@ -31,7 +31,7 @@ "php": "^7.2.5 || ^8.0", "ext-json": "*", "guzzlehttp/promises": "^1.4", - "guzzlehttp/psr7": "^1.7", + "guzzlehttp/psr7": "^1.7 || ^2.0", "psr/http-client": "^1.0" }, "provide": { @@ -39,6 +39,7 @@ }, "require-dev": { "ext-curl": "*", + "bamarni/composer-bin-plugin": "^1.4.1", "php-http/client-integration-tests": "^3.0", "phpunit/phpunit": "^8.5.5 || ^9.3.5", "psr/log": "^1.1" @@ -53,7 +54,7 @@ }, "extra": { "branch-alias": { - "dev-master": "7.1-dev" + "dev-master": "7.3-dev" } }, "autoload": { diff --git a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Client.php b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Client.php index a27142b533..7349ec05f6 100644 --- a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Client.php +++ b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Client.php @@ -204,7 +204,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface { return $option === null ? $this->config - : (isset($this->config[$option]) ? $this->config[$option] : null); + : ($this->config[$option] ?? null); } private function buildUri(UriInterface $uri, array $config): UriInterface diff --git a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/CurlFactory.php b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/CurlFactory.php index b3f52975d0..36d478ae95 100644 --- a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/CurlFactory.php +++ b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/CurlFactory.php @@ -393,11 +393,11 @@ class CurlFactory implements CurlFactoryInterface if (!isset($options['sink'])) { // Use a default temp stream if no sink was set. - $options['sink'] = \fopen('php://temp', 'w+'); + $options['sink'] = \GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'w+'); } $sink = $options['sink']; if (!\is_string($sink)) { - $sink = \GuzzleHttp\Psr7\stream_for($sink); + $sink = \GuzzleHttp\Psr7\Utils::streamFor($sink); } elseif (!\is_dir(\dirname($sink))) { // Ensure that the directory exists before failing in curl. throw new \RuntimeException(\sprintf('Directory %s does not exist for sink value of %s', \dirname($sink), $sink)); @@ -456,6 +456,12 @@ class CurlFactory implements CurlFactoryInterface if (!\file_exists($cert)) { throw new \InvalidArgumentException("SSL certificate not found: {$cert}"); } + # OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files. + # see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html + $ext = pathinfo($cert, \PATHINFO_EXTENSION); + if (preg_match('#^(der|p12)$#i', $ext)) { + $conf[\CURLOPT_SSLCERTTYPE] = strtoupper($ext); + } $conf[\CURLOPT_SSLCERT] = $cert; } diff --git a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/EasyHandle.php b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/EasyHandle.php index a68c62f085..224344d7c8 100644 --- a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/EasyHandle.php +++ b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/EasyHandle.php @@ -63,17 +63,13 @@ final class EasyHandle /** * Attach a response to the easy handle based on the received headers. * - * @throws \RuntimeException if no headers have been received. + * @throws \RuntimeException if no headers have been received or the first + * header line is invalid. */ public function createResponse(): void { - if (empty($this->headers)) { - throw new \RuntimeException('No headers have been received'); - } + [$ver, $status, $reason, $headers] = HeaderProcessor::parseHeaders($this->headers); - // HTTP-version SP status-code SP reason-phrase - $startLine = \explode(' ', \array_shift($this->headers), 3); - $headers = Utils::headersFromLines($this->headers); $normalizedKeys = Utils::normalizeHeaderKeys($headers); if (!empty($this->options['decode_content']) && isset($normalizedKeys['content-encoding'])) { @@ -91,15 +87,13 @@ final class EasyHandle } } - $statusCode = (int) $startLine[1]; - // Attach a response to the easy handle with the parsed headers. $this->response = new Response( - $statusCode, + $status, $headers, $this->sink, - \substr($startLine[0], 5), - isset($startLine[2]) ? (string) $startLine[2] : null + $ver, + $reason ); } diff --git a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php new file mode 100644 index 0000000000..a0988845fd --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php @@ -0,0 +1,42 @@ +lastHeaders; $this->lastHeaders = []; - $parts = \explode(' ', \array_shift($hdrs), 3); - $ver = \explode('/', $parts[0])[1]; - $status = (int) $parts[1]; - $reason = $parts[2] ?? null; - $headers = Utils::headersFromLines($hdrs); + + try { + [$ver, $status, $reason, $headers] = HeaderProcessor::parseHeaders($hdrs); + } catch (\Exception $e) { + return P\Create::rejectionFor( + new RequestException('An error was encountered while creating the response', $request, null, $e) + ); + } + [$stream, $headers] = $this->checkDecode($options, $headers, $stream); $stream = Psr7\Utils::streamFor($stream); $sink = $stream; @@ -112,15 +116,21 @@ class StreamHandler $sink = $this->createSink($stream, $options); } - $response = new Psr7\Response($status, $headers, $sink, $ver, $reason); + try { + $response = new Psr7\Response($status, $headers, $sink, $ver, $reason); + } catch (\Exception $e) { + return P\Create::rejectionFor( + new RequestException('An error was encountered while creating the response', $request, null, $e) + ); + } if (isset($options['on_headers'])) { try { $options['on_headers']($response); } catch (\Exception $e) { - $msg = 'An error was encountered during the on_headers event'; - $ex = new RequestException($msg, $request, $response, $e); - return P\Create::rejectionFor($ex); + return P\Create::rejectionFor( + new RequestException('An error was encountered during the on_headers event', $request, $response, $e) + ); } } @@ -141,7 +151,7 @@ class StreamHandler return $stream; } - $sink = $options['sink'] ?? \fopen('php://temp', 'r+'); + $sink = $options['sink'] ?? Psr7\Utils::tryFopen('php://temp', 'r+'); return \is_string($sink) ? new Psr7\LazyOpenStream($sink, 'w+') : Psr7\Utils::streamFor($sink); } @@ -304,7 +314,7 @@ class StreamHandler return $this->createResource( function () use ($uri, &$http_response_header, $contextResource, $context, $options, $request) { - $resource = \fopen((string) $uri, 'r', false, $contextResource); + $resource = @\fopen((string) $uri, 'r', false, $contextResource); $this->lastHeaders = $http_response_header; if (false === $resource) { diff --git a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/TransferStats.php b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/TransferStats.php index 7b46c2b56d..93fa334c8d 100644 --- a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/TransferStats.php +++ b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/TransferStats.php @@ -128,6 +128,6 @@ final class TransferStats */ public function getHandlerStat(string $stat) { - return isset($this->handlerStats[$stat]) ? $this->handlerStats[$stat] : null; + return $this->handlerStats[$stat] ?? null; } } diff --git a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Utils.php b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Utils.php index 508144ae21..1e4e70462a 100644 --- a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Utils.php +++ b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/src/Utils.php @@ -71,12 +71,7 @@ final class Utils return \STDOUT; } - $resource = \fopen('php://output', 'w'); - if (false === $resource) { - throw new \RuntimeException('Can not open php output for writing to debug the resource.'); - } - - return $resource; + return \GuzzleHttp\Psr7\Utils::tryFopen('php://output', 'w'); } /** diff --git a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/php-cs-fixer/composer.json b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/php-cs-fixer/composer.json new file mode 100644 index 0000000000..3adf5f5cff --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/php-cs-fixer/composer.json @@ -0,0 +1,9 @@ +{ + "require": { + "php": "^7.2.5 || ^8.0", + "friendsofphp/php-cs-fixer": "2.18.3" + }, + "config": { + "preferred-install": "dist" + } +} diff --git a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/phpstan/composer.json b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/phpstan/composer.json new file mode 100644 index 0000000000..bfbc7273b2 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/phpstan/composer.json @@ -0,0 +1,10 @@ +{ + "require": { + "php": "^7.2.5 || ^8.0", + "phpstan/phpstan": "0.12.81", + "phpstan/phpstan-deprecation-rules": "0.12.6" + }, + "config": { + "preferred-install": "dist" + } +} diff --git a/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/psalm/composer.json b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/psalm/composer.json new file mode 100644 index 0000000000..535a0797dc --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/guzzlehttp/guzzle/vendor-bin/psalm/composer.json @@ -0,0 +1,9 @@ +{ + "require": { + "php": "^7.2.5 || ^8.0", + "psalm/phar": "4.6.2" + }, + "config": { + "preferred-install": "dist" + } +} -- 2.20.1