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();
}
/**