diff --git a/composer.json b/composer.json index d4ab0bed4..736f818ca 100644 --- a/composer.json +++ b/composer.json @@ -5,10 +5,10 @@ "require": { "php": ">=8.2", "doctrine/cache": "~1.5", - "guzzlehttp/guzzle": "^5.3", + "guzzlehttp/guzzle": "^7", "guzzlehttp/ringphp": "^1.1", "platformsh/console-form": ">=0.0.37 <2.0", - "platformsh/client": ">=0.87.0 <2.0", + "platformsh/client": "^3@beta", "symfony/console": "^3.0 >=3.2", "symfony/yaml": "^3.0 || ^2.6", "symfony/finder": "^3.0", @@ -25,7 +25,8 @@ "khill/php-duration": "^1.1", "giggsey/libphonenumber-for-php": "^8.13", "symfony/polyfill-mbstring": "^1.19", - "symfony/polyfill-iconv": "^1.19" + "symfony/polyfill-iconv": "^1.19", + "platformsh/oauth2": "^1@beta" }, "suggest": { "drush/drush": "For Drupal projects" diff --git a/composer.lock b/composer.lock index f77005d30..a2d56ed68 100644 --- a/composer.lock +++ b/composer.lock @@ -4,23 +4,24 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fe50ebaa4d5b134599bfaede328ff32e", + "content-hash": "aa53680315ea46e77e18c6ecf638a813", "packages": [ { "name": "cocur/slugify", - "version": "v2.5", + "version": "v3.2", "source": { "type": "git", "url": "https://github.com/cocur/slugify.git", - "reference": "e8167e9a3236044afebd6e8ab13ebeb3ec9ca145" + "reference": "d41701efe58ba2df9cae029c3d21e1518cc6780e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cocur/slugify/zipball/e8167e9a3236044afebd6e8ab13ebeb3ec9ca145", - "reference": "e8167e9a3236044afebd6e8ab13ebeb3ec9ca145", + "url": "https://api.github.com/repos/cocur/slugify/zipball/d41701efe58ba2df9cae029c3d21e1518cc6780e", + "reference": "d41701efe58ba2df9cae029c3d21e1518cc6780e", "shasum": "" }, "require": { + "ext-mbstring": "*", "php": ">=5.5.9" }, "require-dev": { @@ -30,13 +31,13 @@ "mikey179/vfsstream": "~1.6", "mockery/mockery": "~0.9", "nette/di": "~2.2", - "phpunit/phpunit": "~4.8|~5.2", + "phpunit/phpunit": "~4.8.36|~5.2", "pimple/pimple": "~1.1", "plumphp/plum": "~0.1", "silex/silex": "~1.3", - "symfony/config": "~2.4|~3.0", - "symfony/dependency-injection": "~2.4|~3.0", - "symfony/http-kernel": "~2.4|~3.0", + "symfony/config": "~2.4|~3.0|~4.0", + "symfony/dependency-injection": "~2.4|~3.0|~4.0", + "symfony/http-kernel": "~2.4|~3.0|~4.0", "twig/twig": "~1.26|~2.0", "zendframework/zend-modulemanager": "~2.2", "zendframework/zend-servicemanager": "~2.2", @@ -72,7 +73,7 @@ "issues": "https://github.com/cocur/slugify/issues", "source": "https://github.com/cocur/slugify/tree/master" }, - "time": "2017-03-23T21:52:55+00:00" + "time": "2019-01-31T20:38:55+00:00" }, { "name": "composer/ca-bundle", @@ -249,54 +250,6 @@ ], "time": "2022-05-20T20:06:54+00:00" }, - { - "name": "firebase/php-jwt", - "version": "v2.2.0", - "source": { - "type": "git", - "url": "https://github.com/firebase/php-jwt.git", - "reference": "e0a75bfb6413f22092c99b70f310ccb2cca3efa5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/e0a75bfb6413f22092c99b70f310ccb2cca3efa5", - "reference": "e0a75bfb6413f22092c99b70f310ccb2cca3efa5", - "shasum": "" - }, - "require": { - "php": ">=5.2.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "Authentication/", - "Exceptions/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Neuman Vong", - "email": "neuman+pear@twilio.com", - "role": "Developer" - }, - { - "name": "Anant Narayanan", - "email": "anant@php.net", - "role": "Developer" - } - ], - "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", - "homepage": "https://github.com/firebase/php-jwt", - "support": { - "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v2.2.0" - }, - "time": "2015-06-22T23:26:39+00:00" - }, { "name": "giggsey/libphonenumber-for-php", "version": "8.13.50", @@ -431,36 +384,56 @@ "time": "2024-11-04T11:18:07+00:00" }, { - "name": "guzzlehttp/cache-subscriber", - "version": "0.2.0", + "name": "guzzlehttp/guzzle", + "version": "7.9.2", "source": { "type": "git", - "url": "https://github.com/guzzle/cache-subscriber.git", - "reference": "8c766ba399e4c46383e3eaa220201be62abd101e" + "url": "https://github.com/guzzle/guzzle.git", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/cache-subscriber/zipball/8c766ba399e4c46383e3eaa220201be62abd101e", - "reference": "8c766ba399e4c46383e3eaa220201be62abd101e", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", "shasum": "" }, "require": { - "doctrine/cache": "~1.3", - "guzzlehttp/guzzle": "~5.0", - "php": ">=5.4.0" + "ext-json": "*", + "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" + }, + "provide": { + "psr/http-client-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "0.2-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { - "GuzzleHttp\\Subscriber\\Cache\\": "src" + "GuzzleHttp\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -468,52 +441,105 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], - "description": "Guzzle HTTP cache subscriber", - "homepage": "http://guzzlephp.org/", + "description": "Guzzle is a PHP HTTP client library", "keywords": [ - "Guzzle", - "cache" + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" ], "support": { - "issues": "https://github.com/guzzle/cache-subscriber/issues", - "source": "https://github.com/guzzle/cache-subscriber/tree/0.2.0" + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" }, - "abandoned": true, - "time": "2019-09-16T13:44:55+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2024-07-24T11:22:20+00:00" }, { - "name": "guzzlehttp/guzzle", - "version": "5.3.4", + "name": "guzzlehttp/promises", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "b87eda7a7162f95574032da17e9323c9899cb6b2" + "url": "https://github.com/guzzle/promises.git", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b87eda7a7162f95574032da17e9323c9899cb6b2", - "reference": "b87eda7a7162f95574032da17e9323c9899cb6b2", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", "shasum": "" }, "require": { - "guzzlehttp/ringphp": "^1.1", - "php": ">=5.4.0", - "react/promise": "^2.2" + "php": "^7.2.5 || ^8.0" }, "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.0" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, "autoload": { "psr-4": { - "GuzzleHttp\\": "src/" + "GuzzleHttp\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -521,28 +547,166 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", - "homepage": "http://guzzlephp.org/", + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2024-10-17T10:06:22+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ - "client", - "curl", - "framework", "http", - "http client", - "rest", - "web service" + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" ], "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/5.3" + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.0" }, - "time": "2019-10-30T09:32:00+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2024-07-18T11:15:46+00:00" }, { "name": "guzzlehttp/ringphp", @@ -717,6 +881,76 @@ }, "time": "2021-03-17T11:34:55+00:00" }, + { + "name": "league/oauth2-client", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-client.git", + "reference": "160d6274b03562ebeb55ed18399281d8118b76c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/160d6274b03562ebeb55ed18399281d8118b76c8", + "reference": "160d6274b03562ebeb55ed18399281d8118b76c8", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0 || ^7.0", + "paragonie/random_compat": "^1 || ^2 || ^9.99", + "php": "^5.6 || ^7.0 || ^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.5", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpunit/phpunit": "^5.7 || ^6.0 || ^9.5", + "squizlabs/php_codesniffer": "^2.3 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", + "role": "Developer" + }, + { + "name": "Woody Gilk", + "homepage": "https://github.com/shadowhand", + "role": "Contributor" + } + ], + "description": "OAuth 2.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "identity", + "idp", + "oauth", + "oauth2", + "single sign on" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-client/issues", + "source": "https://github.com/thephpleague/oauth2-client/tree/2.7.0" + }, + "time": "2023-04-16T18:19:15+00:00" + }, { "name": "padraic/humbug_get_contents", "version": "1.0.4", @@ -890,30 +1124,35 @@ "time": "2022-02-16T17:07:03+00:00" }, { - "name": "pjcdawkins/guzzle-oauth2-plugin", - "version": "v2.4.1", + "name": "platformsh/client", + "version": "3.0.0-beta1", "source": { "type": "git", - "url": "https://github.com/pjcdawkins/guzzle-oauth2-plugin.git", - "reference": "affdff1cfe962c436b7e725607d03f971602c249" + "url": "https://github.com/platformsh/platformsh-client-php.git", + "reference": "6d5e117f952a513a8673d29b83ef2c505cf5c693" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pjcdawkins/guzzle-oauth2-plugin/zipball/affdff1cfe962c436b7e725607d03f971602c249", - "reference": "affdff1cfe962c436b7e725607d03f971602c249", + "url": "https://api.github.com/repos/platformsh/platformsh-client-php/zipball/6d5e117f952a513a8673d29b83ef2c505cf5c693", + "reference": "6d5e117f952a513a8673d29b83ef2c505cf5c693", "shasum": "" }, "require": { - "firebase/php-jwt": "~2.0", - "guzzlehttp/guzzle": "~5.0" + "cocur/slugify": "^3.0", + "ext-json": "*", + "guzzlehttp/guzzle": "^7 || ^8", + "khill/php-duration": "^1.1", + "php": ">=8.2", + "platformsh/oauth2": "^1.0.0@beta" }, "require-dev": { - "phpunit/phpunit": "~4.5" + "phpunit/phpunit": "^11", + "symplify/easy-coding-standard": "^12.3" }, "type": "library", "autoload": { "psr-4": { - "CommerceGuys\\Guzzle\\Oauth2\\": "src" + "Platformsh\\Client\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -921,51 +1160,42 @@ "MIT" ], "authors": [ - { - "name": "Bojan Zivanovic" - }, - { - "name": "Damien Tournoud" - }, { "name": "Patrick Dawkins" } ], - "description": "An OAuth2 plugin (subscriber) for Guzzle (forked from commerceguys/guzzle-oauth2-plugin)", + "description": "Platform.sh API client", "support": { - "source": "https://github.com/pjcdawkins/guzzle-oauth2-plugin/tree/v2.4.1" + "issues": "https://github.com/platformsh/platformsh-client-php/issues", + "source": "https://github.com/platformsh/platformsh-client-php/tree/3.0.0-beta1" }, - "time": "2024-05-23T15:23:52+00:00" + "time": "2024-11-25T20:48:02+00:00" }, { - "name": "platformsh/client", - "version": "0.87.0", + "name": "platformsh/console-form", + "version": "v0.0.37", "source": { "type": "git", - "url": "https://github.com/platformsh/platformsh-client-php.git", - "reference": "3ba1dde15d3fb34568c87f8b6a84515646e9c880" + "url": "https://github.com/platformsh/console-form.git", + "reference": "f3c499c189d95191f5f92127a03d86f053928a40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/platformsh/platformsh-client-php/zipball/3ba1dde15d3fb34568c87f8b6a84515646e9c880", - "reference": "3ba1dde15d3fb34568c87f8b6a84515646e9c880", + "url": "https://api.github.com/repos/platformsh/console-form/zipball/f3c499c189d95191f5f92127a03d86f053928a40", + "reference": "f3c499c189d95191f5f92127a03d86f053928a40", "shasum": "" }, "require": { - "cocur/slugify": "^2.0 || ~1.0", - "ext-json": "*", - "guzzlehttp/cache-subscriber": "~0.1", - "guzzlehttp/guzzle": "~5.3", "php": ">=5.5.9", - "pjcdawkins/guzzle-oauth2-plugin": ">=2.4.0 <=3.0" + "symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.0 || ^2.6" }, "require-dev": { - "phpunit/phpunit": "~4.5" + "phpunit/phpunit": "^5.0" }, "type": "library", "autoload": { "psr-4": { - "Platformsh\\Client\\": "src" + "Platformsh\\ConsoleForm\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -977,38 +1207,39 @@ "name": "Patrick Dawkins" } ], - "description": "Platform.sh API client", + "description": "A lightweight Symfony Console form system.", "support": { - "issues": "https://github.com/platformsh/platformsh-client-php/issues", - "source": "https://github.com/platformsh/platformsh-client-php/tree/0.87.0" + "issues": "https://github.com/platformsh/console-form/issues", + "source": "https://github.com/platformsh/console-form/tree/v0.0.37" }, - "time": "2024-11-14T08:02:39+00:00" + "time": "2023-05-05T09:24:39+00:00" }, { - "name": "platformsh/console-form", - "version": "v0.0.37", + "name": "platformsh/oauth2", + "version": "1.0.0-beta3", "source": { "type": "git", - "url": "https://github.com/platformsh/console-form.git", - "reference": "f3c499c189d95191f5f92127a03d86f053928a40" + "url": "https://github.com/platformsh/platformsh-oauth2-php.git", + "reference": "3c0e12549850837a827ca432a50aa052e3cab250" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/platformsh/console-form/zipball/f3c499c189d95191f5f92127a03d86f053928a40", - "reference": "f3c499c189d95191f5f92127a03d86f053928a40", + "url": "https://api.github.com/repos/platformsh/platformsh-oauth2-php/zipball/3c0e12549850837a827ca432a50aa052e3cab250", + "reference": "3c0e12549850837a827ca432a50aa052e3cab250", "shasum": "" }, "require": { - "php": ">=5.5.9", - "symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.0 || ^2.6" + "league/oauth2-client": "^2.2", + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^5.0" + "phpunit/phpunit": "^11", + "symplify/easy-coding-standard": "^12.3" }, "type": "library", "autoload": { "psr-4": { - "Platformsh\\ConsoleForm\\": "src" + "Platformsh\\OAuth2\\Client\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1020,12 +1251,12 @@ "name": "Patrick Dawkins" } ], - "description": "A lightweight Symfony Console form system.", + "description": "Platform.sh OAuth2 client", "support": { - "issues": "https://github.com/platformsh/console-form/issues", - "source": "https://github.com/platformsh/console-form/tree/v0.0.37" + "issues": "https://github.com/platformsh/platformsh-oauth2-php/issues", + "source": "https://github.com/platformsh/platformsh-oauth2-php/tree/1.0.0-beta3" }, - "time": "2023-05-05T09:24:39+00:00" + "time": "2024-11-25T19:19:41+00:00" }, { "name": "psr/container", @@ -1075,32 +1306,192 @@ }, "time": "2021-11-05T16:50:12+00:00" }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, { "name": "psr/log", - "version": "1.1.4", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1121,9 +1512,53 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2019-03-08T08:55:37+00:00" }, { "name": "react/promise", @@ -1558,6 +1993,73 @@ ], "time": "2020-10-24T10:57:07+00:00" }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, { "name": "symfony/event-dispatcher", "version": "v3.4.47", @@ -3765,7 +4267,10 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "platformsh/client": 10, + "platformsh/oauth2": 10 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/Command/Auth/ApiTokenLoginCommand.php b/src/Command/Auth/ApiTokenLoginCommand.php index 4f44ba0e7..df18420ca 100644 --- a/src/Command/Auth/ApiTokenLoginCommand.php +++ b/src/Command/Auth/ApiTokenLoginCommand.php @@ -1,10 +1,11 @@ $clientId, - 'token_url' => $tokenUrl, - 'auth_location' => 'headers', + $provider = $this->api()->getClient()->getConnector()->getOAuth2Provider(); + $token = $provider->getAccessToken(new ApiToken(), [ 'api_token' => $apiToken, - ]))->getToken(); + ]); } catch (BadResponseException $e) { if ($this->exceptionMeansInvalidToken($e)) { throw new \RuntimeException('Invalid API token'); @@ -121,7 +120,7 @@ private function exceptionMeansInvalidToken(\Exception $e) { if (!$e instanceof BadResponseException || !$e->getResponse() || !in_array($e->getResponse()->getStatusCode(), [400, 401], true)) { return false; } - $json = $e->getResponse()->json(); + $json = Utils::jsonDecode($e->getResponse(), true); // Compatibility with legacy auth provider. if (isset($json['error'], $json['error_description']) && $json['error'] === 'invalid_grant' diff --git a/src/Command/Auth/BrowserLoginCommand.php b/src/Command/Auth/BrowserLoginCommand.php index 8f161899c..64a9652be 100644 --- a/src/Command/Auth/BrowserLoginCommand.php +++ b/src/Command/Auth/BrowserLoginCommand.php @@ -1,9 +1,11 @@ setData([ - 'accessToken' => $token->getToken(), - 'tokenType' => $token->getType(), - ]); - if ($token->getExpires()) { - $session->set('expires', $token->getExpires()->getTimestamp()); - } - if ($token->getRefreshToken()) { - $session->set('refreshToken', $token->getRefreshToken()->getToken()); - } + $token = new AccessToken($tokenData); + $session->set('accessToken', $token->getToken()); + $session->set('tokenType', $tokenData['token_type'] ?: null); + $session->set('expires', $token->getExpires()); + $session->set('refreshToken', $token->getRefreshToken()); $session->save(); } @@ -342,23 +338,23 @@ private function createDocumentRoot($dir) */ private function getAccessToken($authCode, $codeVerifier, $redirectUri) { - $client = new Client(); - $request = $client->createRequest('post', $this->config()->get('api.oauth2_token_url'), [ - 'body' => [ - 'grant_type' => 'authorization_code', - 'code' => $authCode, - 'client_id' => $this->config()->get('api.oauth2_client_id'), - 'redirect_uri' => $redirectUri, - 'code_verifier' => $codeVerifier, - ], - 'auth' => false, - 'verify' => !$this->config()->getWithDefault('api.skip_ssl', false), - ]); + $client = new Client(['verify' => !$this->config()->getWithDefault('api.skip_ssl', false)]); + $request = new Request('POST', $this->config()->get('api.oauth2_token_url'), body: http_build_query([ + 'grant_type' => 'authorization_code', + 'code' => $authCode, + 'redirect_uri' => $redirectUri, + 'code_verifier' => $codeVerifier, + ])); try { - $response = $client->send($request); + $response = $client->send($request, [ + 'headers' => [ + 'Content-Type' => 'application/x-www-form-urlencoded', + ], + 'auth' => [$this->config()->get('api.oauth2_client_id'), ''], + ]); - return $response->json(); + return Utils::jsonDecode((string) $response->getBody(), true); } catch (BadResponseException $e) { throw ApiResponseException::create($request, $e->getResponse(), $e); } diff --git a/src/Command/Auth/VerifyPhoneNumberCommand.php b/src/Command/Auth/VerifyPhoneNumberCommand.php index b2151dad8..f6175f6fc 100644 --- a/src/Command/Auth/VerifyPhoneNumberCommand.php +++ b/src/Command/Auth/VerifyPhoneNumberCommand.php @@ -2,6 +2,7 @@ namespace Platformsh\Cli\Command\Auth; use GuzzleHttp\Exception\BadResponseException; +use GuzzleHttp\Utils; use libphonenumber\NumberParseException; use libphonenumber\PhoneNumberFormat; use libphonenumber\PhoneNumberUtil; @@ -67,12 +68,13 @@ protected function execute(InputInterface $input, OutputInterface $output) $httpClient = $this->api()->getHttpClient(); - $sid = $httpClient->post('/users/' . rawurlencode($myUser->id) . '/phonenumber', [ + $response = $httpClient->post('/users/' . rawurlencode($myUser->id) . '/phonenumber', [ 'json' => [ 'channel' => $channel, 'phone_number' => $number, ], - ])->json()['sid']; + ]); + $sid = Utils::jsonDecode((string) $response->getBody(), true)['sid']; if ($channel === 'call') { $this->stdErr->writeln('Calling the number ' . $number . ' with a verification code.'); @@ -94,7 +96,7 @@ protected function execute(InputInterface $input, OutputInterface $output) ]); } catch (BadResponseException $e) { if (($response = $e->getResponse()) && $response->getStatusCode() === 400) { - $detail = $response->json(); + $detail = Utils::jsonDecode((string) $response->getBody(), true); throw new InvalidArgumentException(isset($detail['error']) ? ucfirst($detail['error']) : 'Invalid verification code'); } throw $e; @@ -102,7 +104,8 @@ protected function execute(InputInterface $input, OutputInterface $output) }); $this->debug('Refreshing phone verification status'); - $needsVerify = $httpClient->post( '/me/verification?force_refresh=1')->json(); + $response = $httpClient->post( '/me/verification?force_refresh=1'); + $needsVerify = Utils::jsonDecode((string) $response->getBody(), true); $this->stdErr->writeln(''); if ($needsVerify['type'] === 'phone') { diff --git a/src/Command/Backup/BackupListCommand.php b/src/Command/Backup/BackupListCommand.php index bedae9a79..0f0c442a9 100644 --- a/src/Command/Backup/BackupListCommand.php +++ b/src/Command/Backup/BackupListCommand.php @@ -54,7 +54,7 @@ protected function execute(InputInterface $input, OutputInterface $output) /** @var \Platformsh\Cli\Service\PropertyFormatter $formatter */ $formatter = $this->getService('property_formatter'); - $backups = $environment->getBackups($input->getOption('limit')); + $backups = $environment->getBackups((int) $input->getOption('limit')); if (!$backups) { $this->stdErr->writeln('No backups found'); return 1; diff --git a/src/Command/BlueGreen/BlueGreenConcludeCommand.php b/src/Command/BlueGreen/BlueGreenConcludeCommand.php index 81c8d86bf..dd358605d 100644 --- a/src/Command/BlueGreen/BlueGreenConcludeCommand.php +++ b/src/Command/BlueGreen/BlueGreenConcludeCommand.php @@ -2,6 +2,7 @@ namespace Platformsh\Cli\Command\BlueGreen; +use GuzzleHttp\Utils; use Platformsh\Cli\Command\CommandBase; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -25,7 +26,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $environment = $this->getSelectedEnvironment(); $httpClient = $this->api()->getHttpClient(); - $data = $httpClient->get($environment->getLink('#versions'))->json(); + $response = $httpClient->get($environment->getLink('#versions')); + $data = Utils::jsonDecode((string) $response->getBody(), true); if (count($data) < 2) { $this->stdErr->writeln(sprintf('Blue/green deployments are not enabled for the environment %s.', $this->api()->getEnvironmentLabel($environment, 'error'))); return 1; diff --git a/src/Command/BlueGreen/BlueGreenDeployCommand.php b/src/Command/BlueGreen/BlueGreenDeployCommand.php index aba7345d6..486581f04 100644 --- a/src/Command/BlueGreen/BlueGreenDeployCommand.php +++ b/src/Command/BlueGreen/BlueGreenDeployCommand.php @@ -2,6 +2,7 @@ namespace Platformsh\Cli\Command\BlueGreen; +use GuzzleHttp\Utils; use Platformsh\Cli\Command\CommandBase; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -27,7 +28,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $environment = $this->getSelectedEnvironment(); $httpClient = $this->api()->getHttpClient(); - $data = $httpClient->get($environment->getLink('#versions'))->json(); + $response = $httpClient->get($environment->getLink('#versions')); + $data = Utils::jsonDecode((string) $response->getBody(), true); if (count($data) < 2) { $this->stdErr->writeln(sprintf('Blue/green deployments are not enabled for the environment %s.', $this->api()->getEnvironmentLabel($environment, 'error'))); $this->stdErr->writeln(sprintf('Enable blue/green first by running: %s blue-green:enable', $this->config()->get('application.executable'))); diff --git a/src/Command/BlueGreen/BlueGreenEnableCommand.php b/src/Command/BlueGreen/BlueGreenEnableCommand.php index ac8e729b2..ab7d91aa9 100644 --- a/src/Command/BlueGreen/BlueGreenEnableCommand.php +++ b/src/Command/BlueGreen/BlueGreenEnableCommand.php @@ -2,6 +2,7 @@ namespace Platformsh\Cli\Command\BlueGreen; +use GuzzleHttp\Utils; use Platformsh\Cli\Command\CommandBase; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -32,7 +33,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $environment = $this->getSelectedEnvironment(); $httpClient = $this->api()->getHttpClient(); - $data = $httpClient->get($environment->getLink('#versions'))->json(); + $response = $httpClient->get($environment->getLink('#versions')); + $data = Utils::jsonDecode((string) $response->getBody(), true); if (count($data) > 1) { $this->stdErr->writeln(sprintf('Blue/green deployments are already enabled for the environment %s.', $this->api()->getEnvironmentLabel($environment))); $this->stdErr->writeln(sprintf('List versions by running: %s versions', $this->config()->get('application.executable'))); diff --git a/src/Command/CommandBase.php b/src/Command/CommandBase.php index 0cb57243a..ad8d6628b 100644 --- a/src/Command/CommandBase.php +++ b/src/Command/CommandBase.php @@ -714,8 +714,8 @@ public function getCurrentProject($suppressErrors = false) } if ($this->config()->has('api.base_url') && $e->getResponse() && $e->getResponse()->getStatusCode() === 401 - && parse_url($this->config()->get('api.base_url'), PHP_URL_HOST) !== $e->getRequest()->getHost()) { - $this->debug('Ignoring 401 error for unrecognized local project hostname: ' . $e->getRequest()->getHost()); + && parse_url($this->config()->get('api.base_url'), PHP_URL_HOST) !== $e->getRequest()->getUri()->getHost()) { + $this->debug('Ignoring 401 error for unrecognized local project hostname: ' . $e->getRequest()->getUri()->getHost()); return $this->currentProject = false; } throw $e; diff --git a/src/Command/Domain/DomainAddCommand.php b/src/Command/Domain/DomainAddCommand.php index 0c6d2b62f..181942726 100644 --- a/src/Command/Domain/DomainAddCommand.php +++ b/src/Command/Domain/DomainAddCommand.php @@ -2,6 +2,7 @@ namespace Platformsh\Cli\Command\Domain; use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Utils; use Platformsh\Cli\Model\EnvironmentDomain; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -68,7 +69,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($response) { $code = $response->getStatusCode(); if ($code === 402) { - $data = $response->json(); + $data = Utils::jsonDecode((string) $response->getBody(), true); if (isset($data['message'], $data['detail']['environments_with_domains_limit'], $data['detail']['environments_with_domains'])) { $this->stdErr->writeln(''); $this->stdErr->writeln($data['message']); @@ -79,7 +80,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } } if ($code === 409) { - $data = $response->json(); + $data = Utils::jsonDecode((string) $response->getBody(), true); if (isset($data['message'], $data['detail']['conflicting_domain']) && strpos($data['message'], 'already has a domain with the same replacement_for') !== false) { $this->stdErr->writeln(''); $this->stdErr->writeln(sprintf( diff --git a/src/Command/Domain/DomainCommandBase.php b/src/Command/Domain/DomainCommandBase.php index 70406adc0..baf649c21 100644 --- a/src/Command/Domain/DomainCommandBase.php +++ b/src/Command/Domain/DomainCommandBase.php @@ -3,6 +3,7 @@ use GuzzleHttp\Exception\BadResponseException; use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Utils; use Platformsh\Cli\Command\CommandBase; use Platformsh\Cli\Util\SslUtil; use Platformsh\Client\Model\Environment; @@ -207,7 +208,7 @@ protected function handleApiException(ClientException $e, Project $project) } // @todo standardize API error parsing if the format is ever formalized if ($response->getStatusCode() === 400) { - $data = $response->json(); + $data = Utils::jsonDecode((string) $response->getBody(), true); if (isset($data['detail']['error'])) { $this->stdErr->writeln($data['detail']['error']); return; diff --git a/src/Command/Integration/IntegrationCommandBase.php b/src/Command/Integration/IntegrationCommandBase.php index 4db35decc..7079d3c6d 100644 --- a/src/Command/Integration/IntegrationCommandBase.php +++ b/src/Command/Integration/IntegrationCommandBase.php @@ -2,6 +2,7 @@ namespace Platformsh\Cli\Command\Integration; use GuzzleHttp\Exception\BadResponseException; +use GuzzleHttp\Utils; use Platformsh\Cli\Command\CommandBase; use Platformsh\Client\Model\Integration; use Platformsh\Client\Model\Project; @@ -667,7 +668,7 @@ protected function getBitbucketAccessToken(array $credentials) if (isset($this->bitbucketAccessTokens[$credentials['key']])) { return $this->bitbucketAccessTokens[$credentials['key']]; } - $result = $this->api() + $response = $this->api() ->getExternalHttpClient() ->post('https://bitbucket.org/site/oauth2/access_token', [ 'auth' => [$credentials['key'], $credentials['secret']], @@ -676,7 +677,7 @@ protected function getBitbucketAccessToken(array $credentials) ], ]); - $data = $result->json(); + $data = Utils::jsonDecode((string) $response->getBody(), true); if (!isset($data['access_token'])) { throw new \RuntimeException('Access token not found in Bitbucket response'); } diff --git a/src/Command/Metrics/MetricsCommandBase.php b/src/Command/Metrics/MetricsCommandBase.php index 0feae5479..d53b21733 100644 --- a/src/Command/Metrics/MetricsCommandBase.php +++ b/src/Command/Metrics/MetricsCommandBase.php @@ -3,6 +3,7 @@ namespace Platformsh\Cli\Command\Metrics; use GuzzleHttp\Exception\BadResponseException; +use GuzzleHttp\Psr7\Request; use Khill\Duration\Duration; use Platformsh\Cli\Command\CommandBase; use Platformsh\Cli\Console\AdaptiveTableCell; @@ -256,7 +257,9 @@ protected function fetchMetrics(InputInterface $input, TimeSpec $timeSpec, Envir // Perform the metrics query. $client = $this->api()->getHttpClient(); - $request = $client->createRequest('POST', $metricsQueryUrl, ['json' => $query->asArray()]); + $request = new Request('POST', $metricsQueryUrl, [ + 'Content-Type' => 'application/json', + ], json_encode($query->asArray())); try { $result = $client->send($request); } catch (BadResponseException $e) { diff --git a/src/Command/Organization/OrganizationCurlCommand.php b/src/Command/Organization/OrganizationCurlCommand.php index c02527a5f..e077ef1c5 100644 --- a/src/Command/Organization/OrganizationCurlCommand.php +++ b/src/Command/Organization/OrganizationCurlCommand.php @@ -1,7 +1,7 @@ validateOrganizationInput($input); - $apiUrl = Url::fromString($this->config()->getApiUrl()); - $absoluteUrl = $apiUrl->combine($organization->getUri())->__toString(); + $apiUri = new Uri($this->config()->getApiUrl()); + $absoluteUrl = $apiUri->withPath($organization->getUri()); /** @var CurlCli $curl */ $curl = $this->getService('curl_cli'); diff --git a/src/Command/Self/SelfReleaseCommand.php b/src/Command/Self/SelfReleaseCommand.php index 7cea343f9..29f8bfd85 100644 --- a/src/Command/Self/SelfReleaseCommand.php +++ b/src/Command/Self/SelfReleaseCommand.php @@ -2,6 +2,7 @@ namespace Platformsh\Cli\Command\Self; use GuzzleHttp\Client; +use GuzzleHttp\Utils; use Platformsh\Cli\Command\CommandBase; use Platformsh\Cli\Util\VersionUtil; use Symfony\Component\Console\Input\InputArgument; @@ -353,7 +354,7 @@ protected function execute(InputInterface $input, OutputInterface $output) ], 'debug' => $output->isDebug(), ]); - $release = $response->json(); + $release = Utils::jsonDecode((string) $response->getBody(), true); $releaseUrl = $repoApiUrl . '/releases/' . $release['id']; $uploadUrl = preg_replace('/\{.+?\}/', '', $release['upload_url']); diff --git a/src/Command/Self/SelfStatsCommand.php b/src/Command/Self/SelfStatsCommand.php index 463e1756e..b6758a937 100644 --- a/src/Command/Self/SelfStatsCommand.php +++ b/src/Command/Self/SelfStatsCommand.php @@ -2,6 +2,7 @@ namespace Platformsh\Cli\Command\Self; use GuzzleHttp\Client; +use GuzzleHttp\Utils; use Platformsh\Cli\Command\CommandBase; use Platformsh\Cli\Service\PropertyFormatter; use Platformsh\Cli\Service\Table; @@ -35,7 +36,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { $repo = $this->config()->get('application.github_repo'); $repoUrl = implode('/', array_map('rawurlencode', explode('/', $repo))); - $releases = (new Client()) + $response = (new Client()) ->get('https://api.github.com/repos/' . $repoUrl . '/releases', [ 'headers' => [ 'Accept' => 'application/vnd.github.v3+json', @@ -44,7 +45,8 @@ protected function execute(InputInterface $input, OutputInterface $output) 'page' => (int) $input->getOption('page'), 'per_page' => (int) $input->getOption('count'), ], - ])->json(); + ]); + $releases = Utils::jsonDecode((string) $response->getBody(), true); if (empty($releases)) { $this->stdErr->writeln('No releases found.'); diff --git a/src/Command/Team/TeamListCommand.php b/src/Command/Team/TeamListCommand.php index 95d91b640..49f83212e 100644 --- a/src/Command/Team/TeamListCommand.php +++ b/src/Command/Team/TeamListCommand.php @@ -2,6 +2,7 @@ namespace Platformsh\Cli\Command\Team; use GuzzleHttp\Exception\BadResponseException; +use GuzzleHttp\Utils; use Platformsh\Cli\Console\ProgressMessage; use Platformsh\Cli\Model\ProjectRoles; use Platformsh\Cli\Service\PropertyFormatter; @@ -165,10 +166,11 @@ private function loadTeamsOnProject(Project $project) $progress->showIfOutputDecorated(sprintf('Loading project teams (page %d)...', $pageNumber)); } try { - $data = $httpClient->get($url)->json(); + $response = $httpClient->get($url); } catch (BadResponseException $e) { throw ApiResponseException::create($e->getRequest(), $e->getResponse(), $e); } + $data = Utils::jsonDecode((string) $response->getBody(), true); foreach ($data['items'] as $item) { $info[$item['team_id']] = $item['granted_at']; } diff --git a/src/Command/Variable/VariableCommandBase.php b/src/Command/Variable/VariableCommandBase.php index d2f53b3f1..434196aa4 100644 --- a/src/Command/Variable/VariableCommandBase.php +++ b/src/Command/Variable/VariableCommandBase.php @@ -4,8 +4,8 @@ use Platformsh\Cli\Command\CommandBase; use Platformsh\Cli\Console\AdaptiveTableCell; +use Platformsh\Client\Model\ApiResourceBase; use Platformsh\Client\Model\ProjectLevelVariable; -use Platformsh\Client\Model\Resource as ApiResource; use Platformsh\Client\Model\Variable as EnvironmentLevelVariable; use Platformsh\ConsoleForm\Field\BooleanField; use Platformsh\ConsoleForm\Field\Field; @@ -100,10 +100,8 @@ protected function getExistingVariable($name, $level = null, $messages = true) /** * Display a variable to stdout. - * - * @param \Platformsh\Client\Model\Resource $variable */ - protected function displayVariable(ApiResource $variable) + protected function displayVariable(ApiResourceBase $variable) { /** @var \Platformsh\Cli\Service\Table $table */ $table = $this->getService('table'); @@ -126,11 +124,11 @@ protected function displayVariable(ApiResource $variable) } /** - * @param ApiResource $variable + * @param ApiResourceBase $variable * * @return string */ - protected function getVariableLevel(ApiResource $variable) + protected function getVariableLevel(ApiResourceBase $variable) { if ($variable instanceof EnvironmentLevelVariable) { return self::LEVEL_ENVIRONMENT; diff --git a/src/Command/Version/VersionListCommand.php b/src/Command/Version/VersionListCommand.php index 10baf5c7f..8db5d7a2b 100644 --- a/src/Command/Version/VersionListCommand.php +++ b/src/Command/Version/VersionListCommand.php @@ -28,7 +28,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $environment = $this->getSelectedEnvironment(); $httpClient = $this->api()->getHttpClient(); - $data = $httpClient->get($environment->getLink('#versions'))->json(); + $response = $httpClient->get($environment->getLink('#versions')); + $data = \GuzzleHttp\Utils::jsonDecode((string) $response->getBody(), true); /** @var Table $table */ $table = $this->getService('table'); diff --git a/src/Command/WelcomeCommand.php b/src/Command/WelcomeCommand.php index 8152e3dee..d29fa442b 100644 --- a/src/Command/WelcomeCommand.php +++ b/src/Command/WelcomeCommand.php @@ -2,7 +2,6 @@ namespace Platformsh\Cli\Command; -use GuzzleHttp\Exception\BadResponseException; use Platformsh\Client\Model\Project; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; diff --git a/src/Console/EventSubscriber.php b/src/Console/EventSubscriber.php index fdf71aa48..9f91106da 100644 --- a/src/Console/EventSubscriber.php +++ b/src/Console/EventSubscriber.php @@ -4,20 +4,20 @@ use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Exception\ServerException; -use GuzzleHttp\Message\RequestInterface; use Platformsh\Cli\Service\Api; use Platformsh\Cli\Service\Config; use Platformsh\Cli\Exception\ConnectionFailedException; use Platformsh\Cli\Exception\LoginRequiredException; use Platformsh\Cli\Exception\PermissionDeniedException; use Platformsh\Client\Exception\EnvironmentStateException; +use Psr\Http\Message\RequestInterface; use Symfony\Component\Console\ConsoleEvents; use Symfony\Component\Console\Event\ConsoleErrorEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class EventSubscriber implements EventSubscriberInterface { - protected $config; + protected Config $config; /** * @param \Platformsh\Cli\Service\Config $config @@ -50,7 +50,7 @@ public function onException(ConsoleErrorEvent $event) if ($error instanceof ConnectException && strpos($error->getMessage(), 'cURL error 6') !== false) { $request = $error->getRequest(); $event->setError(new ConnectionFailedException( - "Failed to connect to host: " . $request->getHost() + "Failed to connect to host: " . $request->getUri()->getHost() . " \nPlease check your Internet connection.", $error )); @@ -61,7 +61,6 @@ public function onException(ConsoleErrorEvent $event) if (($error instanceof ClientException || $error instanceof ServerException) && ($response = $error->getResponse())) { $request = $error->getRequest(); - $requestConfig = $request->getConfig(); $json = (array) json_decode($response->getBody()->__toString(), true); // Create a friendlier message for the OAuth2 "Invalid refresh token" @@ -75,14 +74,14 @@ public function onException(ConsoleErrorEvent $event) $error )); $event->stopPropagation(); - } elseif ($response->getStatusCode() === 401 && $requestConfig['auth'] === 'oauth2') { + } elseif ($response->getStatusCode() === 401) { $event->setError(new LoginRequiredException( 'Unauthorized.', $this->config, $error )); $event->stopPropagation(); - } elseif ($response->getStatusCode() === 403 && $requestConfig['auth'] === 'oauth2') { + } elseif ($response->getStatusCode() === 403) { $event->setError(new PermissionDeniedException($this->permissionDeniedMessage($request), $error)); $event->stopPropagation(); } @@ -109,10 +108,10 @@ private function permissionDeniedMessage(RequestInterface $request) '/environments' => 'environment', '/organizations' => 'organization' ]; - $requestUrl = $request->getUrl(); + $requestUrl = $request->getUri(); $permissionTypes = []; foreach ($pathsPermissionTypes as $path => $pathsPermissionType) { - if (strpos($requestUrl, $path) !== false) { + if (str_contains($requestUrl, $path)) { $permissionTypes[$pathsPermissionType] = $pathsPermissionType; } } diff --git a/src/CredentialHelper/SessionStorage.php b/src/CredentialHelper/SessionStorage.php index 468b598e7..6727c1a15 100644 --- a/src/CredentialHelper/SessionStorage.php +++ b/src/CredentialHelper/SessionStorage.php @@ -2,7 +2,6 @@ namespace Platformsh\Cli\CredentialHelper; -use Platformsh\Client\Session\SessionInterface; use Platformsh\Client\Session\Storage\SessionStorageInterface; /** @@ -30,14 +29,14 @@ public function __construct(Manager $manager, $serverUrlPrefix) } /** - * @param SessionInterface $session + * @param string $sessionId * * @return string */ - private function serverUrl(SessionInterface $session) { + private function serverUrl(string $sessionId): string + { // Remove the 'cli-' prefix from the session ID; - $sessionId = $session->getId(); - if (strpos($sessionId, 'cli-') === 0) { + if (str_starts_with($sessionId, 'cli-')) { $sessionId = substr($sessionId, 4); } @@ -47,17 +46,15 @@ private function serverUrl(SessionInterface $session) { /** * {@inheritdoc} */ - public function load(SessionInterface $session) + public function load(string $sessionId): array { try { - $secret = $this->manager->get($this->serverUrl($session)); + $secret = $this->manager->get($this->serverUrl($sessionId)); } catch (\RuntimeException $e) { throw new \RuntimeException('Failed to load the session', 0, $e); } - if ($secret !== false) { - $session->setData($this->deserialize($secret)); - } + return $secret ? $this->deserialize($secret) : []; } /** @@ -107,10 +104,9 @@ private function listAllServerUrls() { /** * {@inheritdoc} */ - public function save(SessionInterface $session) + public function save($sessionId, array $data): void { - $serverUrl = $this->serverUrl($session); - $data = $session->getData(); + $serverUrl = $this->serverUrl($sessionId); if (empty($data)) { try { $this->manager->erase($serverUrl); @@ -120,7 +116,7 @@ public function save(SessionInterface $session) return; } try { - $this->manager->store($this->serverUrl($session), $this->serialize($data)); + $this->manager->store($serverUrl, $this->serialize($data)); } catch (\RuntimeException $e) { throw new \RuntimeException('Failed to store the session', 0, $e); } diff --git a/src/GuzzleDebugMiddleware.php b/src/GuzzleDebugMiddleware.php new file mode 100644 index 000000000..4798053a3 --- /dev/null +++ b/src/GuzzleDebugMiddleware.php @@ -0,0 +1,86 @@ +includeHeaders = $includeHeaders; + $this->stdErr = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; + } + + public function __invoke(callable $next): callable + { + return function (RequestInterface $request, array $options) use ($next): PromiseInterface { + if (!$this->stdErr->isVeryVerbose()) { + return $next($request, $options); + } + $started = microtime(true); + $seq = self::$requestSeq++; + + $this->stdErr->writeln(sprintf( + '> Making HTTP request #%d: %s', + $seq, $this->formatMessage($request, '> ') + )); + + /** @var PromiseInterface $promise */ + $promise = $next($request, $options); + + return $promise->then(function (ResponseInterface $response) use ($request, $seq, $started): ResponseInterface|PromiseInterface { + $this->stdErr->writeln(sprintf( + '\< Received response for #%d after %d ms: %s', + $seq, (microtime(true) - $started) * 1000, $this->formatMessage($response, '< ') + )); + return $response; + }); + }; + } + + private function formatMessage(RequestInterface|ResponseInterface $message, $headerPrefix = ''): string + { + $startLine = $message instanceof RequestInterface + ? $this->getRequestFirstLine($message) + : $this->getResponseFirstLine($message); + if (!$this->includeHeaders) { + return $startLine; + } + $headers = ''; + foreach ($message->getHeaders() as $name => $values) { + if ($name === 'Authorization') { + $headers .= "\r\n{$headerPrefix}{$name}: [redacted]"; + } else { + $headers .= "\r\n{$headerPrefix}{$name}: " . implode(', ', $values); + } + } + return $startLine . $headers; + } + + private function getRequestFirstLine(RequestInterface $request): string { + $method = $request->getMethod(); + $uri = $request->getUri(); + $protocolVersion = $request->getProtocolVersion(); + + return sprintf('%s %s HTTP/%s', $method, $uri, $protocolVersion); + } + + private function getResponseFirstLine(ResponseInterface $response): string { + $statusCode = $response->getStatusCode(); + $reasonPhrase = $response->getReasonPhrase(); + $protocolVersion = $response->getProtocolVersion(); + + return sprintf('HTTP/%s %d %s', $protocolVersion, $statusCode, $reasonPhrase); + } + +} diff --git a/src/GuzzleDebugSubscriber.php b/src/GuzzleDebugSubscriber.php deleted file mode 100644 index 5392a4dc2..000000000 --- a/src/GuzzleDebugSubscriber.php +++ /dev/null @@ -1,94 +0,0 @@ -includeHeaders = $includeHeaders; - $this->stdErr = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; - } - - public function getEvents() - { - return [ - // Errors are not handled: they are already printed via exceptions. - 'before' => ['onBefore', RequestEvents::LATE], - 'complete' => ['onComplete', RequestEvents::LATE], - ]; - } - - public function onBefore(BeforeEvent $event) - { - if ($this->stdErr->isVeryVerbose()) { - $req = $event->getRequest(); - if (!$req->getConfig()->hasKey('started_at')) { - $req->getConfig()->set('started_at', microtime(true)); - } - if ($req->getConfig()->hasKey('seq')) { - $seq = $req->getConfig()->get('seq'); - } else { - if (self::$requestSeq === null) { - self::$requestSeq = 1; - } - $seq = self::$requestSeq++; - $req->getConfig()->set('seq', $seq); - } - $this->stdErr->writeln(sprintf( - '> Making HTTP request #%d: %s', - $seq, $this->formatMessage($req, '> ') - )); - } - } - - private function formatMessage(MessageInterface $message, $headerPrefix = '') - { - $startLine = AbstractMessage::getStartLine($message); - if (!$this->includeHeaders) { - return $startLine; - } - $headers = ''; - foreach ($message->getHeaders() as $name => $values) { - if ($name === 'Authorization') { - $headers .= "\r\n{$headerPrefix}{$name}: [redacted]"; - } else { - $headers .= "\r\n{$headerPrefix}{$name}: " . implode(', ', $values); - } - } - return $startLine . $headers; - } - - public function onComplete(CompleteEvent $event) - { - if (!$this->stdErr->isVeryVerbose() || !$event->hasResponse()) { - return; - } - $seq = $event->getRequest()->getConfig()->get('seq'); - if (($startedAt = $event->getRequest()->getConfig()->get('started_at')) !== null) { - $this->stdErr->writeln(sprintf( - '\< Received response for #%d after %d ms: %s', - $seq, (microtime(true) - $startedAt) * 1000, $this->formatMessage($event->getResponse(), '< ') - )); - } else { - $this->stdErr->writeln(sprintf( - '\< Received response for #%d: %s', - $seq, $this->formatMessage($event->getResponse(), '< ') - )); - } - } -} diff --git a/src/Local/DependencyManager/DependencyManagerInterface.php b/src/Local/DependencyManager/DependencyManagerInterface.php index dd18297bf..e91ba8627 100644 --- a/src/Local/DependencyManager/DependencyManagerInterface.php +++ b/src/Local/DependencyManager/DependencyManagerInterface.php @@ -41,11 +41,11 @@ public function install($path, array $dependencies, $global = false); /** * Returns a list of "bin" directories in which dependencies are installed. * - * @param string $path The path prefix for the dependencies. + * @param string $prefix The path prefix for the dependencies. * * @return array An array of absolute paths. */ - public function getBinPaths($path); + public function getBinPaths($prefix); /** * Returns a list of environment variables for using installed dependencies. diff --git a/src/Model/EnvironmentDomain.php b/src/Model/EnvironmentDomain.php index 1fe2f05a4..24c935ba1 100644 --- a/src/Model/EnvironmentDomain.php +++ b/src/Model/EnvironmentDomain.php @@ -3,9 +3,9 @@ namespace Platformsh\Cli\Model; use GuzzleHttp\ClientInterface; +use Platformsh\Client\Model\ApiResourceBase; use Platformsh\Client\Model\Domain; use Platformsh\Client\Model\Environment; -use Platformsh\Client\Model\Resource; /** * A domain name on a Platform.sh environment. @@ -16,7 +16,7 @@ * @property-read string $created_at * @property-read string $updated_at */ -class EnvironmentDomain extends Resource +class EnvironmentDomain extends ApiResourceBase { public static function getList(Environment $environment, ClientInterface $client) { diff --git a/src/Service/Api.php b/src/Service/Api.php index 923bb0f4b..0e084139d 100644 --- a/src/Service/Api.php +++ b/src/Service/Api.php @@ -2,21 +2,22 @@ namespace Platformsh\Cli\Service; -use CommerceGuys\Guzzle\Oauth2\AccessToken; use Composer\CaBundle\CaBundle; use Doctrine\Common\Cache\CacheProvider; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; -use GuzzleHttp\Event\ErrorEvent; use GuzzleHttp\Exception\BadResponseException; -use GuzzleHttp\Message\ResponseInterface; +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\Utils; +use League\OAuth2\Client\Provider\Exception\IdentityProviderException; +use League\OAuth2\Client\Token\AccessToken; use Platformsh\Cli\CredentialHelper\KeyringUnavailableException; use Platformsh\Cli\CredentialHelper\Manager; use Platformsh\Cli\CredentialHelper\SessionStorage as CredentialHelperStorage; use Platformsh\Cli\Event\EnvironmentsChangedEvent; use Platformsh\Cli\Event\LoginRequiredEvent; use Platformsh\Cli\Exception\ProcessFailedException; -use Platformsh\Cli\GuzzleDebugSubscriber; +use Platformsh\Cli\GuzzleDebugMiddleware; use Platformsh\Cli\Model\Route; use Platformsh\Cli\Util\NestedArrayUtil; use Platformsh\Cli\Util\Sort; @@ -31,7 +32,7 @@ use Platformsh\Client\Model\Organization\Organization; use Platformsh\Client\Model\Project; use Platformsh\Client\Model\Ref\UserRef; -use Platformsh\Client\Model\Resource as ApiResource; +use Platformsh\Client\Model\ApiResourceBase as ApiResource; use Platformsh\Client\Model\SshKey; use Platformsh\Client\Model\Subscription; use Platformsh\Client\Model\Team\TeamMember; @@ -41,6 +42,8 @@ use Platformsh\Client\Session\Session; use Platformsh\Client\Session\SessionInterface; use Platformsh\Client\Session\Storage\File; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -287,7 +290,6 @@ private function getConnectorOptions() { $this->stdErr->writeln('Waiting for token refresh lock', OutputInterface::VERBOSITY_VERBOSE); }, function () use ($connector, $originalRefreshToken) { $session = $connector->getSession(); - $session->load(true); $accessToken = $this->tokenFromSession($session); return $accessToken && $accessToken->getRefreshToken() !== $originalRefreshToken ? $accessToken : null; @@ -297,7 +299,7 @@ private function getConnectorOptions() { $this->fileLock->release($refreshLockName); }; - $connectorOptions['on_refresh_error'] = function (BadResponseException $e) { + $connectorOptions['on_refresh_error'] = function (IdentityProviderException $e) { return $this->onRefreshError($e); }; @@ -307,6 +309,22 @@ private function getConnectorOptions() { $connectorOptions['centralized_permissions_enabled'] = $this->config->get('api.centralized_permissions') && $this->config->get('api.organizations'); + // Add middlewares. + $connectorOptions['middlewares'] = []; + // Debug responses. + $connectorOptions['middlewares'][] = new GuzzleDebugMiddleware($this->output, $this->config->getWithDefault('api.debug', false)); + // Handle 403 errors. + $connectorOptions['middlewares'][] = function (callable $handler) { + return function (RequestInterface $request, array $options) use ($handler) { + return $handler($request, $options)->then(function (ResponseInterface $response) use ($request) { + if ($response->getStatusCode() === 403) { + $this->on403($request); + } + return $response; + }); + }; + }; + return $connectorOptions; } @@ -354,7 +372,7 @@ private function onStepUpAuthResponse(ResponseInterface $response) { $session = $this->getClient(false)->getConnector()->getSession(); $previousAccessToken = $session->get('accessToken'); - $body = $response->json(); + $body = Utils::jsonDecode((string) $response->getBody(), true); $authMethods = isset($body['amr']) ? $body['amr'] : []; $maxAge = isset($body['max_age']) ? $body['max_age'] : null; @@ -373,33 +391,32 @@ private function onStepUpAuthResponse(ResponseInterface $response) { /** * Logs out and prompts for re-authentication after a token refresh error. * - * @param BadResponseException $e + * @param IdentityProviderException $e * * @return AccessToken|null */ - private function onRefreshError(BadResponseException $e) { - $response = $e->getResponse(); - if ($response && !in_array($response->getStatusCode(), [400, 401])) { + private function onRefreshError(IdentityProviderException $e): ?AccessToken + { + if ($this->inLoginCheck) { return null; } - if ($this->inLoginCheck) { + $data = $e->getResponseBody(); + if (!is_array($data) || !isset($data['error'])) { return null; } + $this->debug($e->getMessage()); + $this->logout(); - if ($this->isSsoSessionExpired($e)) { + if ($this->isSsoSessionExpired($data)) { $this->stdErr->writeln('Your SSO session has expired. You have been logged out.'); - } elseif ($this->isApiTokenInvalid($e)) { + } elseif ($this->isApiTokenInvalid($data)) { $this->stdErr->writeln('The API token is invalid.'); } else { $this->stdErr->writeln('Your session has expired. You have been logged out.'); } - if ($response && $this->stdErr->isVeryVerbose()) { - $this->stdErr->writeln($e->getMessage() . ApiResponseException::getErrorDetails($response)); - } - $this->stdErr->writeln(''); $this->dispatcher->dispatch('login_required', new LoginRequiredEvent([], null, $this->hasApiToken())); @@ -410,39 +427,24 @@ private function onRefreshError(BadResponseException $e) { /** * Tests if an HTTP response from refreshing a token indicates that the user's SSO session has expired. - * - * @param BadResponseException $e - * @return bool */ - private function isSsoSessionExpired(BadResponseException $e) + private function isSsoSessionExpired(array $data): bool { - if (!($response = $e->getResponse()) || $response->getStatusCode() !== 400) { - return false; + if (isset($data['error']) && $data['error'] === 'invalid_grant') { + return isset($errDetails['error_description']) + && str_contains($errDetails['error_description'], 'SSO session has expired'); } - $respBody = (string) $response->getBody(); - $errDetails = \json_decode($respBody, true); - return isset($errDetails['error_description']) - && strpos($errDetails['error_description'], 'SSO session has expired') !== false; + return false; } /** * Tests if an error from refreshing a token indicates that the user's API token is invalid. - * - * @param BadResponseException $e - * @return bool */ - private function isApiTokenInvalid(BadResponseException $e) + private function isApiTokenInvalid(mixed $body): bool { - if (!$response = $e->getResponse()) { - return false; - } - $reqBody = (string) $e->getRequest()->getBody(); - \parse_str($reqBody, $parsed); - if (isset($parsed['grant_type']) && $parsed['grant_type'] === 'api_token') { - $respBody = (string) $response->getBody(); - $errDetails = \json_decode($respBody, true); + if (is_array($body) && isset($body['error']) && $body['error'] === 'invalid_grant') { return isset($errDetails['error_description']) - && strpos($errDetails['error_description'], 'API token') !== false; + && str_contains($errDetails['error_description'], 'API token'); } return false; } @@ -484,18 +486,17 @@ private function tokenFromSession(SessionInterface $session) { */ public function getGuzzleOptions() { $options = [ - 'defaults' => [ - 'headers' => ['User-Agent' => $this->config->getUserAgent()], - 'debug' => false, - 'verify' => $this->config->getWithDefault('api.skip_ssl', false) ? false : $this->caBundlePath(), - 'proxy' => $this->guzzleProxyConfig(), - 'timeout' => $this->config->getWithDefault('api.default_timeout', 30), - ], + 'headers' => ['User-Agent' => $this->config->getUserAgent()], + 'debug' => false, + 'verify' => $this->config->getWithDefault('api.skip_ssl', false) ? false : $this->caBundlePath(), + 'proxy' => $this->guzzleProxyConfig(), + 'timeout' => $this->config->getWithDefault('api.default_timeout', 30), ]; - if ($this->output->isVeryVerbose()) { - $options['defaults']['subscribers'][] = new GuzzleDebugSubscriber($this->output, $this->config->getWithDefault('api.debug', false)); - } + // TODO provide this as a middleware + // if ($this->output->isVeryVerbose()) { + // $options['defaults']['subscribers'][] = new GuzzleDebugMiddleware($this->output, $this->config->getWithDefault('api.debug', false)); + // } if (extension_loaded('zlib')) { $options['defaults']['decode_content'] = true; @@ -576,20 +577,6 @@ public function getClient($autoLogin = true, $reset = false) if ($autoLogin && !$connector->isLoggedIn()) { $this->dispatcher->dispatch('login_required', new LoginRequiredEvent([], null, $this->hasApiToken())); } - - try { - $emitter = $connector->getClient()->getEmitter(); - $emitter->on('error', function (ErrorEvent $event) { - if ($event->getResponse() && $event->getResponse()->getStatusCode() === 403) { - $this->on403($event); - } - }); - if ($this->output->isVeryVerbose()) { - $emitter->attach(new GuzzleDebugSubscriber($this->output, $this->config->getWithDefault('api.debug', false))); - } - } catch (\RuntimeException $e) { - // Ignore errors if the user is not logged in at this stage. - } } return self::$client; @@ -1412,14 +1399,12 @@ public function getSiteUrl(Environment $environment, $appName, EnvironmentDeploy /** * React on an API 403 request. - * - * @param \GuzzleHttp\Event\ErrorEvent $event */ - private function on403(ErrorEvent $event) + private function on403(RequestInterface $request): void { - $url = $event->getRequest()->getUrl(); + $url = $request->getUri(); $path = parse_url($url, PHP_URL_PATH); - if ($path && strpos($path, '/api/projects/') === 0) { + if ($path && str_starts_with($path, '/api/projects/')) { // Clear the environments cache for environment request errors. if (preg_match('#^/api/projects/([^/]+?)/environments/#', $path, $matches)) { $this->clearEnvironmentsCache($matches[1]); @@ -1513,7 +1498,9 @@ public function checkUserVerification() } // Check the API to see if verification is required. - return $this->getHttpClient()->post( '/me/verification')->json(); + $request = new Request('POST', '/me/verification'); + $response = $this->getHttpClient()->send($request); + return Utils::jsonDecode((string) $response->getBody(), true); } /** @@ -1523,7 +1510,9 @@ public function checkUserVerification() */ public function checkCanCreate(Organization $org) { - return $this->getHttpClient()->get( $org->getUri() . '/subscriptions/can-create')->json(); + $request = new Request('GET', $org->getUri() . '/subscriptions/can-create'); + $response = $this->getHttpClient()->send($request); + return Utils::jsonDecode((string) $response->getBody(), true); } /** @@ -1700,7 +1689,9 @@ public function supportsSizingApi(Project $project, EnvironmentDeployment $deplo if (!empty($cachedSettings['sizing_api_enabled'])) { return true; } - $settings = $this->getHttpClient()->get($project->getUri() . '/settings')->json(); + $request = new Request('GET', $project->getUri() . '/settings'); + $response = $this->getHttpClient()->send($request); + $settings = Utils::jsonDecode((string) $response->getBody(), true); $this->cache->save($cacheKey, $settings, $this->config->get('api.projects_ttl')); return !empty($settings['sizing_api_enabled']); } diff --git a/src/Service/Identifier.php b/src/Service/Identifier.php index 8593a817c..33895dd11 100644 --- a/src/Service/Identifier.php +++ b/src/Service/Identifier.php @@ -196,7 +196,7 @@ private function getClusterHeader($url) return false; } } - $cluster = $response->getHeaderAsArray($this->config->get('detection.cluster_header')); + $cluster = $response->getHeader($this->config->get('detection.cluster_header')); $canCache = !empty($cluster) || ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300); if ($canCache) { diff --git a/src/Service/Relationships.php b/src/Service/Relationships.php index 9e18d8a69..edc999187 100644 --- a/src/Service/Relationships.php +++ b/src/Service/Relationships.php @@ -2,7 +2,8 @@ namespace Platformsh\Cli\Service; -use GuzzleHttp\Query; +use GuzzleHttp\Psr7\Query; +use GuzzleHttp\Psr7\Uri; use Platformsh\Cli\Model\Host\HostInterface; use Platformsh\Cli\Model\Host\LocalHost; use Platformsh\Cli\Util\OsUtil; @@ -370,7 +371,7 @@ public function buildUrl(array $instance) // The 'query' is expected to be a string. if (isset($parts['query']) && is_array($parts['query'])) { unset($parts['query']['is_master']); - $parts['query'] = (new Query($parts['query']))->__toString(); + $parts['query'] = Query::build($parts['query']); } // Special case #1: Solr. @@ -385,7 +386,7 @@ public function buildUrl(array $instance) $parts['scheme'] = 'postgresql'; } - return \GuzzleHttp\Url::buildUrl($parts); + return Uri::fromParts($parts)->__toString(); } /**