From 160604b0f06f98214b524b4f2cb64711e0f44853 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sat, 22 Jan 2022 01:31:38 +0100 Subject: [PATCH 01/80] First commit --- README.md | 2 +- composer.json | 11 +- composer.lock | 729 +++++++++++++++++++++++++++++-------- src/Client.php | 164 ++++----- src/Credentials.php | 32 ++ src/DBAL/RecordManager.php | 4 +- src/DBAL/Schema/Schema.php | 8 +- src/Endpoint.php | 58 --- 8 files changed, 686 insertions(+), 322 deletions(-) create mode 100644 src/Credentials.php delete mode 100644 src/Endpoint.php diff --git a/README.md b/README.md index 81f97a3..bced8a9 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ from the static method ```createFromConfig()```. Then, make your call: ```php -$result = $client->call($name, $method, $parameters = [], $options = []); +$result = $client->execute($name, $method, $parameters = [], $options = []); ``` Exceptions: diff --git a/composer.json b/composer.json index 9ec43b4..49bff4a 100644 --- a/composer.json +++ b/composer.json @@ -20,13 +20,14 @@ "require": { "php": ">=7.2", "ext-json": "*", - "ang3/php-xmlrpc-client": "^1.0.2" + "ang3/php-xmlrpc-client": "^1.0.2", + "symfony/http-client": "^4.0 || ^5.0 || ^6.0" }, "require-dev": { "roave/security-advisories": "dev-master", - "symfony/var-dumper": "^3.4 || ^4.0 || ^5.0", - "symfony/phpunit-bridge": "^3.4 || ^4.0 || ^5.0", - "symfony/property-info": "^3.4 || ^4.0 || ^5.0", - "symfony/inflector": "^3.4 || ^4.0 || ^5.0" + "symfony/var-dumper": ">=3.4", + "symfony/phpunit-bridge": ">=3.4", + "symfony/property-info": ">=3.4", + "symfony/inflector": ">=3.4" } } diff --git a/composer.lock b/composer.lock index cd95496..0545a9f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ba145b4e97be319e540aa774f3bd5571", + "content-hash": "1e5af61ac5ecb0e6124e570c374c9f6a", "packages": [ { "name": "ang3/php-xmlrpc-client", @@ -57,6 +57,581 @@ "source": "https://github.com/Ang3/php-xmlrpc-client/tree/master" }, "time": "2020-07-15T19:52:03+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-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/v2.4.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": "2021-03-23T23:28:01+00:00" + }, + { + "name": "symfony/http-client", + "version": "v5.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "5e344f1402584a56631c81a24ec9403e3159c790" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/5e344f1402584a56631c81a24ec9403e3159c790", + "reference": "5e344f1402584a56631c81a24ec9403e3159c790", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/http-client-contracts": "^2.4", + "symfony/polyfill-php73": "^1.11", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.0|^2|^3" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "2.4" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/http-kernel": "^4.4.13|^5.1.5|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/stopwatch": "^4.4|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-client/tree/v5.4.2" + }, + "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": "2021-12-29T10:10:35+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "ec82e57b5b714dbb69300d348bd840b345e24166" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ec82e57b5b714dbb69300d348bd840b345e24166", + "reference": "ec82e57b5b714dbb69300d348bd840b345e24166", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/http-client-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + } + }, + "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": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v2.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": "2021-11-03T09:24:47+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5", + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.24.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": "2021-06-05T21:20:04+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.23.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" + }, + "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": "2021-07-28T13:41:28+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "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": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.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": "2021-11-04T16:48:04+00:00" } ], "packages-dev": [ @@ -442,73 +1017,6 @@ ], "time": "2021-08-11T16:04:32+00:00" }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-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/v2.4.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": "2021-03-23T23:28:01+00:00" - }, { "name": "symfony/inflector", "version": "v5.3.4", @@ -989,89 +1497,6 @@ ], "time": "2021-05-27T12:26:48+00:00" }, - { - "name": "symfony/polyfill-php80", - "version": "v1.23.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" - }, - "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": "2021-07-28T13:41:28+00:00" - }, { "name": "symfony/property-info", "version": "v5.3.4", @@ -1346,5 +1771,5 @@ "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.1.0" } diff --git a/src/Client.php b/src/Client.php index b794b02..4e0b6f8 100755 --- a/src/Client.php +++ b/src/Client.php @@ -11,16 +11,19 @@ use Ang3\Component\Odoo\Exception\RequestException; use InvalidArgumentException; use Psr\Log\LoggerInterface; +use Symfony\Component\HttpClient\HttpClient; +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; class Client { use ExpressionBuilderAwareTrait; /** - * Endpoints. + * Services. */ - public const ENDPOINT_COMMON = 'xmlrpc/2/common'; - public const ENDPOINT_OBJECT = 'xmlrpc/2/object'; + public const SERVICE_COMMON = 'common'; + public const SERVICE_OBJECT = 'object'; /** * Special commands. @@ -56,18 +59,9 @@ class Client private $password; /** - * COMMON endpoint. - * - * @var Endpoint - */ - private $commonEndpoint; - - /** - * OBJECT endpoint. - * - * @var Endpoint + * @var HttpClientInterface */ - private $objectEndpoint; + private $httpClient; /** * ORM record manager. @@ -94,9 +88,11 @@ public function __construct(string $url, string $database, string $username, str $this->database = $database; $this->username = $username; $this->password = $password; + $this->httpClient = HttpClient::create([ + 'base_uri' => "$url/jsonrpc" + ]); $this->recordManager = new RecordManager($this); $this->logger = $logger; - $this->initEndpoints(); } /** @@ -130,8 +126,6 @@ public static function createFromConfig(array $config, LoggerInterface $logger = /** * Create a new record. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @throws InvalidArgumentException when $data is empty * @throws RequestException when request failed * @@ -143,14 +137,12 @@ public function create(string $modelName, array $data): int throw new InvalidArgumentException('Data cannot be empty'); } - return (int) $this->call($modelName, OrmQuery::CREATE, [$data]); + return (int) $this->execute($modelName, OrmQuery::CREATE, [$data]); } /** * Read records. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @param array|int $ids * * @throws RequestException when request failed @@ -159,14 +151,12 @@ public function read(string $modelName, $ids, array $options = []): array { $ids = [is_int($ids) ? [$ids] : (array) $ids]; - return (array) $this->call($modelName, OrmQuery::READ, $ids, $options); + return (array) $this->execute($modelName, OrmQuery::READ, $ids, $options); } /** * Update a record(s). * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @param array|int $ids * * @throws RequestException when request failed @@ -177,28 +167,24 @@ public function update(string $modelName, $ids, array $data = []): void return; } - $this->call($modelName, OrmQuery::WRITE, [(array) $ids, $data]); + $this->execute($modelName, OrmQuery::WRITE, [(array) $ids, $data]); } /** * Delete record(s). * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @param array|int $ids * * @throws RequestException when request failed */ public function delete(string $modelName, $ids): void { - $this->call($modelName, OrmQuery::UNLINK, [(array) $ids]); + $this->execute($modelName, OrmQuery::UNLINK, [(array) $ids]); } /** * Search one ID of record by criteria and options. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @param DomainInterface|array|null $criteria * * @throws InvalidArgumentException when $criteria value is not valid @@ -216,8 +202,6 @@ public function searchOne(string $modelName, $criteria = null, array $options = /** * Search all ID of record(s) with options. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @throws InvalidArgumentException when $criteria value is not valid * @throws RequestException when request failed * @@ -233,8 +217,6 @@ public function searchAll(string $modelName, array $options = []): array /** * Find ID of record(s) by criteria and options. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @param DomainInterface|array|null $criteria * * @throws InvalidArgumentException when $criteria value is not valid @@ -248,14 +230,12 @@ public function search(string $modelName, $criteria = null, array $options = []) unset($options['fields']); } - return (array) $this->call($modelName, OrmQuery::SEARCH, $this->expr()->normalizeDomains($criteria), $options); + return (array) $this->execute($modelName, OrmQuery::SEARCH, $this->expr()->normalizeDomains($criteria), $options); } /** * Find ONE record by ID and options. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @throws RequestException when request failed */ public function find(string $modelName, int $id, array $options = []): ?array @@ -266,8 +246,6 @@ public function find(string $modelName, int $id, array $options = []): ?array /** * Find ONE record by criteria and options. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @param DomainInterface|array|null $criteria * * @throws InvalidArgumentException when $criteria value is not valid @@ -283,8 +261,6 @@ public function findOneBy(string $modelName, $criteria = null, array $options = /** * Find all record(s) with options. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @throws RequestException when request failed * * @return array @@ -297,8 +273,6 @@ public function findAll(string $modelName, array $options = []): array /** * Find record(s) by criteria and options. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @param DomainInterface|array|null $criteria * * @throws InvalidArgumentException when $criteria value is not valid @@ -308,14 +282,12 @@ public function findAll(string $modelName, array $options = []): array */ public function findBy(string $modelName, $criteria = null, array $options = []): array { - return (array) $this->call($modelName, OrmQuery::SEARCH_READ, $this->expr()->normalizeDomains($criteria), $options); + return (array) $this->execute($modelName, OrmQuery::SEARCH_READ, $this->expr()->normalizeDomains($criteria), $options); } /** * Check if a record exists. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @throws RequestException when request failed */ public function exists(string $modelName, int $id): bool @@ -326,8 +298,6 @@ public function exists(string $modelName, int $id): bool /** * Count all records for a model. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @throws InvalidArgumentException when $criteria value is not valid * @throws RequestException when request failed */ @@ -339,8 +309,6 @@ public function countAll(string $modelName): int /** * Count number of records for a model and criteria. * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager instead. - * * @param DomainInterface|array|null $criteria * * @throws InvalidArgumentException when $criteria value is not valid @@ -348,7 +316,7 @@ public function countAll(string $modelName): int */ public function count(string $modelName, $criteria = null): int { - return (int) $this->call($modelName, OrmQuery::SEARCH_COUNT, $this->expr()->normalizeDomains($criteria)); + return (int) $this->execute($modelName, OrmQuery::SEARCH_COUNT, $this->expr()->normalizeDomains($criteria)); } /** @@ -358,49 +326,30 @@ public function count(string $modelName, $criteria = null): int */ public function listFields(string $modelName, array $options = []): array { - return (array) $this->call($modelName, self::LIST_FIELDS, [], $options); + return (array) $this->execute($modelName, self::LIST_FIELDS, [], $options); } /** * @return mixed */ - public function call(string $name, string $method, array $parameters = [], array $options = []) + public function execute(string $name, string $method, array $parameters = [], array $options = []) { - $loggerContext = [ - 'request_id' => uniqid('rpc', true), - 'name' => $name, - 'method' => $method, - 'parameters' => $parameters, - 'options' => $options, - ]; - - if ($this->logger) { - $this->logger->debug('Calling method {name}::{method}', $loggerContext); - } - - $result = $this->objectEndpoint->call('execute_kw', [ + return $this->request(self::SERVICE_OBJECT, 'execute', [ $this->database, $this->authenticate(), $this->password, $name, $method, $parameters, - $options, + $options ]); - - if ($this->logger) { - $loggedResult = is_scalar($result) ? $result : json_encode($result); - $this->logger->debug(sprintf('Request result: %s', $loggedResult), [ - 'request_id' => $loggerContext['request_id'], - ]); - } - - return $result; } - public function version(): array + public function version() { - return $this->commonEndpoint->call('version'); + $data = $this->request(self::SERVICE_COMMON, 'version'); + + return $data['result']; } /** @@ -409,12 +358,12 @@ public function version(): array public function authenticate(): int { if (null === $this->uid) { - $this->uid = $this->commonEndpoint->call('authenticate', [ + $data = $this->request(self::SERVICE_COMMON, 'login', [ $this->database, $this->username, - $this->password, - [], + $this->password ]); + $this->uid = $data['result']; if (!$this->uid || !is_int($this->uid)) { throw new AuthenticationException(); @@ -424,6 +373,41 @@ public function authenticate(): int return $this->uid; } + /** + * @return mixed + */ + public function request(string $service, string $method, array $parameters = []) + { + $context['request_id'] = uniqid('rpc', true); + if ($this->logger) { + $this->logger->info('JSON RPC request #{request_id} - {service}::{method} (uid: #{uid})', $context); + } + + $response = $this->httpClient->request('POST', '', [ + 'json' => [ + 'jsonrpc' => "2.0", + 'method' => 'call', + 'params' => [ + 'service' => $service, + 'method' => $method, + 'args' => $parameters + ], + 'id' => uniqid('odoo_request'), + ] + ]); + + $result = $response->getContent(); + + if ($this->logger) { + $loggedResult = is_scalar($result) ? $result : json_encode($result); + $this->logger->debug(sprintf('Request result: %s', $loggedResult), [ + 'request_id' => $context['request_id'], + ]); + } + + return json_decode($result, true); + } + public function getIdentifier(): string { $database = preg_replace('([^a-zA-Z0-9_])', '_', $this->database); @@ -440,7 +424,6 @@ public function getUrl(): string public function setUrl(string $url): self { $this->url = $url; - $this->initEndpoints(); return $this; } @@ -481,16 +464,6 @@ public function setPassword(string $password): self return $this; } - public function getCommonEndpoint(): Endpoint - { - return $this->commonEndpoint; - } - - public function getObjectEndpoint(): Endpoint - { - return $this->objectEndpoint; - } - public function getRecordManager(): RecordManager { return $this->recordManager; @@ -507,13 +480,4 @@ public function setLogger(?LoggerInterface $logger): self return $this; } - - /** - * @internal - */ - private function initEndpoints(): void - { - $this->commonEndpoint = new Endpoint($this->url.'/'.self::ENDPOINT_COMMON); - $this->objectEndpoint = new Endpoint($this->url.'/'.self::ENDPOINT_OBJECT); - } } diff --git a/src/Credentials.php b/src/Credentials.php new file mode 100644 index 0000000..939b9aa --- /dev/null +++ b/src/Credentials.php @@ -0,0 +1,32 @@ +username = $username; + $this->password = $password; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getPassword(): string + { + return $this->password; + } +} \ No newline at end of file diff --git a/src/DBAL/RecordManager.php b/src/DBAL/RecordManager.php index b023f6a..cbcea38 100644 --- a/src/DBAL/RecordManager.php +++ b/src/DBAL/RecordManager.php @@ -254,10 +254,10 @@ public function executeQuery(QueryInterface $query) $options = $query->getOptions(); if (!$options) { - return $this->client->call($query->getName(), $query->getMethod(), $query->getParameters()); + return $this->client->execute($query->getName(), $query->getMethod(), $query->getParameters()); } - return $this->client->call($query->getName(), $query->getMethod(), $query->getParameters(), $options); + return $this->client->execute($query->getName(), $query->getMethod(), $query->getParameters(), $options); } public function getClient(): Client diff --git a/src/DBAL/Schema/Schema.php b/src/DBAL/Schema/Schema.php index 966fc4c..21fbb4e 100644 --- a/src/DBAL/Schema/Schema.php +++ b/src/DBAL/Schema/Schema.php @@ -36,7 +36,7 @@ public function getModel(string $modelName): Model if (!isset($this->loadedModels[$modelName])) { $expr = $this->client->expr(); - $modelData = $this->client->call(self::IR_MODEL, OrmQuery::SEARCH_READ, $expr->normalizeDomains($expr->eq('model', $modelName))); + $modelData = $this->client->execute(self::IR_MODEL, OrmQuery::SEARCH_READ, $expr->normalizeDomains($expr->eq('model', $modelName))); $this->loadedModels[$modelName] = $this->createModel($modelData[0]); } @@ -56,7 +56,7 @@ public function hasModel(string $modelName): bool public function getModelNames(): array { if (!$this->modelNames) { - $this->modelNames = array_column($this->client->call(self::IR_MODEL, OrmQuery::SEARCH_READ, [[]], [ + $this->modelNames = array_column($this->client->execute(self::IR_MODEL, OrmQuery::SEARCH_READ, [[]], [ 'fields' => ['model'], ]), 'model'); } @@ -70,7 +70,7 @@ public function getModelNames(): array private function createModel(array $modelData): Model { $expr = $this->client->expr(); - $fields = $this->client->call( + $fields = $this->client->execute( self::IR_MODEL_FIELDS, OrmQuery::SEARCH_READ, $expr->normalizeDomains($expr->eq('model_id', $modelData['id'])) @@ -81,7 +81,7 @@ private function createModel(array $modelData): Model $selectionsIds = array_filter($fieldData['selection_ids'] ?? []); if (!empty($selectionsIds)) { - $choices = $this->client->call( + $choices = $this->client->execute( self::IR_MODEL_FIELD_SELECTION, OrmQuery::SEARCH_READ, $expr->normalizeDomains($expr->eq('field_id', $fieldData['id'])) diff --git a/src/Endpoint.php b/src/Endpoint.php deleted file mode 100644 index 73c7059..0000000 --- a/src/Endpoint.php +++ /dev/null @@ -1,58 +0,0 @@ -url = $url; - $this->client = new XmlRpcClient($url); - } - - /** - * @throws RequestException when request failed - * - * @return mixed - */ - public function call(string $method, array $args = []) - { - try { - return $this->client->call($method, $args); - } catch (XmlRpcRemoteException $exception) { - if (preg_match('#cannot marshal None unless allow_none is enabled#', $exception->getMessage())) { - return null; - } - - throw RemoteException::create($exception); - } catch (Throwable $exception) { - throw new RequestException($exception->getMessage(), $exception->getCode(), $exception); - } - } - - public function getUrl(): string - { - return $this->url; - } - - public function getClient(): XmlRpcClient - { - return $this->client; - } -} From 3600c545f166e9001669c1e377e5c04ce52fa8a2 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sat, 22 Jan 2022 01:46:46 +0100 Subject: [PATCH 02/80] Create composer.lock --- composer.lock | 3177 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3177 insertions(+) create mode 100644 composer.lock diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..8eb744f --- /dev/null +++ b/composer.lock @@ -0,0 +1,3177 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "4b49594ef16939b738c5e56bc7bd85c2", + "packages": [ + { + "name": "ang3/php-xmlrpc-client", + "version": "v1.0.3", + "source": { + "type": "git", + "url": "https://github.com/Ang3/php-xmlrpc-client.git", + "reference": "c51c06ac2293977e21ce0be546891a5431284d1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ang3/php-xmlrpc-client/zipball/c51c06ac2293977e21ce0be546891a5431284d1a", + "reference": "c51c06ac2293977e21ce0be546891a5431284d1a", + "shasum": "" + }, + "require": { + "ext-xmlrpc": "*", + "php": "^7.2|^8.0" + }, + "require-dev": { + "roave/security-advisories": "dev-master", + "symfony/phpunit-bridge": "^3.4 || ^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ang3\\Component\\XmlRpc\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joanis Rouanet", + "email": "joanis.ang3@gmail.com" + } + ], + "description": "PHP XML-RPC client", + "keywords": [ + "client", + "php", + "rpc", + "xml", + "xmlrpc" + ], + "support": { + "issues": "https://github.com/Ang3/php-xmlrpc-client/issues", + "source": "https://github.com/Ang3/php-xmlrpc-client/tree/v1.0.3" + }, + "time": "2021-12-13T18:06:52+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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/v2.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": "2021-07-12T14:48:14+00:00" + }, + { + "name": "symfony/http-client", + "version": "v5.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "5e344f1402584a56631c81a24ec9403e3159c790" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/5e344f1402584a56631c81a24ec9403e3159c790", + "reference": "5e344f1402584a56631c81a24ec9403e3159c790", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/http-client-contracts": "^2.4", + "symfony/polyfill-php73": "^1.11", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.0|^2|^3" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "2.4" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/http-kernel": "^4.4.13|^5.1.5|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/stopwatch": "^4.4|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-client/tree/v5.4.2" + }, + "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": "2021-12-29T10:10:35+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "ec82e57b5b714dbb69300d348bd840b345e24166" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ec82e57b5b714dbb69300d348bd840b345e24166", + "reference": "ec82e57b5b714dbb69300d348bd840b345e24166", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/http-client-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + } + }, + "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": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v2.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": "2021-11-03T09:24:47+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5", + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.24.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": "2021-06-05T21:20:04+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9", + "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.24.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": "2021-09-13T13:58:33+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "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": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.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": "2021-11-04T16:48:04+00:00" + } + ], + "packages-dev": [ + { + "name": "composer/pcre", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560", + "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/1.0.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-21T20:24:37+00:00" + }, + { + "name": "composer/semver", + "version": "3.2.7", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "deac27056b57e46faf136fae7b449eeaa71661ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/deac27056b57e46faf136fae7b449eeaa71661ee", + "reference": "deac27056b57e46faf136fae7b449eeaa71661ee", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.54", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.2.7" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-04T09:57:54+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "12f1b79476638a5615ed00ea6adbb269cec96fd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/12f1b79476638a5615ed00ea6adbb269cec96fd8", + "reference": "12f1b79476638a5615ed00ea6adbb269cec96fd8", + "shasum": "" + }, + "require": { + "composer/pcre": "^1", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-04T18:29:42+00:00" + }, + { + "name": "doctrine/annotations", + "version": "1.13.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "5b668aef16090008790395c02c893b1ba13f7e08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/5b668aef16090008790395c02c893b1ba13f7e08", + "reference": "5b668aef16090008790395c02c893b1ba13f7e08", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^6.0 || ^8.1", + "phpstan/phpstan": "^0.12.20", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", + "symfony/cache": "^4.4 || ^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.13.2" + }, + "time": "2021-08-05T19:00:23+00:00" + }, + { + "name": "doctrine/lexer", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c", + "reference": "9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0", + "phpstan/phpstan": "1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/1.2.2" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2022-01-12T08:27:12+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "333f15e07c866e33e2765e84ba1e0b88e6a3af3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/333f15e07c866e33e2765e84ba1e0b88e6a3af3b", + "reference": "333f15e07c866e33e2765e84ba1e0b88e6a3af3b", + "shasum": "" + }, + "require": { + "composer/semver": "^3.2", + "composer/xdebug-handler": "^3.0", + "doctrine/annotations": "^1.13", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0", + "php-cs-fixer/diff": "^2.0", + "symfony/console": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0", + "symfony/finder": "^5.4 || ^6.0", + "symfony/options-resolver": "^5.4 || ^6.0", + "symfony/polyfill-mbstring": "^1.23", + "symfony/polyfill-php80": "^1.23", + "symfony/polyfill-php81": "^1.23", + "symfony/process": "^5.4 || ^6.0", + "symfony/stopwatch": "^5.4 || ^6.0" + }, + "require-dev": { + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^1.5", + "mikey179/vfsstream": "^1.6.10", + "php-coveralls/php-coveralls": "^2.5.2", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", + "phpspec/prophecy": "^1.15", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "phpunitgoodpractices/polyfill": "^1.5", + "phpunitgoodpractices/traits": "^1.9.1", + "symfony/phpunit-bridge": "^6.0", + "symfony/yaml": "^5.4 || ^6.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", + "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2022-01-14T00:29:20+00:00" + }, + { + "name": "php-cs-fixer/diff", + "version": "v2.0.2", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/diff.git", + "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", + "symfony/process": "^3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "sebastian/diff v3 backport support for PHP 5.6+", + "homepage": "https://github.com/PHP-CS-Fixer", + "keywords": [ + "diff" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/diff/issues", + "source": "https://github.com/PHP-CS-Fixer/diff/tree/v2.0.2" + }, + "time": "2020-10-14T08:32:19+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "0.12.99", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7", + "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7", + "shasum": "" + }, + "require": { + "php": "^7.1|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.12-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/0.12.99" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpstan", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2021-09-12T20:09:55+00:00" + }, + { + "name": "psr/cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/master" + }, + "time": "2016-08-06T20:24:11+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "roave/security-advisories", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Roave/SecurityAdvisories.git", + "reference": "26f418efa3cfbf0b404ee2b504bff7ecefe92df7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/26f418efa3cfbf0b404ee2b504bff7ecefe92df7", + "reference": "26f418efa3cfbf0b404ee2b504bff7ecefe92df7", + "shasum": "" + }, + "conflict": { + "3f/pygmentize": "<1.2", + "adodb/adodb-php": "<5.20.12", + "akaunting/akaunting": "<2.1.13", + "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", + "amazing/media2click": ">=1,<1.3.3", + "amphp/artax": "<1.0.6|>=2,<2.0.6", + "amphp/http": "<1.0.1", + "amphp/http-client": ">=4,<4.4", + "anchorcms/anchor-cms": "<=0.12.7", + "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6", + "area17/twill": "<1.2.5|>=2,<2.5.3", + "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", + "aws/aws-sdk-php": ">=3,<3.2.1", + "bagisto/bagisto": "<0.1.5", + "barrelstrength/sprout-base-email": "<1.2.7", + "barrelstrength/sprout-forms": "<3.9", + "baserproject/basercms": "<4.5.4", + "billz/raspap-webgui": "<=2.6.6", + "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", + "bolt/bolt": "<3.7.2", + "bolt/core": "<4.1.13", + "bottelet/flarepoint": "<2.2.1", + "brightlocal/phpwhois": "<=4.2.5", + "buddypress/buddypress": "<7.2.1", + "bugsnag/bugsnag-laravel": ">=2,<2.0.2", + "cachethq/cachet": "<2.5.1", + "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.5.18|>=3.6,<3.6.15|>=3.7,<3.7.7", + "cardgate/magento2": "<2.0.33", + "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", + "cartalyst/sentry": "<=2.1.6", + "catfan/medoo": "<1.7.5", + "centreon/centreon": "<20.10.7", + "cesnet/simplesamlphp-module-proxystatistics": "<3.1", + "codeception/codeception": "<3.1.3|>=4,<4.1.22", + "codeigniter/framework": "<=3.0.6", + "codeigniter4/framework": "<4.1.6", + "codiad/codiad": "<=2.8.4", + "composer/composer": "<1.10.23|>=2-alpha.1,<2.1.9", + "concrete5/concrete5": "<8.5.5", + "concrete5/core": "<8.5.7", + "contao-components/mediaelement": ">=2.14.2,<2.21.1", + "contao/core": ">=2,<3.5.39", + "contao/core-bundle": ">=4,<4.4.56|>=4.5,<4.9.18|>=4.10,<4.11.7|= 4.10.0", + "contao/listing-bundle": ">=4,<4.4.8", + "craftcms/cms": "<3.7.14", + "croogo/croogo": "<3.0.7", + "datadog/dd-trace": ">=0.30,<0.30.2", + "david-garcia/phpwhois": "<=4.3.1", + "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1", + "directmailteam/direct-mail": "<5.2.4", + "doctrine/annotations": ">=1,<1.2.7", + "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", + "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", + "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2|>=3,<3.1.4", + "doctrine/doctrine-bundle": "<1.5.2", + "doctrine/doctrine-module": "<=0.7.1", + "doctrine/mongodb-odm": ">=1,<1.0.2", + "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", + "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", + "dolibarr/dolibarr": "<=14.0.4|>= 3.3.beta1, < 13.0.2", + "dompdf/dompdf": ">=0.6,<0.6.2", + "drupal/core": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", + "drupal/drupal": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", + "dweeves/magmi": "<=0.7.24", + "ecodev/newsletter": "<=4", + "elgg/elgg": "<3.3.24|>=4,<4.0.5", + "endroid/qr-code-bundle": "<3.4.2", + "enshrined/svg-sanitize": "<0.13.1", + "erusev/parsedown": "<1.7.2", + "ether/logs": "<3.0.4", + "ezsystems/demobundle": ">=5.4,<5.4.6.1", + "ezsystems/ez-support-tools": ">=2.2,<2.2.3", + "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1", + "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1", + "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<=1.5.25", + "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", + "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<=1.3.1", + "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", + "ezsystems/ezplatform-richtext": ">=2.3,<=2.3.7", + "ezsystems/ezplatform-user": ">=1,<1.0.1", + "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<=7.5.15.1", + "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1", + "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", + "ezsystems/repository-forms": ">=2.3,<2.3.2.1", + "ezyang/htmlpurifier": "<4.1.1", + "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", + "feehi/cms": "<=2.1.1", + "feehi/feehicms": "<=0.1.3", + "firebase/php-jwt": "<2", + "flarum/core": ">=1,<=1.0.1", + "flarum/sticky": ">=0.1-beta.14,<=0.1-beta.15", + "flarum/tags": "<=0.1-beta.13", + "fluidtypo3/vhs": "<5.1.1", + "fooman/tcpdf": "<6.2.22", + "forkcms/forkcms": "<=5.9.2", + "fossar/tcpdf-parser": "<6.2.22", + "francoisjacquet/rosariosis": "<8.1.1", + "friendsofsymfony/oauth2-php": "<1.3", + "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", + "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", + "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", + "froala/wysiwyg-editor": "<3.2.7", + "fuel/core": "<1.8.1", + "gaoming13/wechat-php-sdk": "<=1.10.2", + "getgrav/grav": "<=1.7.24", + "getkirby/cms": "<3.5.8", + "getkirby/panel": "<2.5.14", + "gilacms/gila": "<=1.11.4", + "globalpayments/php-sdk": "<2", + "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", + "gree/jose": "<=2.2", + "gregwar/rst": "<1.0.3", + "grumpydictator/firefly-iii": "<5.6.5", + "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", + "helloxz/imgurl": "<=2.31", + "hillelcoren/invoice-ninja": "<5.3.35", + "hjue/justwriting": "<=1", + "hov/jobfair": "<1.0.13|>=2,<2.0.2", + "ibexa/post-install": "<=1.0.4", + "icecoder/icecoder": "<=8", + "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", + "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.99999|>=4.2,<=4.2.99999|>=5,<=5.0.99999|>=5.1,<=5.1.99999|>=5.2,<=5.2.99999|>=5.3,<=5.3.99999|>=5.4,<=5.4.99999|>=5.5,<=5.5.49|>=5.6,<=5.6.99999|>=5.7,<=5.7.99999|>=5.8,<=5.8.99999|>=6,<6.18.31|>=7,<7.22.4", + "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", + "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", + "impresscms/impresscms": "<=1.4.2", + "in2code/femanager": "<5.5.1|>=6,<6.3.1", + "intelliants/subrion": "<=4.2.1", + "ivankristianto/phpwhois": "<=4.3", + "jackalope/jackalope-doctrine-dbal": "<1.7.4", + "james-heinrich/getid3": "<1.9.21", + "joomla/archive": "<1.1.10", + "joomla/session": "<1.3.1", + "jsmitty12/phpwhois": "<5.1", + "kazist/phpwhois": "<=4.2.6", + "kevinpapst/kimai2": "<1.16.7", + "kitodo/presentation": "<3.1.2", + "klaviyo/magento2-extension": ">=1,<3", + "kreait/firebase-php": ">=3.2,<3.8.1", + "la-haute-societe/tcpdf": "<6.2.22", + "laminas/laminas-http": "<2.14.2", + "laravel/framework": "<6.20.42|>=7,<7.30.6|>=8,<8.75", + "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", + "latte/latte": "<2.10.8", + "lavalite/cms": "<=5.8", + "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", + "league/commonmark": "<0.18.3", + "league/flysystem": "<1.1.4|>=2,<2.1.1", + "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", + "librenms/librenms": "<=21.11", + "limesurvey/limesurvey": "<3.27.19", + "livewire/livewire": ">2.2.4,<2.2.6", + "lms/routes": "<2.1.1", + "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", + "magento/community-edition": ">=2,<2.2.10|>=2.3,<2.3.3", + "magento/magento1ce": "<1.9.4.3", + "magento/magento1ee": ">=1,<1.14.4.3", + "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2-p.2", + "marcwillmann/turn": "<0.3.3", + "mautic/core": "<4|= 2.13.1", + "mediawiki/core": ">=1.27,<1.27.6|>=1.29,<1.29.3|>=1.30,<1.30.2|>=1.31,<1.31.9|>=1.32,<1.32.6|>=1.32.99,<1.33.3|>=1.33.99,<1.34.3|>=1.34.99,<1.35", + "microweber/microweber": "<1.2.11", + "miniorange/miniorange-saml": "<1.4.3", + "mittwald/typo3_forum": "<1.2.1", + "modx/revolution": "<2.8", + "monolog/monolog": ">=1.8,<1.12", + "moodle/moodle": "<3.7.9|>=3.8,<3.8.8|>=3.9,<3.9.5|>=3.10-beta,<3.10.2", + "namshi/jose": "<2.2", + "neoan3-apps/template": "<1.1.1", + "neos/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", + "neos/form": ">=1.2,<4.3.3|>=5,<5.0.9|>=5.1,<5.1.3", + "neos/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.9.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", + "neos/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", + "netgen/tagsbundle": ">=3.4,<3.4.11|>=4,<4.0.15", + "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", + "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", + "nilsteampassnet/teampass": "<=2.1.27.36", + "nukeviet/nukeviet": "<4.3.4", + "nystudio107/craft-seomatic": "<3.3", + "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", + "october/backend": "<1.1.2", + "october/cms": "= 1.1.1|= 1.0.471|= 1.0.469|>=1.0.319,<1.0.469", + "october/october": ">=1.0.319,<1.0.466|>=2.1,<2.1.12", + "october/rain": "<1.0.472|>=1.1,<1.1.2", + "october/system": "<1.0.473|>=1.1,<1.1.6|>=2.1,<2.1.12", + "onelogin/php-saml": "<2.10.4", + "oneup/uploader-bundle": "<1.9.3|>=2,<2.1.5", + "opencart/opencart": "<=3.0.3.2", + "openid/php-openid": "<2.3", + "openmage/magento-lts": "<19.4.15|>=20,<20.0.13", + "orchid/platform": ">=9,<9.4.4", + "oro/crm": ">=1.7,<1.7.4|>=3.1,<4.1.17|>=4.2,<4.2.7", + "oro/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<4.2.8", + "padraic/humbug_get_contents": "<1.1.2", + "pagarme/pagarme-php": ">=0,<3", + "pagekit/pagekit": "<=1.0.18", + "paragonie/random_compat": "<2", + "passbolt/passbolt_api": "<2.11", + "paypal/merchant-sdk-php": "<3.12", + "pear/archive_tar": "<1.4.14", + "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", + "personnummer/personnummer": "<3.0.2", + "phanan/koel": "<5.1.4", + "phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7", + "phpmailer/phpmailer": "<6.5", + "phpmussel/phpmussel": ">=1,<1.6", + "phpmyadmin/phpmyadmin": "<4.9.6|>=5,<5.0.3", + "phpoffice/phpexcel": "<1.8.2", + "phpoffice/phpspreadsheet": "<1.16", + "phpseclib/phpseclib": "<2.0.31|>=3,<3.0.7", + "phpservermon/phpservermon": "<=3.5.2", + "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", + "phpwhois/phpwhois": "<=4.2.5", + "phpxmlrpc/extras": "<0.6.1", + "pimcore/pimcore": "<10.2.9", + "pocketmine/pocketmine-mp": "<4.0.7", + "pressbooks/pressbooks": "<5.18", + "prestashop/autoupgrade": ">=4,<4.10.1", + "prestashop/contactform": ">1.0.1,<4.3", + "prestashop/gamification": "<2.3.2", + "prestashop/prestashop": ">=1.7.5,<=1.7.8.1", + "prestashop/productcomments": ">=4,<4.2.1", + "prestashop/ps_emailsubscription": "<2.6.1", + "prestashop/ps_facetedsearch": "<3.4.1", + "prestashop/ps_linklist": "<3.1", + "privatebin/privatebin": "<1.2.2|>=1.3,<1.3.2", + "propel/propel": ">=2-alpha.1,<=2-alpha.7", + "propel/propel1": ">=1,<=1.7.1", + "pterodactyl/panel": "<1.7", + "pusher/pusher-php-server": "<2.2.1", + "pwweb/laravel-core": "<=0.3.6-beta", + "rainlab/debugbar-plugin": "<3.1", + "remdex/livehelperchat": "<3.92", + "rmccue/requests": ">=1.6,<1.8", + "robrichards/xmlseclibs": "<3.0.4", + "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", + "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", + "scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11", + "sensiolabs/connect": "<4.2.3", + "serluck/phpwhois": "<=4.2.6", + "shopware/core": "<=6.4.6", + "shopware/platform": "<=6.4.6", + "shopware/production": "<=6.3.5.2", + "shopware/shopware": "<5.7.7", + "showdoc/showdoc": "<2.10", + "silverstripe/admin": ">=1,<1.8.1", + "silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2", + "silverstripe/cms": "<4.3.6|>=4.4,<4.4.4", + "silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1", + "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", + "silverstripe/framework": "<4.7.4", + "silverstripe/graphql": "<3.5.2|>=4-alpha.1,<4-alpha.2", + "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", + "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4", + "silverstripe/subsites": ">=2,<2.1.1", + "silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1", + "silverstripe/userforms": "<3", + "simple-updates/phpwhois": "<=1", + "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", + "simplesamlphp/simplesamlphp": "<1.18.6", + "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", + "simplito/elliptic-php": "<1.0.6", + "slim/slim": "<2.6", + "smarty/smarty": "<3.1.43|>=4,<4.0.3", + "snipe/snipe-it": "<5.3.5", + "socalnick/scn-social-auth": "<1.15.2", + "socialiteproviders/steam": "<1.1", + "spoonity/tcpdf": "<6.2.22", + "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", + "ssddanbrown/bookstack": "<21.12.1", + "stormpath/sdk": ">=0,<9.9.99", + "studio-42/elfinder": "<2.1.59", + "subrion/cms": "<=4.2.1", + "sulu/sulu": "= 2.4.0-RC1|<1.6.44|>=2,<2.2.18|>=2.3,<2.3.8", + "swiftmailer/swiftmailer": ">=4,<5.4.5", + "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", + "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", + "sylius/grid-bundle": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", + "sylius/paypal-plugin": ">=1,<1.2.4|>=1.3,<1.3.1", + "sylius/resource-bundle": "<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", + "sylius/sylius": "<1.6.9|>=1.7,<1.7.9|>=1.8,<1.8.3|>=1.9,<1.9.5", + "symbiote/silverstripe-multivaluefield": ">=3,<3.0.99", + "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", + "symbiote/silverstripe-versionedfiles": "<=2.0.3", + "symfont/process": ">=0,<4", + "symfony/cache": ">=3.1,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", + "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", + "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", + "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", + "symfony/http-kernel": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.4.13|>=5,<5.1.5|>=5.2,<5.3.12", + "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", + "symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1", + "symfony/mime": ">=4.3,<4.3.8", + "symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/polyfill": ">=1,<1.10", + "symfony/polyfill-php55": ">=1,<1.10", + "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/routing": ">=2,<2.0.19", + "symfony/security": ">=2,<2.7.51|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.8", + "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11|>=5.3,<5.3.12", + "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9", + "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8|>=5.3,<5.3.2", + "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", + "symfony/symfony": ">=2,<3.4.49|>=4,<4.4.35|>=5,<5.3.12", + "symfony/translation": ">=2,<2.0.17", + "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", + "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", + "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", + "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", + "t3/dce": ">=2.2,<2.6.2", + "t3g/svg-sanitizer": "<1.0.3", + "tecnickcom/tcpdf": "<6.2.22", + "thelia/backoffice-default-template": ">=2.1,<2.1.2", + "thelia/thelia": ">=2.1-beta.1,<2.1.3", + "theonedemon/phpwhois": "<=4.2.5", + "tinymce/tinymce": "<5.10", + "titon/framework": ">=0,<9.9.99", + "topthink/framework": "<6.0.9", + "topthink/think": "<=6.0.9", + "topthink/thinkphp": "<=3.2.3", + "tribalsystems/zenario": "<8.8.53370", + "truckersmp/phpwhois": "<=4.3.1", + "twig/twig": "<1.38|>=2,<2.7", + "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.38|>=9,<9.5.29|>=10,<10.4.19|>=11,<11.5", + "typo3/cms-backend": ">=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", + "typo3/cms-core": ">=6.2,<=6.2.56|>=7,<=7.6.52|>=8,<=8.7.41|>=9,<9.5.29|>=10,<10.4.19|>=11,<11.5", + "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", + "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", + "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", + "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", + "typo3/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", + "typo3fluid/fluid": ">=2,<2.0.8|>=2.1,<2.1.7|>=2.2,<2.2.4|>=2.3,<2.3.7|>=2.4,<2.4.4|>=2.5,<2.5.11|>=2.6,<2.6.10", + "ua-parser/uap-php": "<3.8", + "unisharp/laravel-filemanager": "<=2.3", + "userfrosting/userfrosting": ">=0.3.1,<4.6.3", + "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", + "vanilla/safecurl": "<0.9.2", + "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4", + "vrana/adminer": "<4.7.9", + "wallabag/tcpdf": "<6.2.22", + "wanglelecc/laracms": "<=1.0.3", + "web-auth/webauthn-framework": ">=3.3,<3.3.4", + "webcoast/deferred-image-processing": "<1.0.2", + "wikimedia/parsoid": "<0.12.2", + "willdurand/js-translation-bundle": "<2.1.1", + "wp-cli/wp-cli": "<2.5", + "yetiforce/yetiforce-crm": "<=6.3", + "yidashi/yii2cmf": "<=2", + "yii2mod/yii2-cms": "<1.9.2", + "yiisoft/yii": ">=1.1.14,<1.1.15", + "yiisoft/yii2": "<2.0.38", + "yiisoft/yii2-bootstrap": "<2.0.4", + "yiisoft/yii2-dev": "<2.0.43", + "yiisoft/yii2-elasticsearch": "<2.0.5", + "yiisoft/yii2-gii": "<2.0.4", + "yiisoft/yii2-jui": "<2.0.4", + "yiisoft/yii2-redis": "<2.0.8", + "yoast-seo-for-typo3/yoast_seo": "<7.2.3", + "yourls/yourls": "<=1.8.2", + "zendesk/zendesk_api_client_php": "<2.2.11", + "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", + "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", + "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", + "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", + "zendframework/zend-developer-tools": ">=1.2.2,<1.2.3", + "zendframework/zend-diactoros": ">=1,<1.8.4", + "zendframework/zend-feed": ">=1,<2.10.3", + "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-http": ">=1,<2.8.1", + "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", + "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", + "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", + "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4", + "zendframework/zend-validator": ">=2.3,<2.3.6", + "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", + "zendframework/zendframework": "<=3", + "zendframework/zendframework1": "<1.12.20", + "zendframework/zendopenid": ">=2,<2.0.2", + "zendframework/zendxml": ">=1,<1.0.1", + "zetacomponents/mail": "<1.8.2", + "zf-commons/zfc-user": "<1.2.2", + "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", + "zfr/zfr-oauth2-server-module": "<0.1.2", + "zoujingli/thinkadmin": "<6.0.22" + }, + "type": "metapackage", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "role": "maintainer" + }, + { + "name": "Ilya Tribusean", + "email": "slash3b@gmail.com", + "role": "maintainer" + } + ], + "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "support": { + "issues": "https://github.com/Roave/SecurityAdvisories/issues", + "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" + }, + "funding": [ + { + "url": "https://github.com/Ocramius", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/roave/security-advisories", + "type": "tidelift" + } + ], + "time": "2022-01-21T23:14:14+00:00" + }, + { + "name": "symfony/console", + "version": "v5.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "a2c6b7ced2eb7799a35375fb9022519282b5405e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/a2c6b7ced2eb7799a35375fb9022519282b5405e", + "reference": "a2c6b7ced2eb7799a35375fb9022519282b5405e", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.4.2" + }, + "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": "2021-12-20T16:11:12+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/27d39ae126352b9fa3be5e196ccf4617897be3eb", + "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/event-dispatcher-contracts": "^2|^3", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "symfony/dependency-injection": "<4.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.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": "2021-11-23T10:19:22+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", + "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "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": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.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": "2021-07-12T14:48:14+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "731f917dc31edcffec2c6a777f3698c33bea8f01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/731f917dc31edcffec2c6a777f3698c33bea8f01", + "reference": "731f917dc31edcffec2c6a777f3698c33bea8f01", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.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": "2021-10-28T13:39:27+00:00" + }, + { + "name": "symfony/finder", + "version": "v5.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "e77046c252be48c48a40816187ed527703c8f76c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/e77046c252be48c48a40816187ed527703c8f76c", + "reference": "e77046c252be48c48a40816187ed527703c8f76c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.4.2" + }, + "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": "2021-12-15T11:06:13+00:00" + }, + { + "name": "symfony/inflector", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/inflector.git", + "reference": "994f78cae91007021142b1e5df87fb297fe9f8d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/inflector/zipball/994f78cae91007021142b1e5df87fb297fe9f8d8", + "reference": "994f78cae91007021142b1e5df87fb297fe9f8d8", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16", + "symfony/string": "^5.3.10|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Inflector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts words between their singular and plural forms (English only)", + "homepage": "https://symfony.com", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string", + "symfony", + "words" + ], + "support": { + "source": "https://github.com/symfony/inflector/tree/v5.4.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" + } + ], + "abandoned": "EnglishInflector from the String component", + "time": "2021-11-23T10:19:22+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "b0fb78576487af19c500aaddb269fd36701d4847" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b0fb78576487af19c500aaddb269fd36701d4847", + "reference": "b0fb78576487af19c500aaddb269fd36701d4847", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php73": "~1.0", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v5.4.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": "2021-11-23T10:19:22+00:00" + }, + { + "name": "symfony/phpunit-bridge", + "version": "v6.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/phpunit-bridge.git", + "reference": "5d6cc6720085084f504d2482fc4a2f268784006b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/5d6cc6720085084f504d2482fc4a2f268784006b", + "reference": "5d6cc6720085084f504d2482fc4a2f268784006b", + "shasum": "" + }, + "require": { + "php": ">=7.1.3" + }, + "conflict": { + "phpunit/phpunit": "<7.5|9.1.2" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.1|^3.0", + "symfony/error-handler": "^5.4|^6.0" + }, + "suggest": { + "symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + }, + "bin": [ + "bin/simple-phpunit" + ], + "type": "symfony-bridge", + "extra": { + "thanks": { + "name": "phpunit/phpunit", + "url": "https://github.com/sebastianbergmann/phpunit" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Bridge\\PhpUnit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides utilities for PHPUnit, especially user deprecation notices management", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.0.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": "2021-11-29T15:32:57+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "30885182c981ab175d4d034db0f6f469898070ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", + "reference": "30885182c981ab175d4d034db0f6f469898070ab", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.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": "2021-10-20T20:35:02+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "81b86b50cf841a64252b439e738e97f4a34e2783" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783", + "reference": "81b86b50cf841a64252b439e738e97f4a34e2783", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + }, + "files": [ + "bootstrap.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": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.24.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": "2021-11-23T21:10:46+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.24.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": "2021-02-19T12:13:01+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.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": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.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": "2021-11-30T18:21:41+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.24.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": "2021-09-13T13:58:11+00:00" + }, + { + "name": "symfony/process", + "version": "v5.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "2b3ba8722c4aaf3e88011be5e7f48710088fb5e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/2b3ba8722c4aaf3e88011be5e7f48710088fb5e4", + "reference": "2b3ba8722c4aaf3e88011be5e7f48710088fb5e4", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.4.2" + }, + "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": "2021-12-27T21:01:00+00:00" + }, + { + "name": "symfony/property-info", + "version": "v5.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-info.git", + "reference": "a32f813896ffb3b4710fca5af5b05bef600cf4f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-info/zipball/a32f813896ffb3b4710fca5af5b05bef600cf4f0", + "reference": "a32f813896ffb3b4710fca5af5b05bef600cf4f0", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/dependency-injection": "<4.4" + }, + "require-dev": { + "doctrine/annotations": "^1.10.4", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "phpstan/phpdoc-parser": "^1.0", + "symfony/cache": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/serializer": "^4.4|^5.0|^6.0" + }, + "suggest": { + "phpdocumentor/reflection-docblock": "To use the PHPDoc", + "psr/cache-implementation": "To cache results", + "symfony/doctrine-bridge": "To use Doctrine metadata", + "symfony/serializer": "To use Serializer metadata" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyInfo\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Dunglas", + "email": "dunglas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Extracts information about PHP class' properties using metadata of popular sources", + "homepage": "https://symfony.com", + "keywords": [ + "doctrine", + "phpdoc", + "property", + "symfony", + "type", + "validator" + ], + "support": { + "source": "https://github.com/symfony/property-info/tree/v5.4.2" + }, + "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": "2021-12-26T13:30:54+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "208ef96122bfed82a8f3a61458a07113a08bdcfe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/208ef96122bfed82a8f3a61458a07113a08bdcfe", + "reference": "208ef96122bfed82a8f3a61458a07113a08bdcfe", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/service-contracts": "^1|^2|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v5.4.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": "2021-11-23T10:19:22+00:00" + }, + { + "name": "symfony/string", + "version": "v5.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d", + "reference": "e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "conflict": { + "symfony/translation-contracts": ">=3.0" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "files": [ + "Resources/functions.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.4.2" + }, + "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": "2021-12-16T21:52:00+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v5.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "1b56c32c3679002b3a42384a580e16e2600f41c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/1b56c32c3679002b3a42384a580e16e2600f41c1", + "reference": "1b56c32c3679002b3a42384a580e16e2600f41c1", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "phpunit/phpunit": "<5.4.3", + "symfony/console": "<4.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/uid": "^5.1|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v5.4.2" + }, + "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": "2021-12-29T10:10:35+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "roave/security-advisories": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.2", + "ext-json": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.1.0" +} From 20a4a5e3fb54b1c0872c874be2d11927714ecb78 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sat, 22 Jan 2022 16:02:35 +0100 Subject: [PATCH 03/80] Fix JSON calls --- .php-cs-fixer.cache | 2 +- src/Client.php | 55 ++++++++++++----------- src/Credentials.php | 32 ------------- src/DBAL/Expression/CompositeDomain.php | 2 - src/DBAL/Expression/ExpressionBuilder.php | 27 +++++++++-- src/Exception/RemoteException.php | 21 +++++---- 6 files changed, 65 insertions(+), 74 deletions(-) delete mode 100644 src/Credentials.php diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index d772139..56d890d 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"7.4.24","version":"3.5.0:v3.5.0#333f15e07c866e33e2765e84ba1e0b88e6a3af3b","indent":" ","lineEnding":"\n","rules":{"array_syntax":true,"backtick_to_shell_exec":true,"binary_operator_spaces":true,"blank_line_before_statement":{"statements":["return"]},"braces":{"allow_single_line_anonymous_class_with_empty_body":true,"allow_single_line_closure":true},"cast_spaces":true,"class_attributes_separation":{"elements":{"method":"one"}},"class_definition":{"single_line":true},"clean_namespace":true,"concat_space":true,"echo_tag_syntax":true,"empty_loop_body":{"style":"braces"},"empty_loop_condition":true,"fully_qualified_strict_types":true,"function_typehint_space":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"method_argument_space":{"on_multiline":"ignore"},"native_function_casing":true,"native_function_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_blank_lines":{"tokens":["case","continue","curly_brace_block","default","extra","parenthesis_brace_block","square_brace_block","switch","throw","use"]},"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_mixed":true,"allow_unused_params":true},"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","return","switch_case","yield","yield_from"]},"no_unneeded_curly_braces":{"namespaces":true},"no_unset_cast":true,"no_unused_imports":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"object_operator_without_whitespace":true,"ordered_imports":true,"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":true,"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_without_name":true,"protected_to_private":true,"semicolon_after_instruction":true,"single_class_element_per_statement":true,"single_line_comment_style":{"comment_types":["hash"]},"single_line_throw":true,"single_quote":true,"single_space_after_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"switch_continue_to_break":true,"trailing_comma_in_multiline":true,"trim_array_spaces":true,"types_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":true,"blank_line_after_opening_tag":true,"compact_nullable_typehint":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"return_type_declaration":true,"short_scalar_cast":true,"single_blank_line_before_namespace":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true},"hashes":{"src\\Client.php":43118670,"src\\DBAL\\Expression\\CollectionOperation.php":2939472196,"src\\DBAL\\Expression\\Comparison.php":4237095711,"src\\DBAL\\Expression\\CompositeDomain.php":354536269,"src\\DBAL\\Expression\\ConversionException.php":107016403,"src\\DBAL\\Expression\\CustomDomain.php":3489925073,"src\\DBAL\\Expression\\DomainInterface.php":3877458668,"src\\DBAL\\Expression\\ExpressionBuilder.php":1657354195,"src\\DBAL\\Expression\\ExpressionBuilderAwareTrait.php":3796741222,"src\\DBAL\\Query\\AbstractQuery.php":2930363797,"src\\DBAL\\Query\\NativeQuery.php":2335259464,"src\\DBAL\\Query\\NoResultException.php":1860820185,"src\\DBAL\\Query\\NoUniqueResultException.php":1183675382,"src\\DBAL\\Query\\OrmQuery.php":632474652,"src\\DBAL\\Query\\QueryBuilder.php":3685258252,"src\\DBAL\\Query\\QueryException.php":754063031,"src\\DBAL\\Query\\QueryInterface.php":3966439455,"src\\DBAL\\RecordManager.php":1113792547,"src\\DBAL\\Repository\\RecordNotFoundException.php":3899077087,"src\\DBAL\\Repository\\RecordRepository.php":377204786,"src\\DBAL\\Schema\\Choice.php":2356126375,"src\\DBAL\\Schema\\Field.php":2188356149,"src\\DBAL\\Schema\\Model.php":145712388,"src\\DBAL\\Schema\\Schema.php":240188129,"src\\DBAL\\Schema\\SchemaException.php":195684899,"src\\DBAL\\Schema\\Selection.php":1041914487,"src\\Endpoint.php":1412879540,"src\\Exception\\AuthenticationException.php":2083575707,"src\\Exception\\ExceptionInterface.php":141028630,"src\\Exception\\MissingConfigParameterException.php":3577118993,"src\\Exception\\RemoteException.php":2392187581,"src\\Exception\\RequestException.php":1948041852}} \ No newline at end of file +{"php":"7.4.24","version":"3.5.0:v3.5.0#333f15e07c866e33e2765e84ba1e0b88e6a3af3b","indent":" ","lineEnding":"\n","rules":{"array_syntax":true,"backtick_to_shell_exec":true,"binary_operator_spaces":true,"blank_line_before_statement":{"statements":["return"]},"braces":{"allow_single_line_anonymous_class_with_empty_body":true,"allow_single_line_closure":true},"cast_spaces":true,"class_attributes_separation":{"elements":{"method":"one"}},"class_definition":{"single_line":true},"clean_namespace":true,"concat_space":true,"echo_tag_syntax":true,"empty_loop_body":{"style":"braces"},"empty_loop_condition":true,"fully_qualified_strict_types":true,"function_typehint_space":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"method_argument_space":{"on_multiline":"ignore"},"native_function_casing":true,"native_function_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_blank_lines":{"tokens":["case","continue","curly_brace_block","default","extra","parenthesis_brace_block","square_brace_block","switch","throw","use"]},"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_mixed":true,"allow_unused_params":true},"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","return","switch_case","yield","yield_from"]},"no_unneeded_curly_braces":{"namespaces":true},"no_unset_cast":true,"no_unused_imports":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"object_operator_without_whitespace":true,"ordered_imports":true,"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":true,"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_without_name":true,"protected_to_private":true,"semicolon_after_instruction":true,"single_class_element_per_statement":true,"single_line_comment_style":{"comment_types":["hash"]},"single_line_throw":true,"single_quote":true,"single_space_after_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"switch_continue_to_break":true,"trailing_comma_in_multiline":true,"trim_array_spaces":true,"types_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":true,"blank_line_after_opening_tag":true,"compact_nullable_typehint":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"return_type_declaration":true,"short_scalar_cast":true,"single_blank_line_before_namespace":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true},"hashes":{"src\\Client.php":1876081534,"src\\DBAL\\Expression\\CollectionOperation.php":2939472196,"src\\DBAL\\Expression\\Comparison.php":4237095711,"src\\DBAL\\Expression\\CompositeDomain.php":3256396640,"src\\DBAL\\Expression\\ConversionException.php":107016403,"src\\DBAL\\Expression\\CustomDomain.php":3489925073,"src\\DBAL\\Expression\\DomainInterface.php":3877458668,"src\\DBAL\\Expression\\ExpressionBuilder.php":1215622714,"src\\DBAL\\Expression\\ExpressionBuilderAwareTrait.php":3796741222,"src\\DBAL\\Query\\AbstractQuery.php":2930363797,"src\\DBAL\\Query\\NativeQuery.php":2335259464,"src\\DBAL\\Query\\NoResultException.php":1860820185,"src\\DBAL\\Query\\NoUniqueResultException.php":1183675382,"src\\DBAL\\Query\\OrmQuery.php":632474652,"src\\DBAL\\Query\\QueryBuilder.php":1139452024,"src\\DBAL\\Query\\QueryException.php":754063031,"src\\DBAL\\Query\\QueryInterface.php":3966439455,"src\\DBAL\\RecordManager.php":3573959836,"src\\DBAL\\Repository\\RecordNotFoundException.php":3899077087,"src\\DBAL\\Repository\\RecordRepository.php":377204786,"src\\DBAL\\Schema\\Choice.php":2356126375,"src\\DBAL\\Schema\\Field.php":2188356149,"src\\DBAL\\Schema\\Model.php":145712388,"src\\DBAL\\Schema\\Schema.php":3354099713,"src\\DBAL\\Schema\\SchemaException.php":195684899,"src\\DBAL\\Schema\\Selection.php":1041914487,"src\\Endpoint.php":1412879540,"src\\Exception\\AuthenticationException.php":2083575707,"src\\Exception\\ExceptionInterface.php":141028630,"src\\Exception\\MissingConfigParameterException.php":3577118993,"src\\Exception\\RemoteException.php":3918346446,"src\\Exception\\RequestException.php":1948041852}} \ No newline at end of file diff --git a/src/Client.php b/src/Client.php index 7c57bb6..d87e0ac 100644 --- a/src/Client.php +++ b/src/Client.php @@ -7,12 +7,12 @@ use Ang3\Component\Odoo\DBAL\RecordManager; use Ang3\Component\Odoo\Exception\AuthenticationException; use Ang3\Component\Odoo\Exception\MissingConfigParameterException; +use Ang3\Component\Odoo\Exception\RemoteException; use Ang3\Component\Odoo\Exception\RequestException; use InvalidArgumentException; use Psr\Log\LoggerInterface; use Symfony\Component\HttpClient\HttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; -use Symfony\Contracts\HttpClient\ResponseInterface; class Client { @@ -88,7 +88,7 @@ public function __construct(string $url, string $database, string $username, str $this->username = $username; $this->password = $password; $this->httpClient = HttpClient::create([ - 'base_uri' => "$url/jsonrpc" + 'base_uri' => "$url/jsonrpc", ]); $this->recordManager = new RecordManager($this); $this->logger = $logger; @@ -123,7 +123,7 @@ public static function createFromConfig(array $config, LoggerInterface $logger = } /** - * Create a new record. + * Creates a new record and returns the new ID. * * @throws InvalidArgumentException when $data is empty * @throws RequestException when request failed @@ -150,7 +150,7 @@ public function read(string $modelName, $ids, array $options = []): array { $ids = [is_int($ids) ? [$ids] : (array) $ids]; - return (array) $this->execute($modelName, OrmQuery::READ, $ids, $options); + return (array) $this->execute($modelName, OrmQuery::READ, [$ids], $options); } /** @@ -275,7 +275,9 @@ public function findAll(string $modelName, array $options = []): array */ public function findBy(string $modelName, iterable $criteria = null, array $options = []): array { - return (array) $this->execute($modelName, OrmQuery::SEARCH_READ, $this->expr()->normalizeDomains($criteria), $options); + $parameters = $this->expr()->normalizeDomains($criteria); + + return (array) $this->execute($modelName, OrmQuery::SEARCH_READ, $parameters, $options); } /** @@ -307,7 +309,7 @@ public function countAll(string $modelName): int * @throws InvalidArgumentException when $criteria value is not valid * @throws RequestException when request failed */ - public function count(string $modelName, iterable $criteria = null) + public function count(string $modelName, iterable $criteria = null): int { return $this->execute($modelName, OrmQuery::SEARCH_COUNT, $this->expr()->normalizeDomains($criteria)); } @@ -327,24 +329,20 @@ public function listFields(string $modelName, array $options = []): array */ public function execute(string $name, string $method, array $parameters = [], array $options = []) { - return $this->request(self::SERVICE_OBJECT, 'execute', [ + return $this->request(self::SERVICE_OBJECT, 'execute_kw', $this->database, $this->authenticate(), $this->password, $name, $method, - [ - $parameters, - $options - ] - ]); + [$parameters], + $options + ); } - public function version() + public function version(): array { - $data = $this->request(self::SERVICE_COMMON, 'version'); - - return $data['result']; + return $this->request(self::SERVICE_COMMON, 'version'); } /** @@ -353,12 +351,11 @@ public function version() public function authenticate(): int { if (null === $this->uid) { - $data = $this->request(self::SERVICE_COMMON, 'login', [ + $this->uid = $this->request(self::SERVICE_COMMON, 'login', $this->database, $this->username, $this->password - ]); - $this->uid = $data['result']; + ); if (!$this->uid || !is_int($this->uid)) { throw new AuthenticationException(); @@ -369,9 +366,11 @@ public function authenticate(): int } /** + * @param mixed[] $arguments + * * @return mixed */ - public function request(string $service, string $method, array $parameters = []) + public function request(string $service, string $method, ...$arguments) { $context['request_id'] = uniqid('rpc', true); if ($this->logger) { @@ -380,27 +379,33 @@ public function request(string $service, string $method, array $parameters = []) $response = $this->httpClient->request('POST', '', [ 'json' => [ - 'jsonrpc' => "2.0", + 'jsonrpc' => '2.0', 'method' => 'call', 'params' => [ 'service' => $service, 'method' => $method, - 'args' => $parameters + 'args' => $arguments, ], 'id' => uniqid('odoo_request'), - ] + ], ]); $result = $response->getContent(); if ($this->logger) { - $loggedResult = is_scalar($result) ? $result : json_encode($result); + $loggedResult = $result; $this->logger->debug(sprintf('Request result: %s', $loggedResult), [ 'request_id' => $context['request_id'], ]); } - return json_decode($result, true); + $payload = json_decode($result, true); + + if (is_array($payload['error'] ?? null)) { + throw RemoteException::create($payload); + } + + return $payload['result']; } public function getIdentifier(): string diff --git a/src/Credentials.php b/src/Credentials.php deleted file mode 100644 index 939b9aa..0000000 --- a/src/Credentials.php +++ /dev/null @@ -1,32 +0,0 @@ -username = $username; - $this->password = $password; - } - - public function getUsername(): string - { - return $this->username; - } - - public function getPassword(): string - { - return $this->password; - } -} \ No newline at end of file diff --git a/src/DBAL/Expression/CompositeDomain.php b/src/DBAL/Expression/CompositeDomain.php index 31a3a17..d10f0ea 100644 --- a/src/DBAL/Expression/CompositeDomain.php +++ b/src/DBAL/Expression/CompositeDomain.php @@ -129,8 +129,6 @@ private function prepare(): ?DomainInterface if (!$domains[$key]) { unset($domains[$key]); } - - continue; } } diff --git a/src/DBAL/Expression/ExpressionBuilder.php b/src/DBAL/Expression/ExpressionBuilder.php index b4be514..f418968 100644 --- a/src/DBAL/Expression/ExpressionBuilder.php +++ b/src/DBAL/Expression/ExpressionBuilder.php @@ -242,14 +242,21 @@ public function clearRecords(): CollectionOperation public function normalizeDomains(iterable $criteria = null): array { if (!$criteria) { - return [[]]; + return []; } if (is_array($criteria)) { $normalizedCriteria = $this->andX(); foreach ($criteria as $fieldName => $value) { - $normalizedCriteria->add($this->eq($fieldName, $this->formatValue($value))); + $comparison = $this->eq($fieldName, $this->formatValue($value)); + + if (1 === count($criteria)) { + $normalizedCriteria = $comparison; + break; + } + + $normalizedCriteria->add($comparison); } $criteria = $normalizedCriteria; @@ -259,7 +266,21 @@ public function normalizeDomains(iterable $criteria = null): array throw new InvalidArgumentException(sprintf('Expected parameter #1 of type %s|array<%s|array>, %s given', DomainInterface::class, DomainInterface::class, gettype($criteria))); } - return $criteria instanceof CompositeDomain ? [$this->formatValue($criteria->toArray())] : [[$this->formatValue($criteria->toArray())]]; + $criteriaArray = $this->formatValue($criteria->toArray()); + + if (!$criteriaArray) { + return $this->normalizeDomains(); + } + + if ($criteria instanceof CompositeDomain) { + dump($criteriaArray); + + return $criteriaArray; + } + + dump([$criteriaArray]); + + return [$criteriaArray]; } /** diff --git a/src/Exception/RemoteException.php b/src/Exception/RemoteException.php index ac1919d..1612664 100644 --- a/src/Exception/RemoteException.php +++ b/src/Exception/RemoteException.php @@ -2,22 +2,21 @@ namespace Ang3\Component\Odoo\Exception; -use Ang3\Component\XmlRpc\Exception\RemoteException as XmlRemoteException; - class RemoteException extends RequestException { /** * @var array */ - protected $xmlTrace = []; + protected $remoteTrace = []; - public static function create(XmlRemoteException $remoteException): self + public static function create(array $payload): self { - $errorCode = $remoteException->getCode(); - $errorMessage = $remoteException->getMessage(); + $errorCode = $payload['error']['code']; + $errorMessage = $payload['error']['message']; + $remoteTrace = trim($payload['error']['data']['debug']); - if (preg_match('#Traceback \(most recent call last\)#', $errorMessage)) { - $messages = array_filter(explode("\n", $errorMessage)); + if (preg_match('#'.preg_quote('Traceback (most recent call last):').'#', $remoteTrace)) { + $messages = array_filter(explode("\n", $remoteTrace)); foreach ($messages as $key => $message) { $messages[$key] = trim($message); @@ -46,7 +45,7 @@ public static function create(XmlRemoteException $remoteException): self } $exception = new self(implode("\n", $messageParts), $errorCode); - $exception->xmlTrace = array_reverse($trace); + $exception->remoteTrace = array_reverse($trace); return $exception; } @@ -54,8 +53,8 @@ public static function create(XmlRemoteException $remoteException): self return new self($errorMessage, $errorCode); } - public function getXmlTrace(): array + public function getRemoteTrace(): array { - return $this->xmlTrace; + return $this->remoteTrace; } } From 7f3180d5f5b347db5596b1738aa5754fc6083b6d Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sat, 22 Jan 2022 16:20:30 +0100 Subject: [PATCH 04/80] Fixed execute_kw calls --- src/Client.php | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Client.php b/src/Client.php index d87e0ac..0d299e7 100644 --- a/src/Client.php +++ b/src/Client.php @@ -136,7 +136,9 @@ public function create(string $modelName, array $data): int throw new InvalidArgumentException('Data cannot be empty'); } - return (int) $this->execute($modelName, OrmQuery::CREATE, [$data]); + $result = $this->execute($modelName, OrmQuery::CREATE, [[$data]]); + + return (int) array_shift($result); } /** @@ -166,7 +168,8 @@ public function update(string $modelName, $ids, array $data = []): void return; } - $this->execute($modelName, OrmQuery::WRITE, [(array) $ids, $data]); + $ids = is_array($ids) ? $ids : [(int) $ids]; + $this->execute($modelName, OrmQuery::WRITE, [$ids, $data]); } /** @@ -225,7 +228,7 @@ public function search(string $modelName, iterable $criteria = null, array $opti unset($options['fields']); } - return (array) $this->execute($modelName, OrmQuery::SEARCH, $this->expr()->normalizeDomains($criteria), $options); + return (array) $this->execute($modelName, OrmQuery::SEARCH, [$this->expr()->normalizeDomains($criteria)], $options); } /** @@ -275,9 +278,7 @@ public function findAll(string $modelName, array $options = []): array */ public function findBy(string $modelName, iterable $criteria = null, array $options = []): array { - $parameters = $this->expr()->normalizeDomains($criteria); - - return (array) $this->execute($modelName, OrmQuery::SEARCH_READ, $parameters, $options); + return (array) $this->execute($modelName, OrmQuery::SEARCH_READ, [$this->expr()->normalizeDomains($criteria)], $options); } /** @@ -311,7 +312,7 @@ public function countAll(string $modelName): int */ public function count(string $modelName, iterable $criteria = null): int { - return $this->execute($modelName, OrmQuery::SEARCH_COUNT, $this->expr()->normalizeDomains($criteria)); + return $this->execute($modelName, OrmQuery::SEARCH_COUNT, [$this->expr()->normalizeDomains($criteria)]); } /** @@ -335,7 +336,7 @@ public function execute(string $name, string $method, array $parameters = [], ar $this->password, $name, $method, - [$parameters], + $parameters, $options ); } @@ -377,17 +378,21 @@ public function request(string $service, string $method, ...$arguments) $this->logger->info('JSON RPC request #{request_id} - {service}::{method} (uid: #{uid})', $context); } - $response = $this->httpClient->request('POST', '', [ - 'json' => [ - 'jsonrpc' => '2.0', - 'method' => 'call', - 'params' => [ - 'service' => $service, - 'method' => $method, - 'args' => $arguments, - ], - 'id' => uniqid('odoo_request'), + $data = [ + 'jsonrpc' => '2.0', + 'method' => 'call', + 'params' => [ + 'service' => $service, + 'method' => $method, + 'args' => $arguments, ], + 'id' => uniqid('odoo_request'), + ]; + + dump($data); + + $response = $this->httpClient->request('POST', '', [ + 'json' => $data, ]); $result = $response->getContent(); From 1d7943a49f7bcef3fdb9cd1a3ee5e1a6e1520038 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sat, 22 Jan 2022 23:19:58 +0100 Subject: [PATCH 05/80] Delete DBAL features --- .php-cs-fixer.cache | 2 +- composer.lock | 19 +- src/Client.php | 51 +- src/DBAL/Query/AbstractQuery.php | 133 ----- src/DBAL/Query/NativeQuery.php | 7 - src/DBAL/Query/NoResultException.php | 11 - src/DBAL/Query/NoUniqueResultException.php | 11 - src/DBAL/Query/OrmQuery.php | 188 ------ src/DBAL/Query/QueryBuilder.php | 546 ------------------ src/DBAL/Query/QueryException.php | 7 - src/DBAL/Query/QueryInterface.php | 14 - src/DBAL/RecordManager.php | 277 --------- .../Repository/RecordNotFoundException.php | 34 -- src/DBAL/Repository/RecordRepository.php | 254 -------- src/DBAL/Schema/Choice.php | 43 -- src/DBAL/Schema/Field.php | 238 -------- src/DBAL/Schema/Model.php | 141 ----- src/DBAL/Schema/Schema.php | 114 ---- src/DBAL/Schema/SchemaException.php | 18 - src/DBAL/Schema/Selection.php | 67 --- .../Domain}/Comparison.php | 2 +- .../Domain}/CompositeDomain.php | 2 +- .../Domain}/CustomDomain.php | 2 +- .../Domain}/DomainInterface.php | 2 +- .../Exception}/ConversionException.php | 2 +- .../Expression/ExpressionBuilder.php | 7 +- .../ExpressionBuilderAwareTrait.php | 2 +- .../Operation}/CollectionOperation.php | 4 +- .../Operation/OperationInterface.php | 8 + 29 files changed, 64 insertions(+), 2142 deletions(-) delete mode 100644 src/DBAL/Query/AbstractQuery.php delete mode 100644 src/DBAL/Query/NativeQuery.php delete mode 100644 src/DBAL/Query/NoResultException.php delete mode 100644 src/DBAL/Query/NoUniqueResultException.php delete mode 100644 src/DBAL/Query/OrmQuery.php delete mode 100644 src/DBAL/Query/QueryBuilder.php delete mode 100644 src/DBAL/Query/QueryException.php delete mode 100644 src/DBAL/Query/QueryInterface.php delete mode 100644 src/DBAL/RecordManager.php delete mode 100644 src/DBAL/Repository/RecordNotFoundException.php delete mode 100644 src/DBAL/Repository/RecordRepository.php delete mode 100644 src/DBAL/Schema/Choice.php delete mode 100644 src/DBAL/Schema/Field.php delete mode 100644 src/DBAL/Schema/Model.php delete mode 100644 src/DBAL/Schema/Schema.php delete mode 100644 src/DBAL/Schema/SchemaException.php delete mode 100644 src/DBAL/Schema/Selection.php rename src/{DBAL/Expression => Expression/Domain}/Comparison.php (98%) rename src/{DBAL/Expression => Expression/Domain}/CompositeDomain.php (98%) rename src/{DBAL/Expression => Expression/Domain}/CustomDomain.php (92%) rename src/{DBAL/Expression => Expression/Domain}/DomainInterface.php (72%) rename src/{DBAL/Expression => Expression/Exception}/ConversionException.php (55%) rename src/{DBAL => }/Expression/ExpressionBuilder.php (96%) rename src/{DBAL => }/Expression/ExpressionBuilderAwareTrait.php (90%) rename src/{DBAL/Expression => Expression/Operation}/CollectionOperation.php (95%) create mode 100644 src/Expression/Operation/OperationInterface.php diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 56d890d..947f8b8 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"7.4.24","version":"3.5.0:v3.5.0#333f15e07c866e33e2765e84ba1e0b88e6a3af3b","indent":" ","lineEnding":"\n","rules":{"array_syntax":true,"backtick_to_shell_exec":true,"binary_operator_spaces":true,"blank_line_before_statement":{"statements":["return"]},"braces":{"allow_single_line_anonymous_class_with_empty_body":true,"allow_single_line_closure":true},"cast_spaces":true,"class_attributes_separation":{"elements":{"method":"one"}},"class_definition":{"single_line":true},"clean_namespace":true,"concat_space":true,"echo_tag_syntax":true,"empty_loop_body":{"style":"braces"},"empty_loop_condition":true,"fully_qualified_strict_types":true,"function_typehint_space":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"method_argument_space":{"on_multiline":"ignore"},"native_function_casing":true,"native_function_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_blank_lines":{"tokens":["case","continue","curly_brace_block","default","extra","parenthesis_brace_block","square_brace_block","switch","throw","use"]},"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_mixed":true,"allow_unused_params":true},"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","return","switch_case","yield","yield_from"]},"no_unneeded_curly_braces":{"namespaces":true},"no_unset_cast":true,"no_unused_imports":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"object_operator_without_whitespace":true,"ordered_imports":true,"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":true,"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_without_name":true,"protected_to_private":true,"semicolon_after_instruction":true,"single_class_element_per_statement":true,"single_line_comment_style":{"comment_types":["hash"]},"single_line_throw":true,"single_quote":true,"single_space_after_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"switch_continue_to_break":true,"trailing_comma_in_multiline":true,"trim_array_spaces":true,"types_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":true,"blank_line_after_opening_tag":true,"compact_nullable_typehint":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"return_type_declaration":true,"short_scalar_cast":true,"single_blank_line_before_namespace":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true},"hashes":{"src\\Client.php":1876081534,"src\\DBAL\\Expression\\CollectionOperation.php":2939472196,"src\\DBAL\\Expression\\Comparison.php":4237095711,"src\\DBAL\\Expression\\CompositeDomain.php":3256396640,"src\\DBAL\\Expression\\ConversionException.php":107016403,"src\\DBAL\\Expression\\CustomDomain.php":3489925073,"src\\DBAL\\Expression\\DomainInterface.php":3877458668,"src\\DBAL\\Expression\\ExpressionBuilder.php":1215622714,"src\\DBAL\\Expression\\ExpressionBuilderAwareTrait.php":3796741222,"src\\DBAL\\Query\\AbstractQuery.php":2930363797,"src\\DBAL\\Query\\NativeQuery.php":2335259464,"src\\DBAL\\Query\\NoResultException.php":1860820185,"src\\DBAL\\Query\\NoUniqueResultException.php":1183675382,"src\\DBAL\\Query\\OrmQuery.php":632474652,"src\\DBAL\\Query\\QueryBuilder.php":1139452024,"src\\DBAL\\Query\\QueryException.php":754063031,"src\\DBAL\\Query\\QueryInterface.php":3966439455,"src\\DBAL\\RecordManager.php":3573959836,"src\\DBAL\\Repository\\RecordNotFoundException.php":3899077087,"src\\DBAL\\Repository\\RecordRepository.php":377204786,"src\\DBAL\\Schema\\Choice.php":2356126375,"src\\DBAL\\Schema\\Field.php":2188356149,"src\\DBAL\\Schema\\Model.php":145712388,"src\\DBAL\\Schema\\Schema.php":3354099713,"src\\DBAL\\Schema\\SchemaException.php":195684899,"src\\DBAL\\Schema\\Selection.php":1041914487,"src\\Endpoint.php":1412879540,"src\\Exception\\AuthenticationException.php":2083575707,"src\\Exception\\ExceptionInterface.php":141028630,"src\\Exception\\MissingConfigParameterException.php":3577118993,"src\\Exception\\RemoteException.php":3918346446,"src\\Exception\\RequestException.php":1948041852}} \ No newline at end of file +{"php":"7.4.24","version":"3.5.0:v3.5.0#333f15e07c866e33e2765e84ba1e0b88e6a3af3b","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":true,"class_definition":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":{"elements":["method","property"]},"encoding":true,"full_opening_tag":true},"hashes":{"C:\\Users\\Ang3\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder1375\\src\\Client.php":4035633777}} \ No newline at end of file diff --git a/composer.lock b/composer.lock index 8eb744f..9f33d9a 100644 --- a/composer.lock +++ b/composer.lock @@ -1311,12 +1311,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "26f418efa3cfbf0b404ee2b504bff7ecefe92df7" + "reference": "25a216c5c0426f341fbff7f045fa3946c8041521" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/26f418efa3cfbf0b404ee2b504bff7ecefe92df7", - "reference": "26f418efa3cfbf0b404ee2b504bff7ecefe92df7", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/25a216c5c0426f341fbff7f045fa3946c8041521", + "reference": "25a216c5c0426f341fbff7f045fa3946c8041521", "shasum": "" }, "conflict": { @@ -1345,6 +1345,7 @@ "brightlocal/phpwhois": "<=4.2.5", "buddypress/buddypress": "<7.2.1", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", + "bytefury/crater": "<6", "cachethq/cachet": "<2.5.1", "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.5.18|>=3.6,<3.6.15|>=3.7,<3.7.7", "cardgate/magento2": "<2.0.33", @@ -1379,7 +1380,7 @@ "doctrine/mongodb-odm": ">=1,<1.0.2", "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "<=14.0.4|>= 3.3.beta1, < 13.0.2", + "dolibarr/dolibarr": "<=14.0.5|>= 3.3.beta1, < 13.0.2", "dompdf/dompdf": ">=0.6,<0.6.2", "drupal/core": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", "drupal/drupal": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", @@ -1401,7 +1402,7 @@ "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", "ezsystems/ezplatform-richtext": ">=2.3,<=2.3.7", "ezsystems/ezplatform-user": ">=1,<1.0.1", - "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<=7.5.15.1", + "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.26", "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1", "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", "ezsystems/repository-forms": ">=2.3,<2.3.2.1", @@ -1440,7 +1441,7 @@ "hjue/justwriting": "<=1", "hov/jobfair": "<1.0.13|>=2,<2.0.2", "ibexa/post-install": "<=1.0.4", - "icecoder/icecoder": "<=8", + "icecoder/icecoder": "<=8.1", "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.99999|>=4.2,<=4.2.99999|>=5,<=5.0.99999|>=5.1,<=5.1.99999|>=5.2,<=5.2.99999|>=5.3,<=5.3.99999|>=5.4,<=5.4.99999|>=5.5,<=5.5.49|>=5.6,<=5.6.99999|>=5.7,<=5.7.99999|>=5.8,<=5.8.99999|>=6,<6.18.31|>=7,<7.22.4", "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", @@ -1472,6 +1473,7 @@ "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", "librenms/librenms": "<=21.11", "limesurvey/limesurvey": "<3.27.19", + "livehelperchat/livehelperchat": "<=3.91", "livewire/livewire": ">2.2.4,<2.2.6", "lms/routes": "<2.1.1", "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", @@ -1585,9 +1587,10 @@ "simplito/elliptic-php": "<1.0.6", "slim/slim": "<2.6", "smarty/smarty": "<3.1.43|>=4,<4.0.3", - "snipe/snipe-it": "<5.3.5", + "snipe/snipe-it": "<5.3.7", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", + "spipu/html2pdf": "<5.2.4", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", "ssddanbrown/bookstack": "<21.12.1", @@ -1744,7 +1747,7 @@ "type": "tidelift" } ], - "time": "2022-01-21T23:14:14+00:00" + "time": "2022-01-22T00:48:41+00:00" }, { "name": "symfony/console", diff --git a/src/Client.php b/src/Client.php index 0d299e7..4a5ff21 100644 --- a/src/Client.php +++ b/src/Client.php @@ -2,13 +2,11 @@ namespace Ang3\Component\Odoo; -use Ang3\Component\Odoo\DBAL\Expression\ExpressionBuilderAwareTrait; -use Ang3\Component\Odoo\DBAL\Query\OrmQuery; -use Ang3\Component\Odoo\DBAL\RecordManager; use Ang3\Component\Odoo\Exception\AuthenticationException; use Ang3\Component\Odoo\Exception\MissingConfigParameterException; use Ang3\Component\Odoo\Exception\RemoteException; use Ang3\Component\Odoo\Exception\RequestException; +use Ang3\Component\Odoo\Expression\ExpressionBuilder; use InvalidArgumentException; use Psr\Log\LoggerInterface; use Symfony\Component\HttpClient\HttpClient; @@ -16,14 +14,23 @@ class Client { - use ExpressionBuilderAwareTrait; - /** * Services. */ public const SERVICE_COMMON = 'common'; public const SERVICE_OBJECT = 'object'; + /** + * Query ORM methods. + */ + public const CREATE = 'create'; + public const WRITE = 'write'; + public const READ = 'read'; + public const UNLINK = 'unlink'; + public const SEARCH_READ = 'search_read'; + public const SEARCH = 'search'; + public const SEARCH_COUNT = 'search_count'; + /** * Special commands. */ @@ -63,11 +70,9 @@ class Client private $httpClient; /** - * ORM record manager. - * - * @var RecordManager + * @var ExpressionBuilder */ - private $recordManager; + private $expressionBuilder; /** * Optional logger. @@ -90,7 +95,7 @@ public function __construct(string $url, string $database, string $username, str $this->httpClient = HttpClient::create([ 'base_uri' => "$url/jsonrpc", ]); - $this->recordManager = new RecordManager($this); + $this->expressionBuilder = new ExpressionBuilder(); $this->logger = $logger; } @@ -136,7 +141,7 @@ public function create(string $modelName, array $data): int throw new InvalidArgumentException('Data cannot be empty'); } - $result = $this->execute($modelName, OrmQuery::CREATE, [[$data]]); + $result = $this->execute($modelName, self::CREATE, [[$data]]); return (int) array_shift($result); } @@ -152,7 +157,7 @@ public function read(string $modelName, $ids, array $options = []): array { $ids = [is_int($ids) ? [$ids] : (array) $ids]; - return (array) $this->execute($modelName, OrmQuery::READ, [$ids], $options); + return (array) $this->execute($modelName, self::READ, [[$ids]], $options); } /** @@ -169,7 +174,7 @@ public function update(string $modelName, $ids, array $data = []): void } $ids = is_array($ids) ? $ids : [(int) $ids]; - $this->execute($modelName, OrmQuery::WRITE, [$ids, $data]); + $this->execute($modelName, self::WRITE, [$ids, $data]); } /** @@ -182,7 +187,7 @@ public function update(string $modelName, $ids, array $data = []): void public function delete(string $modelName, $ids): void { $ids = is_array($ids) ? $ids : [(int) $ids]; - $this->execute($modelName, OrmQuery::UNLINK, [$ids]); + $this->execute($modelName, self::UNLINK, [$ids]); } /** @@ -228,7 +233,7 @@ public function search(string $modelName, iterable $criteria = null, array $opti unset($options['fields']); } - return (array) $this->execute($modelName, OrmQuery::SEARCH, [$this->expr()->normalizeDomains($criteria)], $options); + return (array) $this->execute($modelName, self::SEARCH, [$this->expressionBuilder->normalizeDomains($criteria)], $options); } /** @@ -278,7 +283,7 @@ public function findAll(string $modelName, array $options = []): array */ public function findBy(string $modelName, iterable $criteria = null, array $options = []): array { - return (array) $this->execute($modelName, OrmQuery::SEARCH_READ, [$this->expr()->normalizeDomains($criteria)], $options); + return (array) $this->execute($modelName, self::SEARCH_READ, [$this->expressionBuilder->normalizeDomains($criteria)], $options); } /** @@ -312,7 +317,7 @@ public function countAll(string $modelName): int */ public function count(string $modelName, iterable $criteria = null): int { - return $this->execute($modelName, OrmQuery::SEARCH_COUNT, [$this->expr()->normalizeDomains($criteria)]); + return $this->execute($modelName, self::SEARCH_COUNT, [$this->expressionBuilder->normalizeDomains($criteria)]); } /** @@ -330,7 +335,9 @@ public function listFields(string $modelName, array $options = []): array */ public function execute(string $name, string $method, array $parameters = [], array $options = []) { - return $this->request(self::SERVICE_OBJECT, 'execute_kw', + return $this->request( + self::SERVICE_OBJECT, + 'execute_kw', $this->database, $this->authenticate(), $this->password, @@ -352,7 +359,9 @@ public function version(): array public function authenticate(): int { if (null === $this->uid) { - $this->uid = $this->request(self::SERVICE_COMMON, 'login', + $this->uid = $this->request( + self::SERVICE_COMMON, + 'login', $this->database, $this->username, $this->password @@ -469,9 +478,9 @@ public function setPassword(string $password): self return $this; } - public function getRecordManager(): RecordManager + public function getExpressionBuilder(): ExpressionBuilder { - return $this->recordManager; + return $this->expressionBuilder; } public function getLogger(): ?LoggerInterface diff --git a/src/DBAL/Query/AbstractQuery.php b/src/DBAL/Query/AbstractQuery.php deleted file mode 100644 index dc5ee01..0000000 --- a/src/DBAL/Query/AbstractQuery.php +++ /dev/null @@ -1,133 +0,0 @@ -recordManager = $recordManager; - $this->name = $name; - $this->method = $method; - } - - public function getName(): string - { - return $this->name; - } - - /** - * @return $this - */ - public function setName(string $name) - { - $this->name = $name; - - return $this; - } - - public function getMethod(): string - { - return $this->method; - } - - /** - * @return $this - */ - public function setMethod(string $method) - { - $this->method = $method; - - return $this; - } - - public function getParameters(): array - { - return $this->parameters; - } - - /** - * @return $this - */ - public function setParameters(array $parameters = []) - { - $this->parameters = $parameters; - - return $this; - } - - public function getOptions(): array - { - return $this->options; - } - - /** - * @return $this - */ - public function setOptions(array $options = []) - { - $this->options = $options; - - return $this; - } - - /** - * Add an option on the query. - * - * @param mixed $value - * - * @return $this - */ - public function addOption(string $name, $value) - { - $this->options[$name] = $value; - - return $this; - } - - /** - * Execute the query. - * Allowed methods: all. - * - * @return mixed - */ - public function execute() - { - return $this->recordManager->executeQuery($this); - } - - /** - * Gets the related manager of the query. - */ - public function getRecordManager(): RecordManager - { - return $this->recordManager; - } -} diff --git a/src/DBAL/Query/NativeQuery.php b/src/DBAL/Query/NativeQuery.php deleted file mode 100644 index 3f34622..0000000 --- a/src/DBAL/Query/NativeQuery.php +++ /dev/null @@ -1,7 +0,0 @@ -method = $method; - - return $this; - } - - /** - * Counts the number of records from parameters. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws QueryException on invalid query method - */ - public function count(): int - { - if (!in_array($this->method, [self::SEARCH, self::SEARCH_READ])) { - throw new QueryException(sprintf('You can count results with method "%s" and "%s" only.', self::SEARCH, self::SEARCH_READ)); - } - - $query = new self($this->recordManager, $this->name, self::SEARCH_COUNT); - $query->setParameters($this->recordManager->getExpressionBuilder()->normalizeDomains($this->parameters)); - - return (int) $query->execute(); - } - - /** - * Gets just ONE scalar result. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @return bool|int|float|string - * - * @throws NoUniqueResultException on no unique result - * @throws NoResultException on no result - * @throws QueryException on invalid query method - */ - public function getSingleScalarResult() - { - $result = $this->getOneOrNullScalarResult(); - - if (!$result) { - throw new NoResultException(); - } - - return $result; - } - - /** - * Gets one or NULL scalar result. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @return bool|int|float|string|null - * - * @throws NoUniqueResultException on no unique result - * @throws QueryException on invalid query method - */ - public function getOneOrNullScalarResult() - { - $result = $this->getScalarResult(); - - if (count($result) > 1) { - throw new NoUniqueResultException(); - } - - return array_shift($result); - } - - /** - * Gets a list of scalar result. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws QueryException on invalid query method - * - * @return array - */ - public function getScalarResult(): array - { - $result = $this->getResult(); - - if (self::SEARCH === $this->method) { - return $result; - } - - $selectedFields = $this->options['fields'] ?? []; - if (count($selectedFields) > 1) { - throw new QueryException('More than one field selected.'); - } - - $selectedFieldName = $selectedFields[0] ?? 'id'; - - foreach ($result as $key => $value) { - $result[$key] = $value[$selectedFieldName] ?? null; - } - - return $result; - } - - /** - * Gets one row. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws NoUniqueResultException on no unique result - * @throws NoResultException on no result - * @throws QueryException on invalid query method - */ - public function getSingleResult(): array - { - $result = $this->getOneOrNullResult(); - $result = array_shift($result); - - if (!$result) { - throw new NoResultException(); - } - - return $result; - } - - /** - * Gets one or NULL row. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws NoUniqueResultException on no unique result - * @throws QueryException on invalid query method - */ - public function getOneOrNullResult(): ?array - { - $result = $this->getResult(); - - if (count($result) > 1) { - throw new NoUniqueResultException(); - } - - return array_shift($result); - } - - /** - * Gets all result rows. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws QueryException on invalid query method - */ - public function getResult(): array - { - if (!in_array($this->method, [self::SEARCH, self::SEARCH_READ])) { - throw new QueryException(sprintf('You can get results with methods "%s" and "%s" only.', self::SEARCH, self::SEARCH_READ)); - } - - return (array) $this->execute(); - } -} diff --git a/src/DBAL/Query/QueryBuilder.php b/src/DBAL/Query/QueryBuilder.php deleted file mode 100644 index c40b95f..0000000 --- a/src/DBAL/Query/QueryBuilder.php +++ /dev/null @@ -1,546 +0,0 @@ -recordManager = $recordManager; - $this->from($modelName); - } - - /** - * Defines the query of type "SELECT" with selected fields. - * No fields selected = all fields returned. - * - * @param array|string|null $fields - */ - public function select($fields = null): self - { - $this->type = self::SELECT; - $this->select = []; - $this->values = []; - $this->ids = []; - - $fields = $fields ? (array) $fields : []; - - foreach ($fields as $fieldName) { - $this->addSelect($fieldName); - } - - return $this; - } - - /** - * Defines the query of type "SEARCH". - */ - public function search(): self - { - $this->type = self::SEARCH; - $this->select = []; - $this->values = []; - $this->ids = []; - - return $this; - } - - /** - * Defines the query of type "INSERT". - */ - public function insert(): self - { - $this->type = self::INSERT; - $this->select = []; - $this->ids = []; - $this->where = null; - - return $this; - } - - /** - * Defines the query of type "UPDATE" with ids of records to update. - * - * @param int[]|int $ids - */ - public function update($ids): self - { - $this->type = self::UPDATE; - $this->select = []; - - return $this->setIds(is_array($ids) ? $ids : [(int) $ids]); - } - - /** - * Defines the query of type "DELETE" with ids of records to delete. - * - * @param int[]|int $ids - */ - public function delete($ids): self - { - $this->type = self::DELETE; - $this->select = []; - $this->values = []; - - return $this->setIds(is_array($ids) ? $ids : [(int) $ids]); - } - - /** - * Adds a field to select. - * - * @throws QueryException when the type of the query is not "SELECT" - */ - public function addSelect(string $fieldName): self - { - if (self::SELECT !== $this->type) { - throw new QueryException('You can select fields in query of type "SELECT" only.'); - } - - if (!in_array($fieldName, $this->select)) { - $this->select[] = $fieldName; - } - - return $this; - } - - /** - * Gets selected fields. - */ - public function getSelect(): array - { - return $this->select; - } - - /** - * Sets the target model name. - */ - public function from(string $modelName): self - { - $this->from = $modelName; - - return $this; - } - - /** - * Gets the target model name of the query. - */ - public function getFrom(): ?string - { - return $this->from; - } - - /** - * Sets target IDs in case of query of type "UPDATE" or "DELETE". - * - * @throws QueryException when the type of the query is not "UPDATE" nor "DELETE" - */ - public function setIds(array $ids): self - { - $this->ids = []; - - foreach ($ids as $id) { - $this->addId($id); - } - - return $this; - } - - /** - * Adds target ID in case of query of type "UPDATE" or "DELETE". - * - * @throws QueryException when the type of the query is not "UPDATE" nor "DELETE" - */ - public function addId(int $id): self - { - if (!in_array($this->type, [self::UPDATE, self::DELETE])) { - throw new QueryException('You can set indexes in query of type "UPDATE" or "DELETE" only.'); - } - - if (!in_array($id, $this->ids, true)) { - $this->ids[] = $id; - } - - return $this; - } - - /** - * @return int[] - */ - public function getIds(): array - { - return $this->ids; - } - - /** - * Sets field values in case of query of type "INSERT" or "UPDATE". - * - * @throws QueryException when the type of the query is not "INSERT" nor "UPDATE" - */ - public function setValues(array $values = []): self - { - $this->values = []; - - foreach ($values as $fieldName => $value) { - $this->set($fieldName, $value); - } - - return $this; - } - - /** - * Set a field value in case of query of type "INSERT" or "UPDATE". - * - * @param mixed $value - * - * @throws QueryException when the type of the query is not "INSERT" nor "UPDATE" - */ - public function set(string $fieldName, $value): self - { - if (!in_array($this->type, [self::INSERT, self::UPDATE])) { - throw new QueryException('You can set values in query of type "INSERT" or "UPDATE" only.'); - } - - $this->values[$fieldName] = $value; - - return $this; - } - - /** - * Gets field values set in case of query of type "INSERT" or "UPDATE". - */ - public function getValues(): array - { - return $this->values; - } - - /** - * Sets criteria for queries of type "SELECT" and "SEARCH". - * - * @throws QueryException when the type of the query is not "SELECT" not "SEARCH" - */ - public function where(?DomainInterface $domain = null): self - { - $this->assertSupportsWhereClause(); - $this->where = $domain; - - return $this; - } - - /** - * Takes the WHERE clause and adds a node with logical operator AND. - * - * @throws QueryException when the type of the query is not "SELECT" nor "SEARCH" - */ - public function andWhere(DomainInterface $domain): self - { - $this->assertSupportsWhereClause(); - $this->where = $this->expr()->andX($this->where, $domain); - - return $this; - } - - /** - * Takes the WHERE clause and adds a node with logical operator OR. - * - * @throws QueryException when the type of the query is not "SELECT" nor "SEARCH" - */ - public function orWhere(DomainInterface $domain): self - { - $this->assertSupportsWhereClause(); - $this->where = $this->expr()->orX($this->where, $domain); - - return $this; - } - - /** - * Gets the WHERE clause. - */ - public function getWhere(): ?DomainInterface - { - return $this->where; - } - - /** - * @internal - * - * @throws QueryException when the type of the query is not "SELECT" nor "SEARCH" - */ - private function assertSupportsWhereClause(): void - { - if (!in_array($this->type, [self::SELECT, self::SEARCH])) { - throw new QueryException('You can set criteria in query of type "SELECT" or "SEARCH" only.'); - } - } - - /** - * Sets orders. - */ - public function setOrders(array $orders = []): self - { - $this->orders = []; - - foreach ($orders as $fieldName => $isAsc) { - $this->addOrderBy($fieldName, $isAsc); - } - - return $this; - } - - /** - * Clears orders and adds one. - */ - public function orderBy(string $fieldName, bool $isAsc = true): self - { - $this->orders = []; - - return $this->addOrderBy($fieldName, $isAsc); - } - - /** - * Adds order. - * - * @throws QueryException when the query type is not valid - */ - public function addOrderBy(string $fieldName, bool $isAsc = true): self - { - if (!in_array($this->type, [self::SELECT, self::SEARCH])) { - throw new QueryException('You can set orders in query of type "SELECT", "SEARCH" only.'); - } - - $this->orders[$fieldName] = $isAsc; - - return $this; - } - - /** - * Gets ordered fields. - */ - public function getOrders(): array - { - return $this->orders; - } - - /** - * Sets the max results of the query (limit). - */ - public function setMaxResults(?int $maxResults): self - { - $this->maxResults = $maxResults; - - return $this; - } - - /** - * Gets the max results of the query. - */ - public function getMaxResults(): ?int - { - return $this->maxResults; - } - - /** - * Sets the first results of the query (offset). - */ - public function setFirstResult(?int $firstResult): self - { - $this->firstResult = $firstResult; - - return $this; - } - - /** - * Gets the first results of the query. - */ - public function getFirstResult(): ?int - { - return $this->firstResult; - } - - /** - * Computes and returns the query. - * - * @throws QueryException on invalid query - * @throws ConversionException on data conversion failure - */ - public function getQuery(): OrmQuery - { - $from = $this->from; - if (null === $from) { - throw new QueryException('Missing FROM clause (model name).'); - } - - switch ($this->type) { - case self::SELECT: - $method = OrmQuery::SEARCH_READ; - break; - case self::SEARCH: - $method = OrmQuery::SEARCH; - break; - case self::INSERT: - $method = OrmQuery::CREATE; - break; - case self::UPDATE: - $method = OrmQuery::WRITE; - break; - case self::DELETE: - $method = OrmQuery::UNLINK; - break; - default: - throw new InvalidArgumentException(sprintf('The query type "%s" is not valid.', $this->type)); - } - - $query = new OrmQuery($this->recordManager, $from, $method); - - switch ($this->type) { - case self::SEARCH: - case self::SELECT: - $parameters = $this->expr()->normalizeDomains($this->where); - break; - case self::DELETE: - if (!$this->ids) { - throw new QueryException('You must set indexes for queries of type "DELETE".'); - } - - $parameters = [$this->ids]; - break; - default: - if (!$this->values) { - throw new QueryException('You must set values for queries of type "INSERT" or "UPDATE".'); - } - - $parameters = $this->expr()->normalizeData($this->values); - - if (self::UPDATE === $this->type) { - if (!$this->ids) { - throw new QueryException('You must set indexes for queries of type "UPDATE".'); - } - - $parameters = [$this->ids, $parameters]; - } - break; - } - - $query->setParameters($parameters); - - if (in_array($this->type, [self::SELECT, self::SEARCH])) { - $options = []; - - if (self::SELECT === $this->type && $this->select) { - $options['fields'] = $this->select; - } - - $orders = $this->orders; - - if ($orders) { - foreach ($orders as $fieldName => $isAsc) { - $orders[$fieldName] = sprintf('%s %s', $fieldName, $isAsc ? 'asc' : 'desc'); - } - - $options['order'] = implode(', ', $orders); - } - - if ($this->firstResult) { - $options['offset'] = $this->firstResult; - } - - if ($this->maxResults) { - $options['limit'] = $this->maxResults; - } - - $query->setOptions($options); - } - - return $query; - } - - /** - * Gets the type of the query. - */ - public function getType(): string - { - return $this->type; - } - - /** - * Gets the related manager of the query. - */ - public function getRecordManager(): RecordManager - { - return $this->recordManager; - } - - /** - * Shortcut to the expression builder of the related client. - */ - public function expr(): ExpressionBuilder - { - return $this->recordManager->getExpressionBuilder(); - } -} diff --git a/src/DBAL/Query/QueryException.php b/src/DBAL/Query/QueryException.php deleted file mode 100644 index 9e24bba..0000000 --- a/src/DBAL/Query/QueryException.php +++ /dev/null @@ -1,7 +0,0 @@ -client = $client; - $this->schema = new Schema($client); - } - - /** - * Create a new record. - * - * @return int the ID of the new record - */ - public function create(string $modelName, array $data): int - { - return $this - ->getRepository($modelName) - ->insert($data); - } - - /** - * Update record(s). - * - * NB: It is not currently possible to perform “computed” updates (by criteria). - * To do it, you have to perform a search then an update with search result IDs. - * - * @param array|int $ids - */ - public function update(string $modelName, $ids, array $data = []): void - { - $this - ->getRepository($modelName) - ->update($ids, $data); - } - - /** - * Delete record(s). - * - * NB: It is not currently possible to perform “computed” deletes (by criteria). - * To do it, you have to perform a search then a delete with search result IDs. - * - * @param array|int $ids - */ - public function delete(string $modelName, $ids): void - { - $this - ->getRepository($modelName) - ->delete($ids); - } - - /** - * Search one ID of record by criteria. - */ - public function searchOne(string $modelName, ?DomainInterface $criteria): ?int - { - return $this - ->getRepository($modelName) - ->searchOne($criteria); - } - - /** - * Search all ID of record(s). - * - * @return int[] - */ - public function searchAll(string $modelName, array $orders = [], int $limit = null, int $offset = null): array - { - return $this - ->getRepository($modelName) - ->searchAll($orders, $limit, $offset); - } - - /** - * Search ID of record(s) by criteria. - * - * @return int[] - */ - public function search(string $modelName, ?DomainInterface $criteria = null, array $orders = [], int $limit = null, int $offset = null): array - { - return $this - ->getRepository($modelName) - ->search($criteria, $orders, $limit, $offset); - } - - /** - * Find ONE record by ID. - * - * @throws RecordNotFoundException when the record was not found - */ - public function read(string $modelName, int $id, array $fields = []): array - { - return $this - ->getRepository($modelName) - ->read($id, $fields); - } - - /** - * Find ONE record by ID. - */ - public function find(string $modelName, int $id, array $fields = []): ?array - { - return $this - ->getRepository($modelName) - ->find($id, $fields); - } - - /** - * Find ONE record by criteria. - */ - public function findOneBy(string $modelName, ?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $offset = null): ?array - { - return $this - ->getRepository($modelName) - ->findOneBy($criteria, $fields, $orders, $offset); - } - - /** - * Find all records. - * - * @return array[] - */ - public function findAll(string $modelName, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array - { - return $this - ->getRepository($modelName) - ->findAll($fields, $orders, $limit, $offset); - } - - /** - * Find record(s) by criteria. - * - * @return array[] - */ - public function findBy(string $modelName, ?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array - { - return $this - ->getRepository($modelName) - ->findBy($criteria, $fields, $orders, $limit, $offset); - } - - /** - * Check if a record exists. - */ - public function exists(string $modelName, int $id): bool - { - return $this - ->getRepository($modelName) - ->exists($id); - } - - /** - * Count number of all records for the model. - */ - public function countAll(string $modelName): int - { - return $this - ->getRepository($modelName) - ->countAll(); - } - - /** - * Count number of records for a model and criteria. - */ - public function count(string $modelName, ?DomainInterface $criteria = null): int - { - return $this - ->getRepository($modelName) - ->count($criteria); - } - - public function getRepository(string $modelName): RecordRepository - { - if (!array_key_exists($modelName, $this->repositories)) { - $repository = new RecordRepository($this, $modelName); - $this->addRepository($repository); - - return $repository; - } - - return $this->repositories[$modelName]; - } - - public function setRepositories(array $repositories = []): self - { - $this->repositories = []; - - foreach ($repositories as $repository) { - $this->addRepository($repository); - } - - return $this; - } - - public function addRepository(RecordRepository $repository): self - { - $this->repositories[$repository->getModelName()] = $repository; - $repository->setRecordManager($this); - - return $this; - } - - public function createQueryBuilder(string $modelName = null): QueryBuilder - { - return new QueryBuilder($this, $modelName); - } - - public function createOrmQuery(string $name, string $method): OrmQuery - { - return new OrmQuery($this, $name, $method); - } - - public function createNativeQuery(string $name, string $method): NativeQuery - { - return new NativeQuery($this, $name, $method); - } - - /** - * @return mixed - */ - public function executeQuery(QueryInterface $query) - { - $options = $query->getOptions(); - - if (!$options) { - return $this->client->execute($query->getName(), $query->getMethod(), $query->getParameters()); - } - - return $this->client->execute($query->getName(), $query->getMethod(), $query->getParameters(), $options); - } - - public function getClient(): Client - { - return $this->client; - } - - public function getSchema(): Schema - { - return $this->schema; - } - - public function getRepositories(): array - { - return $this->repositories; - } -} diff --git a/src/DBAL/Repository/RecordNotFoundException.php b/src/DBAL/Repository/RecordNotFoundException.php deleted file mode 100644 index 4f3e9e8..0000000 --- a/src/DBAL/Repository/RecordNotFoundException.php +++ /dev/null @@ -1,34 +0,0 @@ -modelName = $modelName; - $this->id = $id; - - parent::__construct(sprintf('No record found for model "%s" with ID #%d.', $modelName, $id)); - } - - public function getModelName(): string - { - return $this->modelName; - } - - public function getId(): int - { - return $this->id; - } -} diff --git a/src/DBAL/Repository/RecordRepository.php b/src/DBAL/Repository/RecordRepository.php deleted file mode 100644 index 4d79918..0000000 --- a/src/DBAL/Repository/RecordRepository.php +++ /dev/null @@ -1,254 +0,0 @@ -recordManager = $recordManager; - $this->modelName = $modelName; - $recordManager->addRepository($this); - } - - /** - * Insert a new record. - * - * @throws InvalidArgumentException when $data is empty - * - * @return int the ID of the new record - */ - public function insert(array $data): int - { - if (!$data) { - throw new InvalidArgumentException('Data cannot be empty'); - } - - return $this - ->createQueryBuilder() - ->insert() - ->setValues($data) - ->getQuery() - ->execute(); - } - - /** - * Update record(s). - * - * NB: It is not currently possible to perform “computed” updates - * (where the value being set depends on an existing value of a record). - * - * @param array|int $ids - */ - public function update($ids, array $data = []): void - { - if (!$data) { - return; - } - - $this - ->createQueryBuilder() - ->update((array) $ids) - ->setValues($data) - ->getQuery() - ->execute(); - } - - /** - * Delete record(s). - * - * @param array|int $ids - */ - public function delete($ids): void - { - if (!$ids) { - return; - } - - $this - ->createQueryBuilder() - ->delete((array) $ids) - ->getQuery() - ->execute(); - } - - /** - * Search one ID of record by criteria. - */ - public function searchOne(?DomainInterface $criteria): ?int - { - return (int) $this - ->createQueryBuilder() - ->search() - ->where($criteria) - ->getQuery() - ->getOneOrNullScalarResult(); - } - - /** - * Search all ID of record(s). - * - * @return int[] - */ - public function searchAll(array $orders = [], int $limit = null, int $offset = null): array - { - return $this->search(null, $orders, $limit, $offset); - } - - /** - * Search ID of record(s) by criteria. - * - * @return int[] - */ - public function search(?DomainInterface $criteria = null, array $orders = [], int $limit = null, int $offset = null): array - { - /** @var int[] $result */ - $result = $this - ->createQueryBuilder() - ->search() - ->where($criteria) - ->setOrders($orders) - ->setFirstResult($offset) - ->setMaxResults($limit) - ->getQuery() - ->getScalarResult(); - - return $result; - } - - /** - * Find ONE record by ID. - * - * @throws RecordNotFoundException when the record was not found - */ - public function read(int $id, array $fields = []): array - { - $record = $this->find($id, $fields); - - if (!$record) { - throw new RecordNotFoundException($this->modelName, $id); - } - - return $record; - } - - /** - * Find ONE record by ID. - */ - public function find(int $id, array $fields = []): ?array - { - return $this->findOneBy($this->expr()->eq('id', $id), $fields); - } - - /** - * Find ONE record by criteria. - */ - public function findOneBy(?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $offset = null): ?array - { - $result = $this->findBy($criteria, $fields, $orders, 1, $offset); - - return array_pop($result); - } - - /** - * Find all records. - * - * @return array[] - */ - public function findAll(array $fields = [], array $orders = [], int $limit = null, int $offset = null): array - { - return $this->findBy(null, $fields, $orders, $limit, $offset); - } - - /** - * Find record(s) by criteria. - * - * @return array[] - */ - public function findBy(?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array - { - return $this - ->createQueryBuilder() - ->select($fields) - ->where($criteria) - ->setOrders($orders) - ->setFirstResult($offset) - ->setMaxResults($limit) - ->getQuery() - ->getResult(); - } - - /** - * Check if a record exists. - */ - public function exists(int $id): bool - { - return 1 === $this->count($this->expr()->eq('id', $id)); - } - - /** - * Count number of all records for the model. - */ - public function countAll(): int - { - return $this->count(); - } - - /** - * Count number of records for a model and criteria. - */ - public function count(?DomainInterface $criteria = null): int - { - return $this - ->createQueryBuilder() - ->select() - ->where($criteria) - ->getQuery() - ->count(); - } - - public function createQueryBuilder(): QueryBuilder - { - return $this->recordManager - ->createQueryBuilder($this->modelName) - ->select(); - } - - public function setRecordManager(RecordManager $recordManager): self - { - $this->recordManager = $recordManager; - - return $this; - } - - public function getRecordManager(): RecordManager - { - return $this->recordManager; - } - - public function getModelName(): string - { - return $this->modelName; - } - - public function expr(): ExpressionBuilder - { - return $this->recordManager->getExpressionBuilder(); - } -} diff --git a/src/DBAL/Schema/Choice.php b/src/DBAL/Schema/Choice.php deleted file mode 100644 index aa0796f..0000000 --- a/src/DBAL/Schema/Choice.php +++ /dev/null @@ -1,43 +0,0 @@ -name = $name; - $this->value = $value; - $this->id = $id; - } - - public function getId(): ?int - { - return $this->id; - } - - public function getName(): string - { - return $this->name; - } - - public function getValue(): string - { - return $this->value; - } -} diff --git a/src/DBAL/Schema/Field.php b/src/DBAL/Schema/Field.php deleted file mode 100644 index 07d040c..0000000 --- a/src/DBAL/Schema/Field.php +++ /dev/null @@ -1,238 +0,0 @@ -id = (int) $data['id']; - $this->name = (string) $data['name']; - $this->type = (string) $data['ttype']; - $this->required = (bool) $data['required']; - $this->readOnly = (bool) $data['readonly']; - $this->displayName = $data['display_name'] ?? null; - $this->size = $data['size'] ?? null; - $this->selection = $data['selection'] ?? null; - $this->targetModelName = $data['relation'] ?? null; - $this->targetFieldName = $data['relation_field'] ?? null; - } - - public function getModel(): Model - { - return $this->model; - } - - public function setModel(Model $model): self - { - $this->model = $model; - - return $this; - } - - public function getId(): ?int - { - return $this->id; - } - - public function getName(): string - { - return $this->name; - } - - public function getType(): string - { - return $this->type; - } - - public function isRequired(): bool - { - return $this->required; - } - - public function isReadOnly(): bool - { - return $this->readOnly; - } - - public function getDisplayName(): string - { - return $this->displayName ?: $this->name; - } - - public function getSize(): ?int - { - return $this->size; - } - - public function getSelection(): ?Selection - { - return $this->selection; - } - - public function getTargetModelName(): ?string - { - return $this->targetModelName; - } - - public function getTargetFieldName(): ?string - { - return $this->targetFieldName; - } - - public function isIdentifier(): bool - { - return 'id' === $this->name; - } - - public function isBinary(): bool - { - return Field::T_BINARY === $this->type; - } - - public function isBoolean(): bool - { - return Field::T_BOOLEAN === $this->type; - } - - public function isInteger(): bool - { - return Field::T_INTEGER === $this->type; - } - - public function isFloat(): bool - { - return in_array($this->type, [self::T_FLOAT, self::T_MONETARY]); - } - - public function isNumber(): bool - { - return $this->isInteger() || $this->isFloat(); - } - - public function isString(): bool - { - return in_array($this->type, [self::T_CHAR, self::T_TEXT, self::T_HTML]); - } - - public function isDate(): bool - { - return in_array($this->type, [self::T_DATE, self::T_DATETIME], true); - } - - public function getDateFormat(): string - { - return Field::T_DATETIME === $this->type ? self::DATETIME_FORMAT : self::DATE_FORMAT; - } - - public function isSelection(): bool - { - return Field::T_SELECTION === $this->type; - } - - public function isSelectable(): bool - { - return null !== $this->selection; - } - - public function isAssociation(): bool - { - return in_array($this->type, [ - self::T_MANY_TO_ONE, - self::T_MANY_TO_MANY, - self::T_ONE_TO_MANY, - ], true); - } - - public function isSingleAssociation(): bool - { - return self::T_MANY_TO_ONE === $this->type; - } - - public function isMultipleAssociation(): bool - { - return in_array($this->type, [ - self::T_MANY_TO_MANY, - self::T_ONE_TO_MANY, - ], true); - } -} diff --git a/src/DBAL/Schema/Model.php b/src/DBAL/Schema/Model.php deleted file mode 100644 index e5fb201..0000000 --- a/src/DBAL/Schema/Model.php +++ /dev/null @@ -1,141 +0,0 @@ -schema = $schema; - $this->id = (int) $data['id']; - $this->name = (string) $data['model']; - $this->displayName = (string) $data['name']; - $this->transient = (bool) $data['transient']; - - foreach ($fields as $field) { - $this->addField($field); - } - } - - public function getSchema(): Schema - { - return $this->schema; - } - - public function getId(): int - { - return $this->id; - } - - public function getName(): string - { - return $this->name; - } - - public function getDisplayName(): string - { - return $this->displayName ?: $this->name; - } - - public function isTransient(): bool - { - return $this->transient; - } - - public function hasField(string $fieldName): bool - { - try { - $this->getField($fieldName); - } catch (SchemaException $exception) { - return false; - } - - return true; - } - - /** - * @throws SchemaException when the field was not found - */ - public function getField(string $fieldName): Field - { - $model = $this; - $fields = explode('.', $fieldName); - $lastKey = count($fields) - 1; - - foreach ($fields as $key => $subFieldName) { - $field = $model->getField($subFieldName); - - if ($lastKey === $key) { - break; - } - - $targetModel = $field->getTargetModelName(); - - if (!$targetModel) { - throw SchemaException::fieldNotFound($fieldName, $this); - } - - $model = $this->schema->getModel($targetModel); - } - - return $field; - } - - public function getFields(): array - { - return $this->fields; - } - - /** - * @return array - */ - public function getFieldNames(): array - { - $fieldNames = []; - - foreach ($this->fields as $field) { - $fieldNames[] = $field->getName(); - } - - return $fieldNames; - } - - /** - * @internal - */ - private function addField(Field $field): void - { - $field->setModel($this); - $this->fields[] = $field; - } -} diff --git a/src/DBAL/Schema/Schema.php b/src/DBAL/Schema/Schema.php deleted file mode 100644 index 21fbb4e..0000000 --- a/src/DBAL/Schema/Schema.php +++ /dev/null @@ -1,114 +0,0 @@ -client = $client; - } - - public function getModel(string $modelName): Model - { - if (!$this->hasModel($modelName)) { - throw SchemaException::modelNotFound($modelName); - } - - if (!isset($this->loadedModels[$modelName])) { - $expr = $this->client->expr(); - $modelData = $this->client->execute(self::IR_MODEL, OrmQuery::SEARCH_READ, $expr->normalizeDomains($expr->eq('model', $modelName))); - $this->loadedModels[$modelName] = $this->createModel($modelData[0]); - } - - return $this->loadedModels[$modelName]; - } - - public function hasModel(string $modelName): bool - { - return in_array($modelName, $this->getModelNames()); - } - - /** - * Gets all model names. - * - * @return string[] - */ - public function getModelNames(): array - { - if (!$this->modelNames) { - $this->modelNames = array_column($this->client->execute(self::IR_MODEL, OrmQuery::SEARCH_READ, [[]], [ - 'fields' => ['model'], - ]), 'model'); - } - - return $this->modelNames; - } - - /** - * @internal - */ - private function createModel(array $modelData): Model - { - $expr = $this->client->expr(); - $fields = $this->client->execute( - self::IR_MODEL_FIELDS, - OrmQuery::SEARCH_READ, - $expr->normalizeDomains($expr->eq('model_id', $modelData['id'])) - ); - - foreach ($fields as $key => $fieldData) { - $choices = []; - $selectionsIds = array_filter($fieldData['selection_ids'] ?? []); - - if (!empty($selectionsIds)) { - $choices = $this->client->execute( - self::IR_MODEL_FIELD_SELECTION, - OrmQuery::SEARCH_READ, - $expr->normalizeDomains($expr->eq('field_id', $fieldData['id'])) - ); - - foreach ($choices as $index => $choice) { - if (is_array($choice)) { - $choices[$index] = new Choice((string) $choice['name'], $choice['value'], (int) $choice['id']); - } - } - } elseif (!empty($fieldData['selection'])) { - if (preg_match_all('#^\[\s*(\(\'(\w+)\'\,\s*\'(\w+)\'\)\s*\,?\s*)*\s*\]$#', trim($fieldData['selection']), $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - if (isset($match[2], $match[3])) { - $choices[] = new Choice((string) $match[3], $match[2]); - } - } - } - } - - if ($choices) { - $fieldData['selection'] = $choices; - } - - $fields[$key] = new Field($fieldData); - } - - return new Model($this, $modelData, $fields); - } -} diff --git a/src/DBAL/Schema/SchemaException.php b/src/DBAL/Schema/SchemaException.php deleted file mode 100644 index 1f40d01..0000000 --- a/src/DBAL/Schema/SchemaException.php +++ /dev/null @@ -1,18 +0,0 @@ -getName())); - } - - public static function modelNotFound(string $modelName): self - { - return new self(sprintf('The model "%s" was not found on the database', $modelName)); - } -} diff --git a/src/DBAL/Schema/Selection.php b/src/DBAL/Schema/Selection.php deleted file mode 100644 index 260fd5b..0000000 --- a/src/DBAL/Schema/Selection.php +++ /dev/null @@ -1,67 +0,0 @@ -addChoice($choice); - } - } - - public function getIds(): array - { - $ids = []; - - foreach ($this->choices as $choice) { - $ids[] = $choice->getId(); - } - - return $ids; - } - - public function getNames(): array - { - $names = []; - - foreach ($this->choices as $choice) { - $names[] = $choice->getName(); - } - - return $names; - } - - public function getValues(): array - { - $values = []; - - foreach ($this->choices as $choice) { - $values[] = $choice->getValue(); - } - - return $values; - } - - public function getChoices(): array - { - return $this->choices; - } - - /** - * @internal - */ - private function addChoice(Choice $choice): void - { - $this->choices[] = $choice; - } -} diff --git a/src/DBAL/Expression/Comparison.php b/src/Expression/Domain/Comparison.php similarity index 98% rename from src/DBAL/Expression/Comparison.php rename to src/Expression/Domain/Comparison.php index bf4cc15..7449e9a 100644 --- a/src/DBAL/Expression/Comparison.php +++ b/src/Expression/Domain/Comparison.php @@ -1,6 +1,6 @@ Date: Sat, 22 Jan 2022 23:21:47 +0100 Subject: [PATCH 06/80] composer update --- .php-cs-fixer.cache | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 947f8b8..0f10915 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"7.4.24","version":"3.5.0:v3.5.0#333f15e07c866e33e2765e84ba1e0b88e6a3af3b","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":true,"class_definition":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":{"elements":["method","property"]},"encoding":true,"full_opening_tag":true},"hashes":{"C:\\Users\\Ang3\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder1375\\src\\Client.php":4035633777}} \ No newline at end of file +{"php":"7.4.24","version":"3.5.0:v3.5.0#333f15e07c866e33e2765e84ba1e0b88e6a3af3b","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":true,"class_definition":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":{"elements":["method","property"]},"encoding":true,"full_opening_tag":true},"hashes":{"C:\\Users\\Ang3\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder1375\\src\\Client.php":4035633777,"C:\\Users\\Ang3\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder7234\\src\\Client.php":4035633777}} \ No newline at end of file diff --git a/composer.json b/composer.json index b5b7903..7559e94 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "ang3/php-odoo-api-client", "type": "component", "description": "Odoo API client", - "keywords": ["odoo", "api", "client", "domain", "operation", "dbal", "query", "builder", "repository", "builder", "12", "13"], + "keywords": ["odoo", "api", "client", "domain", "operation", "builder", "json"], "license": "MIT", "authors": [ { From b1e1b3c103232459c36e972f7f52a283857fbb9d Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sat, 22 Jan 2022 23:24:26 +0100 Subject: [PATCH 07/80] Delete useless files --- .gitignore | 1 + .php-cs-fixer.cache | 1 - grumphp.yml | 25 ------------------------- 3 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 .php-cs-fixer.cache delete mode 100644 grumphp.yml diff --git a/.gitignore b/.gitignore index 870de85..aaeb0f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /vendor/ /.idea/ .php_cs.cache +.php-cs-fixer.cache .phpunit.result.cache test.php \ No newline at end of file diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache deleted file mode 100644 index 0f10915..0000000 --- a/.php-cs-fixer.cache +++ /dev/null @@ -1 +0,0 @@ -{"php":"7.4.24","version":"3.5.0:v3.5.0#333f15e07c866e33e2765e84ba1e0b88e6a3af3b","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":true,"class_definition":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":{"elements":["method","property"]},"encoding":true,"full_opening_tag":true},"hashes":{"C:\\Users\\Ang3\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder1375\\src\\Client.php":4035633777,"C:\\Users\\Ang3\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder7234\\src\\Client.php":4035633777}} \ No newline at end of file diff --git a/grumphp.yml b/grumphp.yml deleted file mode 100644 index 2b8c5d4..0000000 --- a/grumphp.yml +++ /dev/null @@ -1,25 +0,0 @@ -parameters: - git_dir: . - bin_dir: vendor/bin - tasks: - composer: ~ - # phpcsfixer2: - # allow_risky: false - # cache_file: ~ - # config: ~ - # rules: - # '@Symfony': true - # indentation_type: false - # braces: - # allow_single_line_closure: false - # position_after_functions_and_oop_constructs: "next" - # using_cache: true - # verbose: true - # diff: false - # triggered_by: ['php'] - phplint: ~ - #phpunit: ~ - yamllint: ~ - ascii: - failed: ~ - succeeded: ~ \ No newline at end of file From c189f602fda7409f005f13584bf909f0f540fd13 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 00:37:19 +0100 Subject: [PATCH 08/80] Transport implementation - PhpNativeStreamTransport --- README.md | 2 +- composer.json | 2 - composer.lock | 979 +++++++----------- src/Client.php | 160 +-- src/Connection.php | 87 ++ src/Transport/AbstractRpcTransport.php | 20 + .../JsonRpcPhpNativeStreamTransport.php | 70 ++ src/Transport/TransportInterface.php | 15 + 8 files changed, 600 insertions(+), 735 deletions(-) create mode 100644 src/Connection.php create mode 100644 src/Transport/AbstractRpcTransport.php create mode 100644 src/Transport/JsonRpcPhpNativeStreamTransport.php create mode 100644 src/Transport/TransportInterface.php diff --git a/README.md b/README.md index bced8a9..5971738 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ use Ang3\Component\Odoo\Client; $client = new Client('', '', '', '', $logger = null); // Option 2 : by calling the static method ::createFromConfig() with configuration as array -$client = Client::createFromConfig([ +$client = Client::create([ 'url' => '', 'database' => '', 'username' => '', diff --git a/composer.json b/composer.json index 7559e94..853ca6a 100644 --- a/composer.json +++ b/composer.json @@ -20,8 +20,6 @@ "require": { "php": ">=7.2", "ext-json": "*", - "ang3/php-xmlrpc-client": "^1.0.2", - "symfony/http-client": "^4.0 || ^5.0 || ^6.0", "psr/log": "^1.1" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 9f33d9a..7c701c2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,590 +4,34 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4b49594ef16939b738c5e56bc7bd85c2", + "content-hash": "7efc08e2d57b005d097af99ce27d7992", "packages": [ { - "name": "ang3/php-xmlrpc-client", - "version": "v1.0.3", - "source": { - "type": "git", - "url": "https://github.com/Ang3/php-xmlrpc-client.git", - "reference": "c51c06ac2293977e21ce0be546891a5431284d1a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Ang3/php-xmlrpc-client/zipball/c51c06ac2293977e21ce0be546891a5431284d1a", - "reference": "c51c06ac2293977e21ce0be546891a5431284d1a", - "shasum": "" - }, - "require": { - "ext-xmlrpc": "*", - "php": "^7.2|^8.0" - }, - "require-dev": { - "roave/security-advisories": "dev-master", - "symfony/phpunit-bridge": "^3.4 || ^4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ang3\\Component\\XmlRpc\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Joanis Rouanet", - "email": "joanis.ang3@gmail.com" - } - ], - "description": "PHP XML-RPC client", - "keywords": [ - "client", - "php", - "rpc", - "xml", - "xmlrpc" - ], - "support": { - "issues": "https://github.com/Ang3/php-xmlrpc-client/issues", - "source": "https://github.com/Ang3/php-xmlrpc-client/tree/v1.0.3" - }, - "time": "2021-12-13T18:06:52+00:00" - }, - { - "name": "psr/container", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" - }, - "time": "2021-11-05T16:50:12+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", - "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.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/v2.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": "2021-07-12T14:48:14+00:00" - }, - { - "name": "symfony/http-client", - "version": "v5.4.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "5e344f1402584a56631c81a24ec9403e3159c790" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/5e344f1402584a56631c81a24ec9403e3159c790", - "reference": "5e344f1402584a56631c81a24ec9403e3159c790", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-client-contracts": "^2.4", - "symfony/polyfill-php73": "^1.11", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.0|^2|^3" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "2.4" - }, - "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", - "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4", - "nyholm/psr7": "^1.0", - "php-http/httplug": "^1.0|^2.0", - "psr/http-client": "^1.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4.13|^5.1.5|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "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": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-client/tree/v5.4.2" - }, - "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": "2021-12-29T10:10:35+00:00" - }, - { - "name": "symfony/http-client-contracts", - "version": "v2.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "ec82e57b5b714dbb69300d348bd840b345e24166" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ec82e57b5b714dbb69300d348bd840b345e24166", - "reference": "ec82e57b5b714dbb69300d348bd840b345e24166", - "shasum": "" - }, - "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/http-client-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - } - }, - "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": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v2.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": "2021-11-03T09:24:47+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.24.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5", - "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "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": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.24.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": "2021-06-05T21:20:04+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.24.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9", - "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.24.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": "2021-09-13T13:58:33+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v2.5.0", + "name": "psr/log", + "version": "1.1.4", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc" + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", - "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "suggest": { - "symfony/service-implementation": "" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "dev-master": "1.1.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Contracts\\Service\\": "" + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -596,42 +40,21 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "log", + "psr", + "psr-3" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.0" + "source": "https://github.com/php-fig/log/tree/1.1.4" }, - "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": "2021-11-04T16:48:04+00:00" + "time": "2021-05-03T11:20:27+00:00" } ], "packages-dev": [ @@ -1255,6 +678,54 @@ }, "time": "2016-08-06T20:24:11+00:00" }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, { "name": "psr/event-dispatcher", "version": "1.0.0", @@ -1830,7 +1301,74 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.2" + "source": "https://github.com/symfony/console/tree/v5.4.2" + }, + "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": "2021-12-20T16:11:12+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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/v2.5.0" }, "funding": [ { @@ -1846,7 +1384,7 @@ "type": "tidelift" } ], - "time": "2021-12-20T16:11:12+00:00" + "time": "2021-07-12T14:48:14+00:00" }, { "name": "symfony/event-dispatcher", @@ -2694,6 +2232,168 @@ ], "time": "2021-11-30T18:21:41+00:00" }, + { + "name": "symfony/polyfill-php73", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5", + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.24.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": "2021-06-05T21:20:04+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9", + "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.24.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": "2021-09-13T13:58:33+00:00" + }, { "name": "symfony/polyfill-php81", "version": "v1.24.0", @@ -2926,6 +2626,89 @@ ], "time": "2021-12-26T13:30:54+00:00" }, + { + "name": "symfony/service-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "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": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.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": "2021-11-04T16:48:04+00:00" + }, { "name": "symfony/stopwatch", "version": "v5.4.0", diff --git a/src/Client.php b/src/Client.php index 4a5ff21..c5f0153 100644 --- a/src/Client.php +++ b/src/Client.php @@ -7,10 +7,10 @@ use Ang3\Component\Odoo\Exception\RemoteException; use Ang3\Component\Odoo\Exception\RequestException; use Ang3\Component\Odoo\Expression\ExpressionBuilder; +use Ang3\Component\Odoo\Transport\JsonRpcPhpNativeStreamTransport; +use Ang3\Component\Odoo\Transport\TransportInterface; use InvalidArgumentException; use Psr\Log\LoggerInterface; -use Symfony\Component\HttpClient\HttpClient; -use Symfony\Contracts\HttpClient\HttpClientInterface; class Client { @@ -37,37 +37,14 @@ class Client public const LIST_FIELDS = 'fields_get'; /** - * URL of the database. - * - * @var string - */ - private $url; - - /** - * Name of the database. - * - * @var string - */ - private $database; - - /** - * Username of internal user. - * - * @var string - */ - private $username; - - /** - * Password of internal user. - * - * @var string + * @var Connection */ - private $password; + private $connection; /** - * @var HttpClientInterface + * @var TransportInterface */ - private $httpClient; + private $transport; /** * @var ExpressionBuilder @@ -86,16 +63,11 @@ class Client */ private $uid; - public function __construct(string $url, string $database, string $username, string $password, LoggerInterface $logger = null) + public function __construct(Connection $connection, TransportInterface $transport = null, LoggerInterface $logger = null) { - $this->url = $url; - $this->database = $database; - $this->username = $username; - $this->password = $password; - $this->httpClient = HttpClient::create([ - 'base_uri' => "$url/jsonrpc", - ]); + $this->connection = $connection; $this->expressionBuilder = new ExpressionBuilder(); + $this->transport = $transport ?: new JsonRpcPhpNativeStreamTransport($this->connection); $this->logger = $logger; } @@ -107,24 +79,9 @@ public function __construct(string $url, string $database, string $username, str * * @throws MissingConfigParameterException when a required parameter is missing */ - public static function createFromConfig(array $config, LoggerInterface $logger = null): self + public static function create(array $config, TransportInterface $transport = null, LoggerInterface $logger = null): self { - $getParam = static function ($config, $paramName, $paramKey) { - $value = $config[$paramName] ?? $config[$paramKey] ?? null; - - if (null === $value) { - throw new MissingConfigParameterException(sprintf('Missing config parameter name "%s" or parameter key %d', $paramName, $paramKey)); - } - - return $value; - }; - - $url = $getParam($config, 'url', 0); - $database = $getParam($config, 'database', 1); - $username = $getParam($config, 'username', 2); - $password = $getParam($config, 'password', 3); - - return new self($url, $database, $username, $password, $logger); + return new self(Connection::create($config), $transport, $logger); } /** @@ -135,7 +92,7 @@ public static function createFromConfig(array $config, LoggerInterface $logger = * * @return int the ID of the new record */ - public function create(string $modelName, array $data): int + public function insert(string $modelName, array $data): int { if (!$data) { throw new InvalidArgumentException('Data cannot be empty'); @@ -155,9 +112,9 @@ public function create(string $modelName, array $data): int */ public function read(string $modelName, $ids, array $options = []): array { - $ids = [is_int($ids) ? [$ids] : (array) $ids]; + $ids = is_int($ids) ? [$ids] : (array) $ids; - return (array) $this->execute($modelName, self::READ, [[$ids]], $options); + return (array) $this->execute($modelName, self::READ, [$ids], $options); } /** @@ -338,9 +295,9 @@ public function execute(string $name, string $method, array $parameters = [], ar return $this->request( self::SERVICE_OBJECT, 'execute_kw', - $this->database, + $this->connection->getDatabase(), $this->authenticate(), - $this->password, + $this->connection->getPassword(), $name, $method, $parameters, @@ -362,9 +319,9 @@ public function authenticate(): int $this->uid = $this->request( self::SERVICE_COMMON, 'login', - $this->database, - $this->username, - $this->password + $this->connection->getDatabase(), + $this->connection->getUsername(), + $this->connection->getPassword() ); if (!$this->uid || !is_int($this->uid)) { @@ -376,7 +333,7 @@ public function authenticate(): int } /** - * @param mixed[] $arguments + * @param mixed ...$arguments * * @return mixed */ @@ -387,34 +344,15 @@ public function request(string $service, string $method, ...$arguments) $this->logger->info('JSON RPC request #{request_id} - {service}::{method} (uid: #{uid})', $context); } - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'call', - 'params' => [ - 'service' => $service, - 'method' => $method, - 'args' => $arguments, - ], - 'id' => uniqid('odoo_request'), - ]; - - dump($data); - - $response = $this->httpClient->request('POST', '', [ - 'json' => $data, - ]); - - $result = $response->getContent(); + $payload = $this->transport->request($service, $method, $arguments); if ($this->logger) { - $loggedResult = $result; + $loggedResult = json_encode($payload); $this->logger->debug(sprintf('Request result: %s', $loggedResult), [ 'request_id' => $context['request_id'], ]); } - $payload = json_decode($result, true); - if (is_array($payload['error'] ?? null)) { throw RemoteException::create($payload); } @@ -422,60 +360,14 @@ public function request(string $service, string $method, ...$arguments) return $payload['result']; } - public function getIdentifier(): string - { - $database = preg_replace('([^a-zA-Z0-9_])', '_', $this->database); - $user = preg_replace('([^a-zA-Z0-9_])', '_', $this->username); - - return sprintf('%s.%s.%s', sha1($this->url), $database, $user); - } - - public function getUrl(): string + public function getConnection(): Connection { - return $this->url; + return $this->connection; } - public function setUrl(string $url): self + public function setConnection(Connection $connection): void { - $this->url = $url; - - return $this; - } - - public function getDatabase(): string - { - return $this->database; - } - - public function setDatabase(string $database): self - { - $this->database = $database; - - return $this; - } - - public function getUsername(): string - { - return $this->username; - } - - public function setUsername(string $username): self - { - $this->username = $username; - - return $this; - } - - public function getPassword(): string - { - return $this->password; - } - - public function setPassword(string $password): self - { - $this->password = $password; - - return $this; + $this->connection = $connection; } public function getExpressionBuilder(): ExpressionBuilder diff --git a/src/Connection.php b/src/Connection.php new file mode 100644 index 0000000..4a9c381 --- /dev/null +++ b/src/Connection.php @@ -0,0 +1,87 @@ +url = $url; + $this->database = $database; + $this->username = $username; + $this->password = $password; + } + + public static function create(array $config): self + { + $getParam = static function ($config, $paramName) { + $value = $config[$paramName] ?? null; + + if (null === $value) { + throw new MissingConfigParameterException(sprintf('Missing config parameter name "%s".', $paramName)); + } + + return $value; + }; + + return new self( + $getParam($config, 'url'), + $getParam($config, 'database'), + $getParam($config, 'username'), + $getParam($config, 'password') + ); + } + + public function getUrl(): string + { + return $this->url; + } + + public function getDatabase(): string + { + return $this->database; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getPassword(): string + { + return $this->password; + } + + /** + * Gets the unique name of this connection. + */ + public function getIdentifier(): string + { + $database = preg_replace('([^a-zA-Z0-9_])', '_', $this->database); + $user = preg_replace('([^a-zA-Z0-9_])', '_', $this->username); + + return sprintf('%s.%s.%s', sha1($this->url), $database, $user); + } +} diff --git a/src/Transport/AbstractRpcTransport.php b/src/Transport/AbstractRpcTransport.php new file mode 100644 index 0000000..23d0967 --- /dev/null +++ b/src/Transport/AbstractRpcTransport.php @@ -0,0 +1,20 @@ + + * @author Jules Sayer + */ +class JsonRpcPhpNativeStreamTransport extends AbstractRpcTransport +{ + /** + * JSON-RPC endpoint. + */ + public const ENDPOINT_JSON_RPC = 'jsonrpc'; + + /** + * @var Connection + */ + private $connection; + + public function __construct(Connection $credentials) + { + $this->connection = $credentials; + } + + public function request(string $service, string $method, array $arguments = []): array + { + $payload = json_encode([ + 'jsonrpc' => '2.0', + 'method' => 'call', + 'params' => [ + 'service' => $service, + 'method' => $method, + 'args' => $arguments, + ], + 'id' => uniqid('odoo_request'), + ]); + + if (JSON_ERROR_NONE !== json_last_error()) { + throw new RequestException(sprintf('Failed to encode data to JSON: %s', json_last_error_msg())); + } + + $context = stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'timeout' => 120, + 'header' => 'Content-Type: application/json', + 'content' => $payload, + ], + ]); + + $url = sprintf('%s/%s', $this->connection->getUrl(), self::ENDPOINT_JSON_RPC); + $request = file_get_contents($url, false, $context); + + if (false === $request) { + throw new RequestException('Unable to connect to Odoo.'); + } + + $data = json_decode($request, true); + + if (JSON_ERROR_NONE !== json_last_error()) { + throw new RequestException(sprintf('Failed to decode JSON data: %s', json_last_error_msg())); + } + + return $data; + } +} diff --git a/src/Transport/TransportInterface.php b/src/Transport/TransportInterface.php new file mode 100644 index 0000000..814c61e --- /dev/null +++ b/src/Transport/TransportInterface.php @@ -0,0 +1,15 @@ + Date: Sun, 23 Jan 2022 00:48:22 +0100 Subject: [PATCH 09/80] Renamed JSON-RPC PHP stream transport --- src/Client.php | 4 ++-- ...ativeStreamTransport.php => JsonRpcPhpStreamTransport.php} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/Transport/{JsonRpcPhpNativeStreamTransport.php => JsonRpcPhpStreamTransport.php} (96%) diff --git a/src/Client.php b/src/Client.php index c5f0153..a6ee584 100644 --- a/src/Client.php +++ b/src/Client.php @@ -7,7 +7,7 @@ use Ang3\Component\Odoo\Exception\RemoteException; use Ang3\Component\Odoo\Exception\RequestException; use Ang3\Component\Odoo\Expression\ExpressionBuilder; -use Ang3\Component\Odoo\Transport\JsonRpcPhpNativeStreamTransport; +use Ang3\Component\Odoo\Transport\JsonRpcPhpStreamTransport; use Ang3\Component\Odoo\Transport\TransportInterface; use InvalidArgumentException; use Psr\Log\LoggerInterface; @@ -67,7 +67,7 @@ public function __construct(Connection $connection, TransportInterface $transpor { $this->connection = $connection; $this->expressionBuilder = new ExpressionBuilder(); - $this->transport = $transport ?: new JsonRpcPhpNativeStreamTransport($this->connection); + $this->transport = $transport ?: new JsonRpcPhpStreamTransport($this->connection); $this->logger = $logger; } diff --git a/src/Transport/JsonRpcPhpNativeStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php similarity index 96% rename from src/Transport/JsonRpcPhpNativeStreamTransport.php rename to src/Transport/JsonRpcPhpStreamTransport.php index 6012035..ee97d13 100644 --- a/src/Transport/JsonRpcPhpNativeStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -9,7 +9,7 @@ * @author Joanis ROUANET * @author Jules Sayer */ -class JsonRpcPhpNativeStreamTransport extends AbstractRpcTransport +class JsonRpcPhpStreamTransport extends AbstractRpcTransport { /** * JSON-RPC endpoint. From 917b289036cf5e1cbbe16440a131445538976b8d Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 00:48:24 +0100 Subject: [PATCH 10/80] Create UPGRADE-8.0.md --- UPGRADE-8.0.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 UPGRADE-8.0.md diff --git a/UPGRADE-8.0.md b/UPGRADE-8.0.md new file mode 100644 index 0000000..06793e7 --- /dev/null +++ b/UPGRADE-8.0.md @@ -0,0 +1,24 @@ +UPGRADE FROM 7.x to 8.0 +======================= + +**Binary compatibility break (BC break)** + +Global +------ + +- The PHP extension ```php-xmlrpc``` is not used anymore. +- The PHP extension ```php-json``` is now required. +- DBAL features was moved to the package [ang3/php-odoo-dbal](...) + - You must install it to be able to use query features. + +Client +------ + +- Renamed method ```Client::create()``` to ```Client::insert()``` +- Renamed static method ```Client::createFromConfig()``` to ```Client::create()``` + +Expression builder +------------------ + +- New folder/namespace architecture. +- Domain expressions fixes. \ No newline at end of file From f10fdb964bfaeaa79380322a795388fc1013d3f4 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 00:56:52 +0100 Subject: [PATCH 11/80] Factorized RPC data normalization --- src/Transport/AbstractRpcTransport.php | 14 ++++++++++++++ src/Transport/JsonRpcPhpStreamTransport.php | 11 +---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Transport/AbstractRpcTransport.php b/src/Transport/AbstractRpcTransport.php index 23d0967..5c19296 100644 --- a/src/Transport/AbstractRpcTransport.php +++ b/src/Transport/AbstractRpcTransport.php @@ -6,6 +6,20 @@ abstract class AbstractRpcTransport implements TransportInterface { + public function normalizeRpcData(string $service, string $method, array $arguments = []): array + { + return [ + 'jsonrpc' => '2.0', + 'method' => 'call', + 'params' => [ + 'service' => $service, + 'method' => $method, + 'args' => $arguments, + ], + 'id' => uniqid('odoo_request'), + ]; + } + /** * @return mixed */ diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index ee97d13..ebcb5ee 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -28,16 +28,7 @@ public function __construct(Connection $credentials) public function request(string $service, string $method, array $arguments = []): array { - $payload = json_encode([ - 'jsonrpc' => '2.0', - 'method' => 'call', - 'params' => [ - 'service' => $service, - 'method' => $method, - 'args' => $arguments, - ], - 'id' => uniqid('odoo_request'), - ]); + $payload = json_encode($this->normalizeRpcData($service, $method, $arguments)); if (JSON_ERROR_NONE !== json_last_error()) { throw new RequestException(sprintf('Failed to encode data to JSON: %s', json_last_error_msg())); From c7c292c042bd67abfd12aa122ba17e2046b02a53 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 00:57:24 +0100 Subject: [PATCH 12/80] Update AbstractRpcTransport.php --- src/Transport/AbstractRpcTransport.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Transport/AbstractRpcTransport.php b/src/Transport/AbstractRpcTransport.php index 5c19296..2f47778 100644 --- a/src/Transport/AbstractRpcTransport.php +++ b/src/Transport/AbstractRpcTransport.php @@ -16,7 +16,7 @@ public function normalizeRpcData(string $service, string $method, array $argumen 'method' => $method, 'args' => $arguments, ], - 'id' => uniqid('odoo_request'), + 'id' => uniqid('odoo_jsonrpc'), ]; } From dea07629161ce97d38de2569f6857f90a8f9df5c Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 01:01:27 +0100 Subject: [PATCH 13/80] Deleted connection setter --- src/Client.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Client.php b/src/Client.php index a6ee584..9fcb28d 100644 --- a/src/Client.php +++ b/src/Client.php @@ -365,11 +365,6 @@ public function getConnection(): Connection return $this->connection; } - public function setConnection(Connection $connection): void - { - $this->connection = $connection; - } - public function getExpressionBuilder(): ExpressionBuilder { return $this->expressionBuilder; From dc1d648a5b7c5e74693ca3fdaf4f94de895b5718 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 01:15:06 +0100 Subject: [PATCH 14/80] Fixed code --- src/Expression/ExpressionBuilder.php | 10 +++++--- .../ExpressionBuilderAwareTrait.php | 25 ------------------- src/Transport/JsonRpcPhpStreamTransport.php | 4 +-- 3 files changed, 8 insertions(+), 31 deletions(-) delete mode 100644 src/Expression/ExpressionBuilderAwareTrait.php diff --git a/src/Expression/ExpressionBuilder.php b/src/Expression/ExpressionBuilder.php index 258b72e..78bfc48 100644 --- a/src/Expression/ExpressionBuilder.php +++ b/src/Expression/ExpressionBuilder.php @@ -7,8 +7,10 @@ use Ang3\Component\Odoo\Expression\Domain\DomainInterface; use Ang3\Component\Odoo\Expression\Exception\ConversionException; use Ang3\Component\Odoo\Expression\Operation\CollectionOperation; +use Ang3\Component\Odoo\Expression\Operation\OperationInterface; use DateTime; use DateTimeInterface; +use DateTimeZone; use Exception; use InvalidArgumentException; @@ -324,7 +326,7 @@ private function formatValue($value) return $this->formatValue($value->toArray()); } - if ($value instanceof CollectionOperation) { + if ($value instanceof OperationInterface) { return $this->formatValue($value->toArray()); } @@ -335,9 +337,9 @@ private function formatValue($value) throw new ConversionException(sprintf('Failed to convert date from timestamp "%d"', $value->getTimestamp()), 0, $e); } - $date->setTimezone(new \DateTimeZone('UTC')); - - return $date->format('Y-m-d H:i:s'); + return $date + ->setTimezone(new DateTimeZone('UTC')) + ->format('Y-m-d H:i:s'); } } diff --git a/src/Expression/ExpressionBuilderAwareTrait.php b/src/Expression/ExpressionBuilderAwareTrait.php deleted file mode 100644 index 1208eb8..0000000 --- a/src/Expression/ExpressionBuilderAwareTrait.php +++ /dev/null @@ -1,25 +0,0 @@ -getExpressionBuilder(); - } - - public function getExpressionBuilder(): ExpressionBuilder - { - if (!$this->expressionBuilder) { - $this->expressionBuilder = new ExpressionBuilder(); - } - - return $this->expressionBuilder; - } -} diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index ebcb5ee..1d9d0d7 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -21,9 +21,9 @@ class JsonRpcPhpStreamTransport extends AbstractRpcTransport */ private $connection; - public function __construct(Connection $credentials) + public function __construct(Connection $connection) { - $this->connection = $credentials; + $this->connection = $connection; } public function request(string $service, string $method, array $arguments = []): array From 006cf6310ac86ff72576102a5c3bfd5e95dcf698 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 01:15:51 +0100 Subject: [PATCH 15/80] Deleted debug dumps --- src/Expression/ExpressionBuilder.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Expression/ExpressionBuilder.php b/src/Expression/ExpressionBuilder.php index 78bfc48..05be2f4 100644 --- a/src/Expression/ExpressionBuilder.php +++ b/src/Expression/ExpressionBuilder.php @@ -279,15 +279,7 @@ public function normalizeDomains(iterable $criteria = null): array return $this->normalizeDomains(); } - if ($criteria instanceof CompositeDomain) { - dump($criteriaArray); - - return $criteriaArray; - } - - dump([$criteriaArray]); - - return [$criteriaArray]; + return $criteria instanceof CompositeDomain ? $criteriaArray : [$criteriaArray]; } /** From 425e7399530f859379c7baf7c63a34d3b76ec12b Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 01:38:36 +0100 Subject: [PATCH 16/80] Create index.rst --- docs/index.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/index.rst diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..42e8585 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,6 @@ +PHP Odoo API client documentation +================================= + +This package is a PHP library to help the developer +to connect and interact with an Odoo database. +It follows `The official Odoo documentation `_ \ No newline at end of file From 9958973f325c0d76db1ad8f958c1ef991b1bbfa7 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 01:59:58 +0100 Subject: [PATCH 17/80] Update index.rst --- docs/index.rst | 69 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 42e8585..5d09eba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,6 +1,69 @@ PHP Odoo API client documentation ================================= -This package is a PHP library to help the developer -to connect and interact with an Odoo database. -It follows `The official Odoo documentation `_ \ No newline at end of file +This package is a PHP library to connect and interact with an Odoo database. +It follows [the official Odoo documentation](https://www.odoo.com/documentation/13.0/developer/misc/api/odoo.html). + +Main features +============= + +- Built-in ORM methods +- Expression builder for domains and collection operations +- Database Abstraction Layer (DBAL) + - Query builder + - Repository + - Schema + +Getting started +=============== + +Software requirements +--------------------- + +PHP version 7.2 or newer to develop using PhpSpreadsheet. Other requirements, such as PHP extensions, are enforced by +composer. See the `require` section of ::composer.json +for details. + +The installation of the PHP extension **php-curl** is recommended +for HTTP requests. If missing, PHP native streams are used. + +Odoo database support +--------------------- + +| Odoo series | Compatibility | Comment | +| --- | --- | --- | +| Newer | Unknown | Needs feedbacks | +| v13.0 | Yes | Some Odoo model names changed (e.g account.invoice > account.move) | +| v12.0 | Yes | First tested version | +| Older | Unknown | Needs feedbacks | + +Installation +------------ + +Open a command console, enter your project directory and execute the +following command to download the latest stable version of the client: + +```console +$ composer require ang3/php-odoo-api-client +``` + +This command requires you to have Composer installed globally, as explained +in the [installation chapter](https://getcomposer.org/doc/00-intro.md) +of the Composer documentation. + +Basic usage +----------- + +.. code-block:: php + '', + 'database' => '', + 'username' => '', + 'password' => '', + ], $logger = null); \ No newline at end of file From f6f520b731d1bb4697894c1bb11499a9d7226080 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 02:03:45 +0100 Subject: [PATCH 18/80] Update index.md --- docs/{index.rst => index.md} | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) rename docs/{index.rst => index.md} (79%) diff --git a/docs/index.rst b/docs/index.md similarity index 79% rename from docs/index.rst rename to docs/index.md index 5d09eba..8441112 100644 --- a/docs/index.rst +++ b/docs/index.md @@ -24,7 +24,7 @@ PHP version 7.2 or newer to develop using PhpSpreadsheet. Other requirements, su composer. See the `require` section of ::composer.json for details. -The installation of the PHP extension **php-curl** is recommended +The installation of the PHP extension `php-curl` is recommended for HTTP requests. If missing, PHP native streams are used. Odoo database support @@ -54,16 +54,17 @@ of the Composer documentation. Basic usage ----------- -.. code-block:: php - '', - 'database' => '', - 'username' => '', - 'password' => '', - ], $logger = null); \ No newline at end of file +$client = Client::create([ + 'url' => '', + 'database' => '', + 'username' => '', + 'password' => '', +], $logger = null); +``` \ No newline at end of file From 7ff0718bbe35a060d0030f4d3ecd05bdf85a6064 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 02:04:13 +0100 Subject: [PATCH 19/80] Update index.md --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 8441112..8333e0c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -20,7 +20,7 @@ Getting started Software requirements --------------------- -PHP version 7.2 or newer to develop using PhpSpreadsheet. Other requirements, such as PHP extensions, are enforced by +PHP version 7.2 or newer to develop using the client. Other requirements, such as PHP extensions, are enforced by composer. See the `require` section of ::composer.json for details. From 22674acf38ab04e03bca7b1ac6707d41bd0abf88 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 02:05:30 +0100 Subject: [PATCH 20/80] Update index.md --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 8333e0c..c483d2d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -21,7 +21,7 @@ Software requirements --------------------- PHP version 7.2 or newer to develop using the client. Other requirements, such as PHP extensions, are enforced by -composer. See the `require` section of ::composer.json +composer. See the `require` section of [composer.json file](../composer.json) for details. The installation of the PHP extension `php-curl` is recommended From e6945cd5f18ba674240fcad8437d7a0ff218289f Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 02:26:05 +0100 Subject: [PATCH 21/80] readthedocs config files --- .readthedocs.yaml | 9 +++++++++ mkdocs.yaml | 5 +++++ 2 files changed, 14 insertions(+) create mode 100644 .readthedocs.yaml create mode 100644 mkdocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..49d89cd --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,9 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +mkdocs: + configuration: mkdocs.yaml \ No newline at end of file diff --git a/mkdocs.yaml b/mkdocs.yaml new file mode 100644 index 0000000..bd27501 --- /dev/null +++ b/mkdocs.yaml @@ -0,0 +1,5 @@ +site_name: PPHP Odoo API client +repo_url: https://github.com/Ang3/php-odoo-api-client +edit_uri: edit/master/docs/ + +theme: readthedocs \ No newline at end of file From 7a1fbd9bb2196b89558fdc8803709e3ae01f8062 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 02:26:36 +0100 Subject: [PATCH 22/80] Update mkdocs.yaml --- mkdocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yaml b/mkdocs.yaml index bd27501..64ebe09 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -1,4 +1,4 @@ -site_name: PPHP Odoo API client +site_name: PHP Odoo API client repo_url: https://github.com/Ang3/php-odoo-api-client edit_uri: edit/master/docs/ From f32cda05ab3ea06cfd7d2a7dc8eca67fd90b22c7 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 02:28:02 +0100 Subject: [PATCH 23/80] Update mkdocs.yaml --- mkdocs.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/mkdocs.yaml b/mkdocs.yaml index 64ebe09..8d13c62 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -1,5 +1,3 @@ site_name: PHP Odoo API client repo_url: https://github.com/Ang3/php-odoo-api-client -edit_uri: edit/master/docs/ - theme: readthedocs \ No newline at end of file From 7065766d66c05874e4defdec90895412448e7780 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 02:28:46 +0100 Subject: [PATCH 24/80] Update .readthedocs.yaml --- .readthedocs.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 49d89cd..2def437 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -5,5 +5,10 @@ # Required version: 2 +build: + os: ubuntu-20.04 + tools: + python: "3.9" + mkdocs: configuration: mkdocs.yaml \ No newline at end of file From 939cf5942217a0e858e5b7987ea59c93ff3f8cf2 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 02:43:49 +0100 Subject: [PATCH 25/80] Update docs --- README.md | 3 +- docs/client/expression_builder.md | 245 ++++++++++++++++++++++++++++++ docs/index.md | 3 + 3 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 docs/client/expression_builder.md diff --git a/README.md b/README.md index 5971738..816c3a6 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,8 @@ use Ang3\Component\Odoo\DBAL\Expression\DomainInterface; * Create a new record. * * @return int the ID of the new record - */public function create(string $modelName, array $data): int; + */ +public function create(string $modelName, array $data): int; /** * Update record(s). diff --git a/docs/client/expression_builder.md b/docs/client/expression_builder.md new file mode 100644 index 0000000..9072214 --- /dev/null +++ b/docs/client/expression_builder.md @@ -0,0 +1,245 @@ +Expression builder +================== + +There are two kinds of expressions : ```domains``` for criteria +and ```collection operations``` in data writing context. +Odoo has its own array format for those expressions. +The aim of the expression builder is to provide some +helper methods to simplify your programmer's life. + +Here is an example of how to get a builder from a client or record manager: + +```php +$expr = $client->getExpressionBuilder(); +``` + +You can still use the expression builder as standalone by creating a new instance: + +```php +use Ang3\Component\Odoo\Expression\ExpressionBuilder; + +$expr = new ExpressionBuilder(); +``` + +Domains +------- + +For all **select/search/count** queries, +Odoo is waiting for an array of [domains](https://www.odoo.com/documentation/13.0/reference/orm.html#search-domains) +with a *polish notation* for logical operations (```AND```, ```OR``` and ```NOT```). + +It could be quickly ugly to do a complex domain, but don't worry the builder makes all +for you. :-) + +Each domain builder method creates an instance of ```Ang3\Component\Odoo\Expression\Domain\DomainInterface```. +The only one method of this interface is ```toArray()``` to get a normalized array of the expression. + +To illustrate how to work with it, here is an example using ```ExpressionBuilder``` helper methods: + +```php +// Get the expression builder +$expr = $recordManager->expr(); + +$result = $recordManager->findBy('model_name', $expr->andX( // Logical node "AND" + $expr->gte('id', 10), // id >= 10 + $expr->lte('id', 100), // id <= 10 +)); +``` + +Of course, you can nest logical nodes: + +```php +$result = $recordManager->findBy('model_name', $expr->andX( + $expr->orX( + $expr->eq('A', 1), + $expr->eq('B', 1) + ), + $expr->orX( + $expr->eq('C', 1), + $expr->eq('D', 1), + $expr->eq('E', 1) + ) +)); +``` + +Internally, the client formats automatically all domains by calling the special builder +method ```normalizeDomains()```. + +Here is a complete list of helper methods available in ```ExpressionBuilder``` for domain expressions: + +```php +/** + * Create a logical operation "AND". + */ +public function andX(DomainInterface ...$domains): CompositeDomain; + +/** + * Create a logical operation "OR". + */ +public function orX(DomainInterface ...$domains): CompositeDomain; + +/** + * Create a logical operation "NOT". + */ +public function notX(DomainInterface ...$domains): CompositeDomain; + +/** + * Check if the field is EQUAL TO the value. + * + * @param mixed $value + */ +public function eq(string $fieldName, $value): Comparison; + +/** + * Check if the field is NOT EQUAL TO the value. + * + * @param mixed $value + */ +public function neq(string $fieldName, $value): Comparison; + +/** + * Check if the field is UNSET OR EQUAL TO the value. + * + * @param mixed $value + */ +public function ueq(string $fieldName, $value): Comparison; + +/** + * Check if the field is LESS THAN the value. + * + * @param mixed $value + */ +public function lt(string $fieldName, $value): Comparison; + +/** + * Check if the field is LESS THAN OR EQUAL the value. + * + * @param mixed $value + */ +public function lte(string $fieldName, $value): Comparison; + +/** + * Check if the field is GREATER THAN the value. + * + * @param mixed $value + */ +public function gt(string $fieldName, $value): Comparison; + +/** + * Check if the field is GREATER THAN OR EQUAL the value. + * + * @param mixed $value + */ +public function gte(string $fieldName, $value): Comparison; + +/** + * Check if the variable is LIKE the value. + * + * An underscore _ in the pattern stands for (matches) any single character + * A percent sign % matches any string of zero or more characters. + * + * If $strict is set to FALSE, the value pattern is "%value%" (automatically wrapped into signs %). + * + * @param mixed $value + */ +public function like(string $fieldName, $value, bool $strict = false, bool $caseSensitive = true): Comparison; + +/** + * Check if the field is IS NOT LIKE the value. + * + * @param mixed $value + */ +public function notLike(string $fieldName, $value, bool $caseSensitive = true): Comparison; + +/** + * Check if the field is IN values list. + */ +public function in(string $fieldName, array $values = []): Comparison; + +/** + * Check if the field is NOT IN values list. + */ +public function notIn(string $fieldName, array $values = []): Comparison; +``` + +Collection operations +--------------------- + +In data writing context with queries of type ```insert``` or ```update```, +Odoo allows you to manage ***toMany** collection fields with special commands. + +Please read the [ORM documentation](https://www.odoo.com/documentation/13.0/reference/orm.html#openerp-models-relationals-format) +to get more information about. + +The expression builder provides helper methods to build a well-formed *operation command*: +each operation method returns an instance of ```Ang3\Component\Odoo\Expression\Operation\CollectionOperation```. +Like domains, the only one method of this interface is ```toArray()``` to get a normalized array of the expression. + +To illustrate how to work with operations, here is an example using ```ExpressionBuilder``` helper methods: + +```php +// Get the expression builder +$expr = $recordManager->expr(); + +// Prepare data for a new record +$data = [ + 'foo' => 'bar', + 'bar_ids' => [ // Field of type "manytoMany" + $expr->addRecord(3), // Add the record of ID 3 to the set + $expr->createRecord([ // Create a new sub record and add it to the set + 'bar' => 'baz' + // ... + ]) + ] +]; + +$result = $recordManager->create('model_name', $data); +``` + +Internally, the client formats automatically the whole query parameters for all writing methods +(```create``` and ```update```) by calling the special builder +method ```normalizeData()```. + +Here is a complete list of helper methods available in ```ExpressionBuilder``` for operation expressions: + +```php +/** + * Adds a new record created from data. + */ +public function createRecord(array $data): CollectionOperation; + +/** + * Updates an existing record of id $id with data. + * /!\ Can not be used in record CREATE query. + */ +public function updateRecord(int $id, array $data): CollectionOperation; + +/** + * Adds an existing record of id $id to the collection. + */ +public function addRecord(int $id): CollectionOperation; + +/** + * Removes the record of id $id from the collection, but does not delete it. + * /!\ Can not be used in record CREATE query. + */ +public function removeRecord(int $id): CollectionOperation; + +/** + * Removes the record of id $id from the collection, then deletes it from the database. + * /!\ Can not be used in record CREATE query. + */ +public function deleteRecord(int $id): CollectionOperation; + +/** + * Replaces all existing records in the collection by the $ids list, + * Equivalent to using the command "clear" followed by a command "add" for each id in $ids. + */ +public function replaceRecords(array $ids = []): CollectionOperation; + +/** + * Removes all records from the collection, equivalent to using the command "remove" on every record explicitly. + * /!\ Can not be used in record CREATE query. + */ +public function clearRecords(): CollectionOperation; +``` \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index c483d2d..1c5d04a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -54,6 +54,9 @@ of the Composer documentation. Basic usage ----------- +First, import the client with a `use` statement and create a client instance statically +with your config: + ```php Date: Sun, 23 Jan 2022 02:45:50 +0100 Subject: [PATCH 26/80] Update expression_builder.md --- docs/client/expression_builder.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/client/expression_builder.md b/docs/client/expression_builder.md index 9072214..f3b3f6c 100644 --- a/docs/client/expression_builder.md +++ b/docs/client/expression_builder.md @@ -7,7 +7,7 @@ Odoo has its own array format for those expressions. The aim of the expression builder is to provide some helper methods to simplify your programmer's life. -Here is an example of how to get a builder from a client or record manager: +Here is an example of how to get a builder from the client: ```php $expr = $client->getExpressionBuilder(); @@ -38,9 +38,9 @@ To illustrate how to work with it, here is an example using ```ExpressionBuilder ```php // Get the expression builder -$expr = $recordManager->expr(); +$expr = $client->expr(); -$result = $recordManager->findBy('model_name', $expr->andX( // Logical node "AND" +$result = $client->findBy('model_name', $expr->andX( // Logical node "AND" $expr->gte('id', 10), // id >= 10 $expr->lte('id', 100), // id <= 10 )); @@ -49,7 +49,7 @@ $result = $recordManager->findBy('model_name', $expr->andX( // Logical node "AND Of course, you can nest logical nodes: ```php -$result = $recordManager->findBy('model_name', $expr->andX( +$result = $client->findBy('model_name', $expr->andX( $expr->orX( $expr->eq('A', 1), $expr->eq('B', 1) @@ -179,7 +179,7 @@ To illustrate how to work with operations, here is an example using ```Expressio ```php // Get the expression builder -$expr = $recordManager->expr(); +$expr = $client->expr(); // Prepare data for a new record $data = [ @@ -193,7 +193,7 @@ $data = [ ] ]; -$result = $recordManager->create('model_name', $data); +$result = $client->create('model_name', $data); ``` Internally, the client formats automatically the whole query parameters for all writing methods From e0e97dd5e519509c4ff4a2cabcc2ab3cf14b64da Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 23 Jan 2022 02:54:45 +0100 Subject: [PATCH 27/80] Update index.md --- docs/index.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/index.md b/docs/index.md index 1c5d04a..2f81814 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,10 +9,8 @@ Main features - Built-in ORM methods - Expression builder for domains and collection operations -- Database Abstraction Layer (DBAL) - - Query builder - - Repository - - Schema +- Query builder to build your queries easily +- Repositories to work on a specific model or create reusable queries Getting started =============== From 0792ede25f19469037bcf688670356d4707d145b Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sat, 7 May 2022 17:41:40 +0200 Subject: [PATCH 28/80] Fixed code --- bin/dev/check_code.sh | 41 - bin/dev/fix_code.sh | 20 - composer.json | 3 +- composer.lock | 830 ++++++++---------- phpstan.neon | 4 +- src/Client.php | 23 +- src/Connection.php | 3 + src/Exception/AuthenticationException.php | 3 + src/Exception/ExceptionInterface.php | 3 + .../MissingConfigParameterException.php | 3 + src/Exception/RemoteException.php | 3 + src/Exception/RequestException.php | 3 + src/Expression/Domain/Comparison.php | 3 + src/Expression/Domain/CompositeDomain.php | 3 + src/Expression/Domain/CustomDomain.php | 3 + src/Expression/Domain/DomainInterface.php | 3 + .../Exception/ConversionException.php | 3 + src/Expression/ExpressionBuilder.php | 12 +- .../Operation/CollectionOperation.php | 3 + .../Operation/OperationInterface.php | 3 + src/Transport/AbstractRpcTransport.php | 3 + src/Transport/JsonRpcPhpStreamTransport.php | 12 +- src/Transport/TransportInterface.php | 5 + 23 files changed, 440 insertions(+), 552 deletions(-) delete mode 100644 bin/dev/check_code.sh delete mode 100644 bin/dev/fix_code.sh diff --git a/bin/dev/check_code.sh b/bin/dev/check_code.sh deleted file mode 100644 index 9240572..0000000 --- a/bin/dev/check_code.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh - -# shellcheck disable=SC2039 -echo -e "\033[33;1m" -echo -e "Checking code" -echo -e "=============\033[0m" -echo - -if [ -z "$1" ] -then - directory='src' - echo -e "No directory specified (default: src)." - echo -else - directory=$1 - echo -e "Level:" $1 - echo -fi - -if [ -z "$2" ] -then - level=7 - echo -e "No level specified (default: 7 [max])." - echo -else - level=$2 - echo -e "Level:" $2 - echo -fi - -if [ $directory = "src" ] -then - config_file='phpstan.neon' -else - config_file='phpstan.'$directory'.neon' -fi - -echo -e "Config file:" $config_file -echo - -vendor/bin/phpstan analyse $directory -c $config_file -l $level -vvv \ No newline at end of file diff --git a/bin/dev/fix_code.sh b/bin/dev/fix_code.sh deleted file mode 100644 index cbcde6b..0000000 --- a/bin/dev/fix_code.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -# shellcheck disable=SC2039 -echo -e "\033[33;1m" -echo -e "Fixing code" -echo -e "===========\033[0m" -echo - -if [ -z "$1" ] -then - directory='src' - echo "No directory specified (default: src)." - echo -else - directory=$1 - echo "Directory:" $directory - echo -fi - -vendor/bin/php-cs-fixer -v fix $directory --rules='{"@Symfony": true}' \ No newline at end of file diff --git a/composer.json b/composer.json index 853ca6a..f38082f 100644 --- a/composer.json +++ b/composer.json @@ -20,12 +20,11 @@ "require": { "php": ">=7.2", "ext-json": "*", - "psr/log": "^1.1" + "psr/log": "^1.1||^2.0||3.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.0", "roave/security-advisories": "dev-master", - "phpstan/phpstan": "^0.12.94", "symfony/var-dumper": ">=3.4", "symfony/phpunit-bridge": ">=3.4", "symfony/property-info": ">=3.4", diff --git a/composer.lock b/composer.lock index 7c701c2..16d0495 100644 --- a/composer.lock +++ b/composer.lock @@ -4,34 +4,34 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7efc08e2d57b005d097af99ce27d7992", + "content-hash": "3ad65fbdfbc68c74d97b4e5b6cfad284", "packages": [ { "name": "psr/log", - "version": "1.1.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" }, "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/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "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/", @@ -52,38 +52,38 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/3.0.0" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2021-07-14T16:46:02+00:00" } ], "packages-dev": [ { "name": "composer/pcre", - "version": "1.0.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560" + "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560", + "url": "https://api.github.com/repos/composer/pcre/zipball/e300eb6c535192decd27a85bc72a9290f0d6b3bd", + "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "phpstan/phpstan": "^1.3", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^4.2 || ^5" + "symfony/phpunit-bridge": "^5" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -111,7 +111,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/1.0.1" + "source": "https://github.com/composer/pcre/tree/3.0.0" }, "funding": [ { @@ -127,27 +127,27 @@ "type": "tidelift" } ], - "time": "2022-01-21T20:24:37+00:00" + "time": "2022-02-25T20:21:48+00:00" }, { "name": "composer/semver", - "version": "3.2.7", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "deac27056b57e46faf136fae7b449eeaa71661ee" + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/deac27056b57e46faf136fae7b449eeaa71661ee", - "reference": "deac27056b57e46faf136fae7b449eeaa71661ee", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^0.12.54", + "phpstan/phpstan": "^1.4", "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", @@ -192,7 +192,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.2.7" + "source": "https://github.com/composer/semver/tree/3.3.2" }, "funding": [ { @@ -208,24 +208,24 @@ "type": "tidelift" } ], - "time": "2022-01-04T09:57:54+00:00" + "time": "2022-04-01T19:23:25+00:00" }, { "name": "composer/xdebug-handler", - "version": "3.0.1", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "12f1b79476638a5615ed00ea6adbb269cec96fd8" + "reference": "ced299686f41dce890debac69273b47ffe98a40c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/12f1b79476638a5615ed00ea6adbb269cec96fd8", - "reference": "12f1b79476638a5615ed00ea6adbb269cec96fd8", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", "shasum": "" }, "require": { - "composer/pcre": "^1", + "composer/pcre": "^1 || ^2 || ^3", "php": "^7.2.5 || ^8.0", "psr/log": "^1 || ^2 || ^3" }, @@ -258,7 +258,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.1" + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" }, "funding": [ { @@ -274,7 +274,7 @@ "type": "tidelift" } ], - "time": "2022-01-04T18:29:42+00:00" + "time": "2022-02-25T21:32:43+00:00" }, { "name": "doctrine/annotations", @@ -350,16 +350,16 @@ }, { "name": "doctrine/lexer", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c" + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c", - "reference": "9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", "shasum": "" }, "require": { @@ -367,7 +367,7 @@ }, "require-dev": { "doctrine/coding-standard": "^9.0", - "phpstan/phpstan": "1.3", + "phpstan/phpstan": "^1.3", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "vimeo/psalm": "^4.11" }, @@ -406,7 +406,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.2.2" + "source": "https://github.com/doctrine/lexer/tree/1.2.3" }, "funding": [ { @@ -422,25 +422,25 @@ "type": "tidelift" } ], - "time": "2022-01-12T08:27:12+00:00" + "time": "2022-02-28T11:07:21+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.5.0", + "version": "v3.8.0", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "333f15e07c866e33e2765e84ba1e0b88e6a3af3b" + "reference": "cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/333f15e07c866e33e2765e84ba1e0b88e6a3af3b", - "reference": "333f15e07c866e33e2765e84ba1e0b88e6a3af3b", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3", + "reference": "cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3", "shasum": "" }, "require": { "composer/semver": "^3.2", - "composer/xdebug-handler": "^3.0", + "composer/xdebug-handler": "^3.0.3", "doctrine/annotations": "^1.13", "ext-json": "*", "ext-tokenizer": "*", @@ -452,8 +452,8 @@ "symfony/finder": "^5.4 || ^6.0", "symfony/options-resolver": "^5.4 || ^6.0", "symfony/polyfill-mbstring": "^1.23", - "symfony/polyfill-php80": "^1.23", - "symfony/polyfill-php81": "^1.23", + "symfony/polyfill-php80": "^1.25", + "symfony/polyfill-php81": "^1.25", "symfony/process": "^5.4 || ^6.0", "symfony/stopwatch": "^5.4 || ^6.0" }, @@ -503,7 +503,7 @@ "description": "A tool to automatically fix PHP code style", "support": { "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", - "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.5.0" + "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.8.0" }, "funding": [ { @@ -511,7 +511,7 @@ "type": "github" } ], - "time": "2022-01-14T00:29:20+00:00" + "time": "2022-03-18T17:20:59+00:00" }, { "name": "php-cs-fixer/diff", @@ -565,86 +565,22 @@ }, "time": "2020-10-14T08:32:19+00:00" }, - { - "name": "phpstan/phpstan", - "version": "0.12.99", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7", - "shasum": "" - }, - "require": { - "php": "^7.1|^8.0" - }, - "conflict": { - "phpstan/phpstan-shim": "*" - }, - "bin": [ - "phpstan", - "phpstan.phar" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.12-dev" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPStan - PHP Static Analysis Tool", - "support": { - "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/0.12.99" - }, - "funding": [ - { - "url": "https://github.com/ondrejmirtes", - "type": "github" - }, - { - "url": "https://github.com/phpstan", - "type": "github" - }, - { - "url": "https://www.patreon.com/phpstan", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" - } - ], - "time": "2021-09-12T20:09:55+00:00" - }, { "name": "psr/cache", - "version": "1.0.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { @@ -664,7 +600,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for caching libraries", @@ -674,28 +610,33 @@ "psr-6" ], "support": { - "source": "https://github.com/php-fig/cache/tree/master" + "source": "https://github.com/php-fig/cache/tree/3.0.0" }, - "time": "2016-08-06T20:24:11+00:00" + "time": "2021-02-03T23:26:27+00:00" }, { "name": "psr/container", - "version": "1.1.2", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { "php": ">=7.4.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -722,9 +663,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { "name": "psr/event-dispatcher", @@ -782,43 +723,49 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "25a216c5c0426f341fbff7f045fa3946c8041521" + "reference": "c99945852fb9e9de3bc89c90f24b4c8ef47668fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/25a216c5c0426f341fbff7f045fa3946c8041521", - "reference": "25a216c5c0426f341fbff7f045fa3946c8041521", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/c99945852fb9e9de3bc89c90f24b4c8ef47668fd", + "reference": "c99945852fb9e9de3bc89c90f24b4c8ef47668fd", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", - "adodb/adodb-php": "<5.20.12", + "admidio/admidio": "<4.1.9", + "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", "akaunting/akaunting": "<2.1.13", + "alextselegidis/easyappointments": "<1.4.3", "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", "amazing/media2click": ">=1,<1.3.3", "amphp/artax": "<1.0.6|>=2,<2.0.6", "amphp/http": "<1.0.1", "amphp/http-client": ">=4,<4.4", "anchorcms/anchor-cms": "<=0.12.7", + "andreapollastri/cipi": "<=3.1.15", "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6", + "appwrite/server-ce": "<0.11.1|>=0.12,<0.12.2", "area17/twill": "<1.2.5|>=2,<2.5.3", "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", "aws/aws-sdk-php": ">=3,<3.2.1", "bagisto/bagisto": "<0.1.5", "barrelstrength/sprout-base-email": "<1.2.7", "barrelstrength/sprout-forms": "<3.9", + "barryvdh/laravel-translation-manager": "<0.6.2", "baserproject/basercms": "<4.5.4", "billz/raspap-webgui": "<=2.6.6", "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", + "bmarshall511/wordpress_zero_spam": "<5.2.13", "bolt/bolt": "<3.7.2", - "bolt/core": "<4.1.13", + "bolt/core": "<=4.2", "bottelet/flarepoint": "<2.2.1", "brightlocal/phpwhois": "<=4.2.5", "buddypress/buddypress": "<7.2.1", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", - "bytefury/crater": "<6", + "bytefury/crater": "<6.0.2", "cachethq/cachet": "<2.5.1", - "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.5.18|>=3.6,<3.6.15|>=3.7,<3.7.7", + "cakephp/cakephp": "<4.0.6", "cardgate/magento2": "<2.0.33", "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", "cartalyst/sentry": "<=2.1.6", @@ -827,17 +774,22 @@ "cesnet/simplesamlphp-module-proxystatistics": "<3.1", "codeception/codeception": "<3.1.3|>=4,<4.1.22", "codeigniter/framework": "<=3.0.6", - "codeigniter4/framework": "<4.1.6", + "codeigniter4/framework": "<4.1.9", "codiad/codiad": "<=2.8.4", - "composer/composer": "<1.10.23|>=2-alpha.1,<2.1.9", - "concrete5/concrete5": "<8.5.5", + "composer/composer": "<1.10.26|>=2-alpha.1,<2.2.12|>=2.3,<2.3.5", + "concrete5/concrete5": "<9", "concrete5/core": "<8.5.7", "contao-components/mediaelement": ">=2.14.2,<2.21.1", + "contao/contao": ">=4,<4.4.56|>=4.5,<4.9.18|>=4.10,<4.11.7|>=4.13,<4.13.3", "contao/core": ">=2,<3.5.39", - "contao/core-bundle": ">=4,<4.4.56|>=4.5,<4.9.18|>=4.10,<4.11.7|= 4.10.0", + "contao/core-bundle": "<4.9.18|>=4.10,<4.11.7|>=4.13,<4.13.3|= 4.10.0", "contao/listing-bundle": ">=4,<4.4.8", - "craftcms/cms": "<3.7.14", + "contao/managed-edition": "<=1.5", + "craftcms/cms": "<3.7.29", "croogo/croogo": "<3.0.7", + "cuyz/valinor": ">=0.5,<0.7", + "czproject/git-php": "<4.0.3", + "darylldoyle/safe-svg": "<1.9.10", "datadog/dd-trace": ">=0.30,<0.30.2", "david-garcia/phpwhois": "<=4.3.1", "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1", @@ -851,15 +803,16 @@ "doctrine/mongodb-odm": ">=1,<1.0.2", "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "<=14.0.5|>= 3.3.beta1, < 13.0.2", - "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", + "dolibarr/dolibarr": "<16|>= 3.3.beta1, < 13.0.2", + "dompdf/dompdf": "<1.2.1", + "drupal/core": ">=7,<7.88|>=8,<9.2.13|>=9.3,<9.3.6", "drupal/drupal": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", "dweeves/magmi": "<=0.7.24", "ecodev/newsletter": "<=4", + "ectouch/ectouch": "<=2.7.2", "elgg/elgg": "<3.3.24|>=4,<4.0.5", "endroid/qr-code-bundle": "<3.4.2", - "enshrined/svg-sanitize": "<0.13.1", + "enshrined/svg-sanitize": "<0.15", "erusev/parsedown": "<1.7.2", "ether/logs": "<3.0.4", "ezsystems/demobundle": ">=5.4,<5.4.6.1", @@ -867,27 +820,29 @@ "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1", "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1", "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", - "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<=1.5.25", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.27", "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", - "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<=1.3.1", + "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<1.3.17", "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", "ezsystems/ezplatform-richtext": ">=2.3,<=2.3.7", "ezsystems/ezplatform-user": ">=1,<1.0.1", - "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.26", + "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.28", "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1", "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", "ezsystems/repository-forms": ">=2.3,<2.3.2.1", "ezyang/htmlpurifier": "<4.1.1", "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", + "facturascripts/facturascripts": "<2022.6", "feehi/cms": "<=2.1.1", "feehi/feehicms": "<=0.1.3", + "fenom/fenom": "<=2.12.1", "firebase/php-jwt": "<2", "flarum/core": ">=1,<=1.0.1", "flarum/sticky": ">=0.1-beta.14,<=0.1-beta.15", "flarum/tags": "<=0.1-beta.13", "fluidtypo3/vhs": "<5.1.1", "fooman/tcpdf": "<6.2.22", - "forkcms/forkcms": "<=5.9.2", + "forkcms/forkcms": "<5.11.1", "fossar/tcpdf-parser": "<6.2.22", "francoisjacquet/rosariosis": "<8.1.1", "friendsofsymfony/oauth2-php": "<1.3", @@ -895,22 +850,28 @@ "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", "froala/wysiwyg-editor": "<3.2.7", + "froxlor/froxlor": "<=0.10.22", "fuel/core": "<1.8.1", "gaoming13/wechat-php-sdk": "<=1.10.2", - "getgrav/grav": "<=1.7.24", + "genix/cms": "<=1.1.11", + "getgrav/grav": "<1.7.31", "getkirby/cms": "<3.5.8", "getkirby/panel": "<2.5.14", "gilacms/gila": "<=1.11.4", "globalpayments/php-sdk": "<2", + "google/protobuf": "<3.15", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", "gree/jose": "<=2.2", "gregwar/rst": "<1.0.3", "grumpydictator/firefly-iii": "<5.6.5", "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", + "guzzlehttp/psr7": "<1.8.4|>=2,<2.1.1", "helloxz/imgurl": "<=2.31", "hillelcoren/invoice-ninja": "<5.3.35", "hjue/justwriting": "<=1", "hov/jobfair": "<1.0.13|>=2,<2.0.2", + "hyn/multi-tenant": ">=5.6,<5.7.2", + "ibexa/core": ">=4,<4.0.5|>=4.1,<4.1.2", "ibexa/post-install": "<=1.0.4", "icecoder/icecoder": "<=8.1", "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", @@ -918,14 +879,18 @@ "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", - "impresscms/impresscms": "<=1.4.2", + "impresscms/impresscms": "<=1.4.3", "in2code/femanager": "<5.5.1|>=6,<6.3.1", "intelliants/subrion": "<=4.2.1", "ivankristianto/phpwhois": "<=4.3", "jackalope/jackalope-doctrine-dbal": "<1.7.4", "james-heinrich/getid3": "<1.9.21", - "joomla/archive": "<1.1.10", + "joomla/archive": "<1.1.12|>=2,<2.0.1", + "joomla/filesystem": "<1.6.2|>=2,<2.0.1", + "joomla/filter": "<1.4.4|>=2,<2.0.1", + "joomla/input": ">=2,<2.0.2", "joomla/session": "<1.3.1", + "jsdecena/laracom": "<2.0.9", "jsmitty12/phpwhois": "<5.1", "kazist/phpwhois": "<=4.2.6", "kevinpapst/kimai2": "<1.16.7", @@ -933,8 +898,11 @@ "klaviyo/magento2-extension": ">=1,<3", "kreait/firebase-php": ">=3.2,<3.8.1", "la-haute-societe/tcpdf": "<6.2.22", + "laminas/laminas-form": "<2.17.1|>=3,<3.0.2|>=3.1,<3.1.1", "laminas/laminas-http": "<2.14.2", + "laravel/fortify": "<1.11.1", "laravel/framework": "<6.20.42|>=7,<7.30.6|>=8,<8.75", + "laravel/laravel": "<=5.8.38", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", "latte/latte": "<2.10.8", "lavalite/cms": "<=5.8", @@ -942,27 +910,31 @@ "league/commonmark": "<0.18.3", "league/flysystem": "<1.1.4|>=2,<2.1.1", "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", - "librenms/librenms": "<=21.11", + "librenms/librenms": "<22.2.2", "limesurvey/limesurvey": "<3.27.19", "livehelperchat/livehelperchat": "<=3.91", "livewire/livewire": ">2.2.4,<2.2.6", "lms/routes": "<2.1.1", "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", + "luyadev/yii-helpers": "<1.2.1", "magento/community-edition": ">=2,<2.2.10|>=2.3,<2.3.3", "magento/magento1ce": "<1.9.4.3", "magento/magento1ee": ">=1,<1.14.4.3", "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2-p.2", "marcwillmann/turn": "<0.3.3", - "mautic/core": "<4|= 2.13.1", + "matyhtf/framework": "<3.0.6", + "mautic/core": "<4.2|= 2.13.1", "mediawiki/core": ">=1.27,<1.27.6|>=1.29,<1.29.3|>=1.30,<1.30.2|>=1.31,<1.31.9|>=1.32,<1.32.6|>=1.32.99,<1.33.3|>=1.33.99,<1.34.3|>=1.34.99,<1.35", - "microweber/microweber": "<1.2.11", + "microweber/microweber": "<1.3", "miniorange/miniorange-saml": "<1.4.3", "mittwald/typo3_forum": "<1.2.1", - "modx/revolution": "<2.8", + "modx/revolution": "<= 2.8.3-pl|<2.8", "monolog/monolog": ">=1.8,<1.12", - "moodle/moodle": "<3.7.9|>=3.8,<3.8.8|>=3.9,<3.9.5|>=3.10-beta,<3.10.2", + "moodle/moodle": "<3.9.13|>=3.10-beta,<3.10.10|>=3.11,<3.11.6", + "mustache/mustache": ">=2,<2.14.1", "namshi/jose": "<2.2", "neoan3-apps/template": "<1.1.1", + "neorazorx/facturascripts": "<2022.4", "neos/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", "neos/form": ">=1.2,<4.3.3|>=5,<5.0.9|>=5.1,<5.1.3", "neos/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.9.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", @@ -972,15 +944,16 @@ "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", "nilsteampassnet/teampass": "<=2.1.27.36", "nukeviet/nukeviet": "<4.3.4", - "nystudio107/craft-seomatic": "<3.3", + "nystudio107/craft-seomatic": "<3.4.12", "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", "october/backend": "<1.1.2", "october/cms": "= 1.1.1|= 1.0.471|= 1.0.469|>=1.0.319,<1.0.469", "october/october": ">=1.0.319,<1.0.466|>=2.1,<2.1.12", "october/rain": "<1.0.472|>=1.1,<1.1.2", - "october/system": "<1.0.473|>=1.1,<1.1.6|>=2.1,<2.1.12", + "october/system": "<1.0.475|>=1.1,<1.1.11|>=2,<2.1.27", "onelogin/php-saml": "<2.10.4", "oneup/uploader-bundle": "<1.9.3|>=2,<2.1.5", + "open-web-analytics/open-web-analytics": "<1.7.4", "opencart/opencart": "<=3.0.3.2", "openid/php-openid": "<2.3", "openmage/magento-lts": "<19.4.15|>=20,<20.0.13", @@ -994,58 +967,65 @@ "passbolt/passbolt_api": "<2.11", "paypal/merchant-sdk-php": "<3.12", "pear/archive_tar": "<1.4.14", + "pear/crypt_gpg": "<1.6.7", "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", "personnummer/personnummer": "<3.0.2", "phanan/koel": "<5.1.4", "phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7", "phpmailer/phpmailer": "<6.5", "phpmussel/phpmussel": ">=1,<1.6", - "phpmyadmin/phpmyadmin": "<4.9.6|>=5,<5.0.3", - "phpoffice/phpexcel": "<1.8.2", + "phpmyadmin/phpmyadmin": "<5.1.3", + "phpoffice/phpexcel": "<1.8", "phpoffice/phpspreadsheet": "<1.16", "phpseclib/phpseclib": "<2.0.31|>=3,<3.0.7", "phpservermon/phpservermon": "<=3.5.2", - "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", + "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5,<5.6.3", "phpwhois/phpwhois": "<=4.2.5", "phpxmlrpc/extras": "<0.6.1", - "pimcore/pimcore": "<10.2.9", - "pocketmine/pocketmine-mp": "<4.0.7", + "pimcore/data-hub": "<1.2.4", + "pimcore/pimcore": "<10.4", + "pocketmine/bedrock-protocol": "<8.0.2", + "pocketmine/pocketmine-mp": "<4.2.9", "pressbooks/pressbooks": "<5.18", "prestashop/autoupgrade": ">=4,<4.10.1", "prestashop/contactform": ">1.0.1,<4.3", "prestashop/gamification": "<2.3.2", - "prestashop/prestashop": ">=1.7.5,<=1.7.8.1", + "prestashop/prestashop": ">=1.7,<=1.7.8.2", "prestashop/productcomments": ">=4,<4.2.1", "prestashop/ps_emailsubscription": "<2.6.1", "prestashop/ps_facetedsearch": "<3.4.1", "prestashop/ps_linklist": "<3.1", - "privatebin/privatebin": "<1.2.2|>=1.3,<1.3.2", + "privatebin/privatebin": "<1.4", "propel/propel": ">=2-alpha.1,<=2-alpha.7", "propel/propel1": ">=1,<=1.7.1", "pterodactyl/panel": "<1.7", + "ptrofimov/beanstalk_console": "<1.7.14", "pusher/pusher-php-server": "<2.2.1", "pwweb/laravel-core": "<=0.3.6-beta", "rainlab/debugbar-plugin": "<3.1", - "remdex/livehelperchat": "<3.92", + "remdex/livehelperchat": "<3.99", "rmccue/requests": ">=1.6,<1.8", "robrichards/xmlseclibs": "<3.0.4", + "rudloff/alltube": "<3.0.3", + "s-cart/s-cart": "<6.7.2", "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", "scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11", "sensiolabs/connect": "<4.2.3", "serluck/phpwhois": "<=4.2.6", - "shopware/core": "<=6.4.6", - "shopware/platform": "<=6.4.6", + "shopware/core": "<=6.4.9", + "shopware/platform": "<=6.4.9", "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<5.7.7", - "showdoc/showdoc": "<2.10", + "shopware/shopware": "<5.7.9", + "shopware/storefront": "<=6.4.8.1", + "showdoc/showdoc": "<2.10.4", "silverstripe/admin": ">=1,<1.8.1", "silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2", "silverstripe/cms": "<4.3.6|>=4.4,<4.4.4", "silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": "<4.7.4", - "silverstripe/graphql": "<3.5.2|>=4-alpha.1,<4-alpha.2", + "silverstripe/framework": "<4.10.1", + "silverstripe/graphql": "<3.5.2|>=4-alpha.1,<4-alpha.2|= 4.0.0-alpha1", "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4", "silverstripe/subsites": ">=2,<2.1.1", @@ -1058,13 +1038,14 @@ "simplito/elliptic-php": "<1.0.6", "slim/slim": "<2.6", "smarty/smarty": "<3.1.43|>=4,<4.0.3", - "snipe/snipe-it": "<5.3.7", + "snipe/snipe-it": "<5.4.3|>= 6.0.0-RC-1, <= 6.0.0-RC-5", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", "spipu/html2pdf": "<5.2.4", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", - "ssddanbrown/bookstack": "<21.12.1", + "ssddanbrown/bookstack": "<22.2.3", + "statamic/cms": "<3.2.39|>=3.3,<3.3.2", "stormpath/sdk": ">=0,<9.9.99", "studio-42/elfinder": "<2.1.59", "subrion/cms": "<=4.2.1", @@ -1072,10 +1053,10 @@ "swiftmailer/swiftmailer": ">=4,<5.4.5", "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", - "sylius/grid-bundle": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", + "sylius/grid-bundle": "<1.10.1", "sylius/paypal-plugin": ">=1,<1.2.4|>=1.3,<1.3.1", "sylius/resource-bundle": "<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", - "sylius/sylius": "<1.6.9|>=1.7,<1.7.9|>=1.8,<1.8.3|>=1.9,<1.9.5", + "sylius/sylius": "<1.9.10|>=1.10,<1.10.11|>=1.11,<1.11.2", "symbiote/silverstripe-multivaluefield": ">=3,<3.0.99", "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", "symbiote/silverstripe-versionedfiles": "<=2.0.3", @@ -1084,7 +1065,7 @@ "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", - "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<=5.3.14|>=5.4.3,<=5.4.3|>=6.0.3,<=6.0.3|= 6.0.3|= 5.4.3|= 5.3.14", "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", "symfony/http-kernel": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.4.13|>=5,<5.1.5|>=5.2,<5.3.12", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", @@ -1100,9 +1081,9 @@ "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9", "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8|>=5.3,<5.3.2", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.3.2", "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", - "symfony/symfony": ">=2,<3.4.49|>=4,<4.4.35|>=5,<5.3.12", + "symfony/symfony": ">=2,<3.4.49|>=4,<4.4.35|>=5,<5.3.12|>=5.3.14,<=5.3.14|>=5.4.3,<=5.4.3|>=6.0.3,<=6.0.3", "symfony/translation": ">=2,<2.0.17", "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", @@ -1110,7 +1091,9 @@ "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", "t3/dce": ">=2.2,<2.6.2", "t3g/svg-sanitizer": "<1.0.3", + "tastyigniter/tastyigniter": "<3.3", "tecnickcom/tcpdf": "<6.2.22", + "terminal42/contao-tablelookupwizard": "<3.3.5", "thelia/backoffice-default-template": ">=2.1,<2.1.2", "thelia/thelia": ">=2.1-beta.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", @@ -1119,9 +1102,9 @@ "topthink/framework": "<6.0.9", "topthink/think": "<=6.0.9", "topthink/thinkphp": "<=3.2.3", - "tribalsystems/zenario": "<8.8.53370", + "tribalsystems/zenario": "<9.2.55826", "truckersmp/phpwhois": "<=4.3.1", - "twig/twig": "<1.38|>=2,<2.7", + "twig/twig": "<1.38|>=2,<2.14.11|>=3,<3.3.8", "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.38|>=9,<9.5.29|>=10,<10.4.19|>=11,<11.5", "typo3/cms-backend": ">=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", "typo3/cms-core": ">=6.2,<=6.2.56|>=7,<=7.6.52|>=8,<=8.7.41|>=9,<9.5.29|>=10,<10.4.19|>=11,<11.5", @@ -1137,7 +1120,7 @@ "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", "vanilla/safecurl": "<0.9.2", "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4", - "vrana/adminer": "<4.7.9", + "vrana/adminer": "<4.8.1", "wallabag/tcpdf": "<6.2.22", "wanglelecc/laracms": "<=1.0.3", "web-auth/webauthn-framework": ">=3.3,<3.3.4", @@ -1145,6 +1128,9 @@ "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", "wp-cli/wp-cli": "<2.5", + "wpanel/wpanel4-cms": "<=4.3.1", + "wwbn/avideo": "<=11.6", + "yeswiki/yeswiki": "<4.1", "yetiforce/yetiforce-crm": "<=6.3", "yidashi/yii2cmf": "<=2", "yii2mod/yii2-cms": "<1.9.2", @@ -1164,10 +1150,10 @@ "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", "zendframework/zend-developer-tools": ">=1.2.2,<1.2.3", - "zendframework/zend-diactoros": ">=1,<1.8.4", - "zendframework/zend-feed": ">=1,<2.10.3", + "zendframework/zend-diactoros": "<1.8.4", + "zendframework/zend-feed": "<2.10.3", "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-http": ">=1,<2.8.1", + "zendframework/zend-http": "<2.8.1", "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", @@ -1218,50 +1204,46 @@ "type": "tidelift" } ], - "time": "2022-01-22T00:48:41+00:00" + "time": "2022-05-06T13:19:01+00:00" }, { "name": "symfony/console", - "version": "v5.4.2", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a2c6b7ced2eb7799a35375fb9022519282b5405e" + "reference": "0d00aa289215353aa8746a31d101f8e60826285c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a2c6b7ced2eb7799a35375fb9022519282b5405e", - "reference": "a2c6b7ced2eb7799a35375fb9022519282b5405e", + "url": "https://api.github.com/repos/symfony/console/zipball/0d00aa289215353aa8746a31d101f8e60826285c", + "reference": "0d00aa289215353aa8746a31d101f8e60826285c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.0.2", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" + "symfony/string": "^5.4|^6.0" }, "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" }, "suggest": { "psr/log": "For using the console logger", @@ -1301,7 +1283,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.2" + "source": "https://github.com/symfony/console/tree/v6.0.8" }, "funding": [ { @@ -1317,29 +1299,29 @@ "type": "tidelift" } ], - "time": "2021-12-20T16:11:12+00:00" + "time": "2022-04-20T15:01:42+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", - "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.0-dev" }, "thanks": { "name": "symfony/contracts", @@ -1368,7 +1350,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1" }, "funding": [ { @@ -1384,44 +1366,42 @@ "type": "tidelift" } ], - "time": "2021-07-12T14:48:14+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.4.0", + "version": "v6.0.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb" + "reference": "6472ea2dd415e925b90ca82be64b8bc6157f3934" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/27d39ae126352b9fa3be5e196ccf4617897be3eb", - "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/6472ea2dd415e925b90ca82be64b8bc6157f3934", + "reference": "6472ea2dd415e925b90ca82be64b8bc6157f3934", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher-contracts": "^2|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.0.2", + "symfony/event-dispatcher-contracts": "^2|^3" }, "conflict": { - "symfony/dependency-injection": "<4.4" + "symfony/dependency-injection": "<5.4" }, "provide": { "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0" + "symfony/event-dispatcher-implementation": "2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^4.4|^5.0|^6.0" + "symfony/stopwatch": "^5.4|^6.0" }, "suggest": { "symfony/dependency-injection": "", @@ -1453,7 +1433,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.3" }, "funding": [ { @@ -1469,24 +1449,24 @@ "type": "tidelift" } ], - "time": "2021-11-23T10:19:22+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v2.5.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a" + "reference": "7bc61cc2db649b4637d331240c5346dcc7708051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", - "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7bc61cc2db649b4637d331240c5346dcc7708051", + "reference": "7bc61cc2db649b4637d331240c5346dcc7708051", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.0.2", "psr/event-dispatcher": "^1" }, "suggest": { @@ -1495,7 +1475,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.0-dev" }, "thanks": { "name": "symfony/contracts", @@ -1532,7 +1512,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.1" }, "funding": [ { @@ -1548,27 +1528,26 @@ "type": "tidelift" } ], - "time": "2021-07-12T14:48:14+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/filesystem", - "version": "v5.4.0", + "version": "v6.0.7", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "731f917dc31edcffec2c6a777f3698c33bea8f01" + "reference": "6c9e4c41f2c51dfde3db298594ed9cba55dbf5ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/731f917dc31edcffec2c6a777f3698c33bea8f01", - "reference": "731f917dc31edcffec2c6a777f3698c33bea8f01", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/6c9e4c41f2c51dfde3db298594ed9cba55dbf5ff", + "reference": "6c9e4c41f2c51dfde3db298594ed9cba55dbf5ff", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.0.2", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" + "symfony/polyfill-mbstring": "~1.8" }, "type": "library", "autoload": { @@ -1596,7 +1575,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.0" + "source": "https://github.com/symfony/filesystem/tree/v6.0.7" }, "funding": [ { @@ -1612,26 +1591,24 @@ "type": "tidelift" } ], - "time": "2021-10-28T13:39:27+00:00" + "time": "2022-04-01T12:54:51+00:00" }, { "name": "symfony/finder", - "version": "v5.4.2", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "e77046c252be48c48a40816187ed527703c8f76c" + "reference": "af7edab28d17caecd1f40a9219fc646ae751c21f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e77046c252be48c48a40816187ed527703c8f76c", - "reference": "e77046c252be48c48a40816187ed527703c8f76c", + "url": "https://api.github.com/repos/symfony/finder/zipball/af7edab28d17caecd1f40a9219fc646ae751c21f", + "reference": "af7edab28d17caecd1f40a9219fc646ae751c21f", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.0.2" }, "type": "library", "autoload": { @@ -1659,7 +1636,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.2" + "source": "https://github.com/symfony/finder/tree/v6.0.8" }, "funding": [ { @@ -1675,20 +1652,20 @@ "type": "tidelift" } ], - "time": "2021-12-15T11:06:13+00:00" + "time": "2022-04-15T08:07:58+00:00" }, { "name": "symfony/inflector", - "version": "v5.4.0", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/symfony/inflector.git", - "reference": "994f78cae91007021142b1e5df87fb297fe9f8d8" + "reference": "6157dac05bbd287d341b82d67a549fdf468f86d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/inflector/zipball/994f78cae91007021142b1e5df87fb297fe9f8d8", - "reference": "994f78cae91007021142b1e5df87fb297fe9f8d8", + "url": "https://api.github.com/repos/symfony/inflector/zipball/6157dac05bbd287d341b82d67a549fdf468f86d1", + "reference": "6157dac05bbd287d341b82d67a549fdf468f86d1", "shasum": "" }, "require": { @@ -1731,7 +1708,7 @@ "words" ], "support": { - "source": "https://github.com/symfony/inflector/tree/v5.4.0" + "source": "https://github.com/symfony/inflector/tree/v5.4.3" }, "funding": [ { @@ -1748,27 +1725,25 @@ } ], "abandoned": "EnglishInflector from the String component", - "time": "2021-11-23T10:19:22+00:00" + "time": "2022-01-02T09:53:40+00:00" }, { "name": "symfony/options-resolver", - "version": "v5.4.0", + "version": "v6.0.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "b0fb78576487af19c500aaddb269fd36701d4847" + "reference": "51f7006670febe4cbcbae177cbffe93ff833250d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b0fb78576487af19c500aaddb269fd36701d4847", - "reference": "b0fb78576487af19c500aaddb269fd36701d4847", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/51f7006670febe4cbcbae177cbffe93ff833250d", + "reference": "51f7006670febe4cbcbae177cbffe93ff833250d", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php73": "~1.0", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.1|^3" }, "type": "library", "autoload": { @@ -1801,7 +1776,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.4.0" + "source": "https://github.com/symfony/options-resolver/tree/v6.0.3" }, "funding": [ { @@ -1817,20 +1792,20 @@ "type": "tidelift" } ], - "time": "2021-11-23T10:19:22+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/phpunit-bridge", - "version": "v6.0.0", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "5d6cc6720085084f504d2482fc4a2f268784006b" + "reference": "4959a1eedd473bdb3f19db5b1525d5415dfab471" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/5d6cc6720085084f504d2482fc4a2f268784006b", - "reference": "5d6cc6720085084f504d2482fc4a2f268784006b", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/4959a1eedd473bdb3f19db5b1525d5415dfab471", + "reference": "4959a1eedd473bdb3f19db5b1525d5415dfab471", "shasum": "" }, "require": { @@ -1884,7 +1859,7 @@ "description": "Provides utilities for PHPUnit, especially user deprecation notices management", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v6.0.0" + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.0.8" }, "funding": [ { @@ -1900,11 +1875,11 @@ "type": "tidelift" } ], - "time": "2021-11-29T15:32:57+00:00" + "time": "2022-04-12T16:11:42+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -1936,12 +1911,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1966,7 +1941,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" }, "funding": [ { @@ -1986,7 +1961,7 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", @@ -2015,12 +1990,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2047,7 +2022,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0" }, "funding": [ { @@ -2067,7 +2042,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -2096,12 +2071,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -2131,7 +2106,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" }, "funding": [ { @@ -2151,7 +2126,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -2183,12 +2158,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2214,7 +2189,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" }, "funding": [ { @@ -2233,17 +2208,17 @@ "time": "2021-11-30T18:21:41+00:00" }, { - "name": "symfony/polyfill-php73", - "version": "v1.24.0", + "name": "symfony/polyfill-php80", + "version": "v1.25.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5", - "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", "shasum": "" }, "require": { @@ -2260,91 +2235,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, "files": [ "bootstrap.php" ], - "classmap": [ - "Resources/stubs" - ] - }, - "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": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.24.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": "2021-06-05T21:20:04+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.24.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9", - "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { "psr-4": { "Symfony\\Polyfill\\Php80\\": "" }, - "files": [ - "bootstrap.php" - ], "classmap": [ "Resources/stubs" ] @@ -2376,7 +2272,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" }, "funding": [ { @@ -2392,11 +2288,11 @@ "type": "tidelift" } ], - "time": "2021-09-13T13:58:33+00:00" + "time": "2022-03-04T08:16:47+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -2422,12 +2318,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -2455,7 +2351,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0" }, "funding": [ { @@ -2475,21 +2371,20 @@ }, { "name": "symfony/process", - "version": "v5.4.2", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "2b3ba8722c4aaf3e88011be5e7f48710088fb5e4" + "reference": "d074154ea8b1443a96391f6e39f9e547b2dd01b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/2b3ba8722c4aaf3e88011be5e7f48710088fb5e4", - "reference": "2b3ba8722c4aaf3e88011be5e7f48710088fb5e4", + "url": "https://api.github.com/repos/symfony/process/zipball/d074154ea8b1443a96391f6e39f9e547b2dd01b9", + "reference": "d074154ea8b1443a96391f6e39f9e547b2dd01b9", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.0.2" }, "type": "library", "autoload": { @@ -2517,7 +2412,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.2" + "source": "https://github.com/symfony/process/tree/v6.0.8" }, "funding": [ { @@ -2533,40 +2428,38 @@ "type": "tidelift" } ], - "time": "2021-12-27T21:01:00+00:00" + "time": "2022-04-12T16:11:42+00:00" }, { "name": "symfony/property-info", - "version": "v5.4.2", + "version": "v6.0.7", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "a32f813896ffb3b4710fca5af5b05bef600cf4f0" + "reference": "0f26f0870f05d65d5c06681ecbf36e546204f4b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/a32f813896ffb3b4710fca5af5b05bef600cf4f0", - "reference": "a32f813896ffb3b4710fca5af5b05bef600cf4f0", + "url": "https://api.github.com/repos/symfony/property-info/zipball/0f26f0870f05d65d5c06681ecbf36e546204f4b5", + "reference": "0f26f0870f05d65d5c06681ecbf36e546204f4b5", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16", - "symfony/string": "^5.1|^6.0" + "php": ">=8.0.2", + "symfony/string": "^5.4|^6.0" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/reflection-docblock": "<5.2", "phpdocumentor/type-resolver": "<1.4.0", - "symfony/dependency-injection": "<4.4" + "symfony/dependency-injection": "<5.4" }, "require-dev": { "doctrine/annotations": "^1.10.4", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "phpdocumentor/reflection-docblock": "^5.2", "phpstan/phpdoc-parser": "^1.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/serializer": "^4.4|^5.0|^6.0" + "symfony/cache": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0" }, "suggest": { "phpdocumentor/reflection-docblock": "To use the PHPDoc", @@ -2608,7 +2501,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v5.4.2" + "source": "https://github.com/symfony/property-info/tree/v6.0.7" }, "funding": [ { @@ -2624,26 +2517,25 @@ "type": "tidelift" } ], - "time": "2021-12-26T13:30:54+00:00" + "time": "2022-03-31T17:18:25+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.5.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc" + "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", - "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e517458f278c2131ca9f262f8fbaf01410f2c65c", + "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1" + "php": ">=8.0.2", + "psr/container": "^2.0" }, "conflict": { "ext-psr": "<1.1|>=2" @@ -2654,7 +2546,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.0-dev" }, "thanks": { "name": "symfony/contracts", @@ -2691,7 +2583,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.0.1" }, "funding": [ { @@ -2707,24 +2599,24 @@ "type": "tidelift" } ], - "time": "2021-11-04T16:48:04+00:00" + "time": "2022-03-13T20:10:05+00:00" }, { "name": "symfony/stopwatch", - "version": "v5.4.0", + "version": "v6.0.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "208ef96122bfed82a8f3a61458a07113a08bdcfe" + "reference": "f2c1780607ec6502f2121d9729fd8150a655d337" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/208ef96122bfed82a8f3a61458a07113a08bdcfe", - "reference": "208ef96122bfed82a8f3a61458a07113a08bdcfe", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f2c1780607ec6502f2121d9729fd8150a655d337", + "reference": "f2c1780607ec6502f2121d9729fd8150a655d337", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.0.2", "symfony/service-contracts": "^1|^2|^3" }, "type": "library", @@ -2753,7 +2645,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v5.4.0" + "source": "https://github.com/symfony/stopwatch/tree/v6.0.5" }, "funding": [ { @@ -2769,47 +2661,46 @@ "type": "tidelift" } ], - "time": "2021-11-23T10:19:22+00:00" + "time": "2022-02-21T17:15:17+00:00" }, { "name": "symfony/string", - "version": "v5.4.2", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d" + "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d", - "reference": "e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d", + "url": "https://api.github.com/repos/symfony/string/zipball/ac0aa5c2282e0de624c175b68d13f2c8f2e2649d", + "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.0.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": ">=3.0" + "symfony/translation-contracts": "<2.0" }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" }, "type": "library", "autoload": { - "psr-4": { - "Symfony\\Component\\String\\": "" - }, "files": [ "Resources/functions.php" ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, "exclude-from-classmap": [ "/Tests/" ] @@ -2839,7 +2730,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.2" + "source": "https://github.com/symfony/string/tree/v6.0.8" }, "funding": [ { @@ -2855,36 +2746,35 @@ "type": "tidelift" } ], - "time": "2021-12-16T21:52:00+00:00" + "time": "2022-04-22T08:18:02+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.4.2", + "version": "v6.0.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "1b56c32c3679002b3a42384a580e16e2600f41c1" + "reference": "fa61dfb4bd3068df2492013dc65f3190e9f550c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/1b56c32c3679002b3a42384a580e16e2600f41c1", - "reference": "1b56c32c3679002b3a42384a580e16e2600f41c1", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/fa61dfb4bd3068df2492013dc65f3190e9f550c0", + "reference": "fa61dfb4bd3068df2492013dc65f3190e9f550c0", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "phpunit/phpunit": "<5.4.3", - "symfony/console": "<4.4" + "symfony/console": "<5.4" }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", "twig/twig": "^2.13|^3.0.4" }, "suggest": { @@ -2928,7 +2818,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.2" + "source": "https://github.com/symfony/var-dumper/tree/v6.0.8" }, "funding": [ { @@ -2944,7 +2834,7 @@ "type": "tidelift" } ], - "time": "2021-12-29T10:10:35+00:00" + "time": "2022-04-26T13:22:23+00:00" } ], "aliases": [], diff --git a/phpstan.neon b/phpstan.neon index 7fab944..fc1d4be 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,4 +5,6 @@ parameters: paths: - %currentWorkingDirectory%/src ignoreErrors: - #- '#(.*) expects class-string\\|T of object, (.*) given$#' \ No newline at end of file + - '#Method Ang3\\Component\\Odoo\\Client\:\:findBy\(\) should return array but returns array\.#' + - '#Cannot cast mixed to int\.#' + - '#Cannot cast mixed to string\.#' \ No newline at end of file diff --git a/src/Client.php b/src/Client.php index 9fcb28d..86f9d0d 100644 --- a/src/Client.php +++ b/src/Client.php @@ -12,6 +12,9 @@ use InvalidArgumentException; use Psr\Log\LoggerInterface; +/** + * @author Joanis ROUANET + */ class Client { /** @@ -98,7 +101,8 @@ public function insert(string $modelName, array $data): int throw new InvalidArgumentException('Data cannot be empty'); } - $result = $this->execute($modelName, self::CREATE, [[$data]]); + /** @var int[] $result */ + $result = (array) $this->execute($modelName, self::CREATE, [[$data]]); return (int) array_shift($result); } @@ -190,7 +194,10 @@ public function search(string $modelName, iterable $criteria = null, array $opti unset($options['fields']); } - return (array) $this->execute($modelName, self::SEARCH, [$this->expressionBuilder->normalizeDomains($criteria)], $options); + /** @var int[] $result */ + $result = (array) $this->execute($modelName, self::SEARCH, [$this->expressionBuilder->normalizeDomains($criteria)], $options); + + return $result; } /** @@ -236,7 +243,7 @@ public function findAll(string $modelName, array $options = []): array * @throws InvalidArgumentException when $criteria value is not valid * @throws RequestException when request failed * - * @return array + * @return array[] */ public function findBy(string $modelName, iterable $criteria = null, array $options = []): array { @@ -274,13 +281,11 @@ public function countAll(string $modelName): int */ public function count(string $modelName, iterable $criteria = null): int { - return $this->execute($modelName, self::SEARCH_COUNT, [$this->expressionBuilder->normalizeDomains($criteria)]); + return (int) $this->execute($modelName, self::SEARCH_COUNT, [$this->expressionBuilder->normalizeDomains($criteria)]); } /** * List model fields. - * - * @deprecated since version 7.0 and will be removed in 8.0, use the record manager schema instead. */ public function listFields(string $modelName, array $options = []): array { @@ -307,7 +312,7 @@ public function execute(string $name, string $method, array $parameters = [], ar public function version(): array { - return $this->request(self::SERVICE_COMMON, 'version'); + return (array) $this->request(self::SERVICE_COMMON, 'version'); } /** @@ -316,7 +321,7 @@ public function version(): array public function authenticate(): int { if (null === $this->uid) { - $this->uid = $this->request( + $this->uid = (int) $this->request( self::SERVICE_COMMON, 'login', $this->connection->getDatabase(), @@ -324,7 +329,7 @@ public function authenticate(): int $this->connection->getPassword() ); - if (!$this->uid || !is_int($this->uid)) { + if (!$this->uid) { throw new AuthenticationException(); } } diff --git a/src/Connection.php b/src/Connection.php index 4a9c381..9a6982d 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -4,6 +4,9 @@ use Ang3\Component\Odoo\Exception\MissingConfigParameterException; +/** + * @author Joanis ROUANET + */ class Connection { /** diff --git a/src/Exception/AuthenticationException.php b/src/Exception/AuthenticationException.php index 6727bd0..4836298 100644 --- a/src/Exception/AuthenticationException.php +++ b/src/Exception/AuthenticationException.php @@ -2,6 +2,9 @@ namespace Ang3\Component\Odoo\Exception; +/** + * @author Joanis ROUANET + */ class AuthenticationException extends RequestException { public function __construct(\Throwable $previous = null) diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index 6a56a89..6e9a137 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -2,6 +2,9 @@ namespace Ang3\Component\Odoo\Exception; +/** + * @author Joanis ROUANET + */ interface ExceptionInterface extends \Throwable { } diff --git a/src/Exception/MissingConfigParameterException.php b/src/Exception/MissingConfigParameterException.php index 3ca66d5..e1a0093 100644 --- a/src/Exception/MissingConfigParameterException.php +++ b/src/Exception/MissingConfigParameterException.php @@ -2,6 +2,9 @@ namespace Ang3\Component\Odoo\Exception; +/** + * @author Joanis ROUANET + */ class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface { } diff --git a/src/Exception/RemoteException.php b/src/Exception/RemoteException.php index 1612664..fff9b42 100644 --- a/src/Exception/RemoteException.php +++ b/src/Exception/RemoteException.php @@ -2,6 +2,9 @@ namespace Ang3\Component\Odoo\Exception; +/** + * @author Joanis ROUANET + */ class RemoteException extends RequestException { /** diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 088bc68..d59dc4f 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -2,6 +2,9 @@ namespace Ang3\Component\Odoo\Exception; +/** + * @author Joanis ROUANET + */ class RequestException extends \RuntimeException implements ExceptionInterface { } diff --git a/src/Expression/Domain/Comparison.php b/src/Expression/Domain/Comparison.php index 7449e9a..6b068a4 100644 --- a/src/Expression/Domain/Comparison.php +++ b/src/Expression/Domain/Comparison.php @@ -4,6 +4,9 @@ use ArrayIterator; +/** + * @author Joanis ROUANET + */ class Comparison implements DomainInterface { /** diff --git a/src/Expression/Domain/CompositeDomain.php b/src/Expression/Domain/CompositeDomain.php index 91e65d9..6ddc1dd 100644 --- a/src/Expression/Domain/CompositeDomain.php +++ b/src/Expression/Domain/CompositeDomain.php @@ -4,6 +4,9 @@ use Generator; +/** + * @author Joanis ROUANET + */ class CompositeDomain implements DomainInterface { /** diff --git a/src/Expression/Domain/CustomDomain.php b/src/Expression/Domain/CustomDomain.php index 5198d95..42c7d88 100644 --- a/src/Expression/Domain/CustomDomain.php +++ b/src/Expression/Domain/CustomDomain.php @@ -4,6 +4,9 @@ use ArrayIterator; +/** + * @author Joanis ROUANET + */ class CustomDomain implements DomainInterface { /** diff --git a/src/Expression/Domain/DomainInterface.php b/src/Expression/Domain/DomainInterface.php index 7bd3d90..7b28379 100644 --- a/src/Expression/Domain/DomainInterface.php +++ b/src/Expression/Domain/DomainInterface.php @@ -4,6 +4,9 @@ use IteratorAggregate; +/** + * @author Joanis ROUANET + */ interface DomainInterface extends IteratorAggregate { public function toArray(): array; diff --git a/src/Expression/Exception/ConversionException.php b/src/Expression/Exception/ConversionException.php index 9e66386..42deecc 100644 --- a/src/Expression/Exception/ConversionException.php +++ b/src/Expression/Exception/ConversionException.php @@ -2,6 +2,9 @@ namespace Ang3\Component\Odoo\Expression\Exception; +/** + * @author Joanis ROUANET + */ class ConversionException extends \RuntimeException { } diff --git a/src/Expression/ExpressionBuilder.php b/src/Expression/ExpressionBuilder.php index 05be2f4..e3cbd3f 100644 --- a/src/Expression/ExpressionBuilder.php +++ b/src/Expression/ExpressionBuilder.php @@ -14,6 +14,9 @@ use Exception; use InvalidArgumentException; +/** + * @author Joanis ROUANET + */ class ExpressionBuilder { /** @@ -273,6 +276,7 @@ public function normalizeDomains(iterable $criteria = null): array throw new InvalidArgumentException(sprintf('Expected parameter #1 of type %s|array<%s|array>, %s given', DomainInterface::class, DomainInterface::class, gettype($criteria))); } + /** @var array $criteriaArray */ $criteriaArray = $this->formatValue($criteria->toArray()); if (!$criteriaArray) { @@ -287,7 +291,7 @@ public function normalizeDomains(iterable $criteria = null): array */ public function normalizeData(array $data = []): array { - return $this->formatValue($data); + return (array) $this->formatValue($data); } /** @@ -335,10 +339,6 @@ private function formatValue($value) } } - try { - return (string) $value; - } catch (Exception $e) { - throw new ConversionException(sprintf('Failed to convert value of type "%s" to string.', gettype($value)), 0, $e); - } + return (string) $value; } } diff --git a/src/Expression/Operation/CollectionOperation.php b/src/Expression/Operation/CollectionOperation.php index 598cde0..44904e3 100644 --- a/src/Expression/Operation/CollectionOperation.php +++ b/src/Expression/Operation/CollectionOperation.php @@ -4,6 +4,9 @@ use InvalidArgumentException; +/** + * @author Joanis ROUANET + */ class CollectionOperation implements OperationInterface { /** diff --git a/src/Expression/Operation/OperationInterface.php b/src/Expression/Operation/OperationInterface.php index ec0432e..a3bd525 100644 --- a/src/Expression/Operation/OperationInterface.php +++ b/src/Expression/Operation/OperationInterface.php @@ -2,6 +2,9 @@ namespace Ang3\Component\Odoo\Expression\Operation; +/** + * @author Joanis ROUANET + */ interface OperationInterface { public function toArray(): array; diff --git a/src/Transport/AbstractRpcTransport.php b/src/Transport/AbstractRpcTransport.php index 2f47778..6424c48 100644 --- a/src/Transport/AbstractRpcTransport.php +++ b/src/Transport/AbstractRpcTransport.php @@ -4,6 +4,9 @@ use Ang3\Component\Odoo\Exception\RemoteException; +/** + * @author Joanis ROUANET + */ abstract class AbstractRpcTransport implements TransportInterface { public function normalizeRpcData(string $service, string $method, array $arguments = []): array diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index 1d9d0d7..2ff8a4f 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -21,9 +21,15 @@ class JsonRpcPhpStreamTransport extends AbstractRpcTransport */ private $connection; - public function __construct(Connection $connection) + /** + * @var int + */ + private $timeOut; + + public function __construct(Connection $connection, int $timeOut = TransportInterface::DEFAULT_TIMEOUT) { $this->connection = $connection; + $this->timeOut = $timeOut; } public function request(string $service, string $method, array $arguments = []): array @@ -37,7 +43,7 @@ public function request(string $service, string $method, array $arguments = []): $context = stream_context_create([ 'http' => [ 'method' => 'POST', - 'timeout' => 120, + 'timeout' => $this->timeOut, 'header' => 'Content-Type: application/json', 'content' => $payload, ], @@ -56,6 +62,6 @@ public function request(string $service, string $method, array $arguments = []): throw new RequestException(sprintf('Failed to decode JSON data: %s', json_last_error_msg())); } - return $data; + return (array) $data; } } diff --git a/src/Transport/TransportInterface.php b/src/Transport/TransportInterface.php index 814c61e..9f16788 100644 --- a/src/Transport/TransportInterface.php +++ b/src/Transport/TransportInterface.php @@ -4,8 +4,13 @@ use Ang3\Component\Odoo\Exception\RequestException; +/** + * @author Joanis ROUANET + */ interface TransportInterface { + public const DEFAULT_TIMEOUT = 120; + /** * Make a request to Odoo database. * From a54fd387ac6d91ed721495469b7f7b7f2c4f4e99 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 22 May 2022 02:45:32 +0200 Subject: [PATCH 29/80] Updated client logging --- src/Client.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Client.php b/src/Client.php index 86f9d0d..298a4ed 100644 --- a/src/Client.php +++ b/src/Client.php @@ -346,15 +346,22 @@ public function request(string $service, string $method, ...$arguments) { $context['request_id'] = uniqid('rpc', true); if ($this->logger) { - $this->logger->info('JSON RPC request #{request_id} - {service}::{method} (uid: #{uid})', $context); + $this->logger->info('JSON RPC request #{request_id} started - {service}::{method} (uid: #{uid})', $context); } + $runtime = microtime(true); $payload = $this->transport->request($service, $method, $arguments); + $runtime = microtime(true) - $runtime; if ($this->logger) { - $loggedResult = json_encode($payload); - $this->logger->debug(sprintf('Request result: %s', $loggedResult), [ + $this->logger->info('JSON RPC request #{request_id} finished - Runtime: %ss.', [ 'request_id' => $context['request_id'], + 'runtime' => number_format($runtime * 1E3, 3, '.', ' '), + ]); + + $this->logger->debug('JSON RPC payload debug.', [ + 'request_id' => $context['request_id'], + 'payload' => $payload, ]); } From 3ad8d997356855ee43c29040b0af86230bc54ab1 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 22 May 2022 02:50:18 +0200 Subject: [PATCH 30/80] Fixed client logging --- src/Client.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Client.php b/src/Client.php index 298a4ed..42e6cf0 100644 --- a/src/Client.php +++ b/src/Client.php @@ -344,9 +344,16 @@ public function authenticate(): int */ public function request(string $service, string $method, ...$arguments) { - $context['request_id'] = uniqid('rpc', true); + $context = [ + 'service' => $service, + 'method' => $method, + 'uid' => (int) $this->uid, + 'arguments' => $arguments, + 'request_id' => uniqid('rpc', true), + ]; + if ($this->logger) { - $this->logger->info('JSON RPC request #{request_id} started - {service}::{method} (uid: #{uid})', $context); + $this->logger->info('JSON RPC request #{request_id} started - {service}::{method}({arguments}) (uid: #{uid})', $context); } $runtime = microtime(true); From 3a259f43a8f27916a7da4fcf41822446175f57c0 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 22 May 2022 03:01:45 +0200 Subject: [PATCH 31/80] Update Client.php --- src/Client.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Client.php b/src/Client.php index 42e6cf0..7ce63c9 100644 --- a/src/Client.php +++ b/src/Client.php @@ -348,7 +348,7 @@ public function request(string $service, string $method, ...$arguments) 'service' => $service, 'method' => $method, 'uid' => (int) $this->uid, - 'arguments' => $arguments, + 'arguments' => array_slice($arguments, 3), 'request_id' => uniqid('rpc', true), ]; @@ -361,7 +361,7 @@ public function request(string $service, string $method, ...$arguments) $runtime = microtime(true) - $runtime; if ($this->logger) { - $this->logger->info('JSON RPC request #{request_id} finished - Runtime: %ss.', [ + $this->logger->info('JSON RPC request #{request_id} finished - Runtime: {runtime}s.', [ 'request_id' => $context['request_id'], 'runtime' => number_format($runtime * 1E3, 3, '.', ' '), ]); From 00fe44508db48db15717507e6d5c1438f74946d4 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 22 May 2022 03:06:07 +0200 Subject: [PATCH 32/80] Update Client.php --- src/Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Client.php b/src/Client.php index 7ce63c9..8c654a8 100644 --- a/src/Client.php +++ b/src/Client.php @@ -363,7 +363,7 @@ public function request(string $service, string $method, ...$arguments) if ($this->logger) { $this->logger->info('JSON RPC request #{request_id} finished - Runtime: {runtime}s.', [ 'request_id' => $context['request_id'], - 'runtime' => number_format($runtime * 1E3, 3, '.', ' '), + 'runtime' => number_format($runtime, 3, '.', ' '), ]); $this->logger->debug('JSON RPC payload debug.', [ From 4a4cadc931967ba02cb2a02b6c366c69744cf559 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Thu, 2 Feb 2023 13:57:57 +0100 Subject: [PATCH 33/80] Code review --- .github/workflows/php_lint.yml | 14 + .github/workflows/phpunit.yml | 32 + .gitignore | 1 + .php-cs-fixer.dist.php | 60 + .php-cs-fixer.php-highest.php | 20 + .travis.yml | 13 - CHANGELOG.md | 7 + composer.json | 10 +- composer.lock | 2835 +++++++---------- docs/index.md | 12 +- phpstan.neon | 2 +- src/Client.php | 146 +- src/Connection.php | 32 +- src/Exception/AuthenticationException.php | 9 + src/Exception/ExceptionInterface.php | 9 + .../MissingConfigParameterException.php | 9 + src/Exception/RemoteException.php | 14 +- src/Exception/RequestException.php | 9 + src/Expression/Domain/Comparison.php | 52 +- src/Expression/Domain/CompositeDomain.php | 46 +- src/Expression/Domain/CustomDomain.php | 20 +- src/Expression/Domain/DomainInterface.php | 13 +- .../Exception/ConversionException.php | 9 + src/Expression/ExpressionBuilder.php | 103 +- .../Operation/CollectionOperation.php | 52 +- .../Operation/OperationInterface.php | 9 + src/Transport/AbstractRpcTransport.php | 16 +- src/Transport/JsonRpcPhpStreamTransport.php | 26 +- src/Transport/TransportInterface.php | 9 + tests/AbstractTest.php | 15 +- tests/Expression/AbstractDomainTest.php | 12 +- tests/Expression/ComparisonTest.php | 24 +- tests/Expression/CompositeDomainTest.php | 28 +- tests/Utils/Debugger.php | 11 +- tests/Utils/ObjectTester.php | 71 +- tests/Utils/Reflector.php | 40 +- tests/Utils/TestDecorator.php | 12 +- 37 files changed, 1760 insertions(+), 2042 deletions(-) create mode 100644 .github/workflows/php_lint.yml create mode 100644 .github/workflows/phpunit.yml create mode 100644 .php-cs-fixer.dist.php create mode 100644 .php-cs-fixer.php-highest.php delete mode 100644 .travis.yml diff --git a/.github/workflows/php_lint.yml b/.github/workflows/php_lint.yml new file mode 100644 index 0000000..8e614e9 --- /dev/null +++ b/.github/workflows/php_lint.yml @@ -0,0 +1,14 @@ +name: PHP Linting + +on: [push, pull_request] + +jobs: + php-cs-fixer: + name: PHP-CS-Fixer + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: PHP-CS-Fixer + uses: docker://oskarstark/php-cs-fixer-ga + with: + args: src --diff --dry-run --config=.php-cs-fixer.php-highest.php \ No newline at end of file diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml new file mode 100644 index 0000000..d6ce694 --- /dev/null +++ b/.github/workflows/phpunit.yml @@ -0,0 +1,32 @@ +name: PHPUnit tests + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + tests_suite: + strategy: + matrix: + php_version: [ '8.1', '8.2' ] + runs-on: ubuntu-latest + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php_version }} + extensions: json + - uses: actions/checkout@v3 + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + - name: Install Dependencies + run: | + composer install + - name: Execute PHPUnit tests + run: vendor/bin/phpunit diff --git a/.gitignore b/.gitignore index aaeb0f9..b698aba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /vendor/ /.idea/ .php_cs.cache +.php_cs-fixer.cache .php-cs-fixer.cache .phpunit.result.cache test.php \ No newline at end of file diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..005f762 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,60 @@ +ignoreDotFiles(false) + ->ignoreVCSIgnored(true) + ->exclude('tests/Fixtures') + ->in(__DIR__) + ->append([ + __DIR__.'/dev-tools/doc.php', + // __DIR__.'/php-cs-fixer', disabled, as we want to be able to run bootstrap file even on lower PHP version, to show nice message + ]) +; + +$config = new PhpCsFixer\Config(); +$config + ->setRiskyAllowed(true) + ->setRules([ + '@PHP74Migration' => true, + '@PHP74Migration:risky' => true, + '@PHPUnit100Migration:risky' => true, + '@PhpCsFixer' => true, + '@PhpCsFixer:risky' => true, + '@Symfony' => true, + 'general_phpdoc_annotation_remove' => ['annotations' => ['expectedDeprecation']], // one should use PHPUnit built-in method instead + 'header_comment' => ['header' => $header], + 'heredoc_indentation' => false, // TODO switch on when # of PR's is lower + 'modernize_strpos' => true, // needs PHP 8+ or polyfill + 'no_useless_concat_operator' => false, // TODO switch back on when the `src/Console/Application.php` no longer needs the concat + 'use_arrow_functions' => false, // TODO switch on when # of PR's is lower + ]) + ->setFinder($finder) +; + +// special handling of fabbot.io service if it's using too old PHP CS Fixer version +if (false !== getenv('FABBOT_IO')) { + try { + PhpCsFixer\FixerFactory::create() + ->registerBuiltInFixers() + ->registerCustomFixers($config->getCustomFixers()) + ->useRuleSet(new PhpCsFixer\RuleSet($config->getRules())) + ; + } catch (PhpCsFixer\ConfigurationException\InvalidConfigurationException $e) { + $config->setRules([]); + } catch (UnexpectedValueException $e) { + $config->setRules([]); + } catch (InvalidArgumentException $e) { + $config->setRules([]); + } +} + +return $config; diff --git a/.php-cs-fixer.php-highest.php b/.php-cs-fixer.php-highest.php new file mode 100644 index 0000000..db24d09 --- /dev/null +++ b/.php-cs-fixer.php-highest.php @@ -0,0 +1,20 @@ += 80200) { + fwrite(STDERR, "PHP CS Fixer's config for PHP-HIGHEST can be executed only on highest supported PHP version - 8.1.*.\n"); + fwrite(STDERR, "Running it on lower PHP version would prevent calling migration rules.\n"); + + exit(1); +} + +$config = require __DIR__.'/.php-cs-fixer.dist.php'; + +$config->setRules(array_merge($config->getRules(), [ + '@PHP81Migration' => true, + '@PHP80Migration:risky' => true, + 'heredoc_indentation' => false, +])); + +return $config; diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0803c40..0000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: php - -php: - - 7.2 - - 7.3 - - 7.4 - -before_install: - - composer self-update - - composer install - -install: - - php -d memory_limit=-1 $(phpenv which composer) install --no-suggest --prefer-dist \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b58e22..5baacb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ CHANGELOG ========= +8.0 +--- + +* Support for PHP >= 8.1 only - The PHP extension `php-xmlrpc` is not used anymore. +* Transport layer - XMLRPC replaced by JSONRPC. +* Code review + 7.0 --- diff --git a/composer.json b/composer.json index f38082f..b447901 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,6 @@ "authors": [ { "name": "Joanis ROUANET", - "email": "joanis.ang3@gmail.com", "homepage": "https://github.com/Ang3" } ], @@ -18,16 +17,11 @@ } }, "require": { - "php": ">=7.2", + "php": ">=8.1", "ext-json": "*", "psr/log": "^1.1||^2.0||3.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "roave/security-advisories": "dev-master", - "symfony/var-dumper": ">=3.4", - "symfony/phpunit-bridge": ">=3.4", - "symfony/property-info": ">=3.4", - "symfony/inflector": ">=3.4" + "symfony/test-pack": "^1.0" } } diff --git a/composer.lock b/composer.lock index 16d0495..2202d9c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3ad65fbdfbc68c74d97b4e5b6cfad284", + "content-hash": "e246e4552146e2ba330c1da7be51a1a5", "packages": [ { "name": "psr/log", @@ -59,36 +59,36 @@ ], "packages-dev": [ { - "name": "composer/pcre", - "version": "3.0.0", + "name": "doctrine/instantiator", + "version": "2.0.0", "source": { "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd" + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/e300eb6c535192decd27a85bc72a9290f0d6b3bd", - "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0" + "php": "^8.1" }, "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, "autoload": { "psr-4": { - "Composer\\Pcre\\": "src" + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", @@ -97,68 +97,69 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" } ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" + "constructor", + "instantiate" ], "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.0.0" + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { - "url": "https://packagist.com", + "url": "https://www.doctrine-project.org/sponsorship.html", "type": "custom" }, { - "url": "https://github.com/composer", - "type": "github" + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" }, { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", "type": "tidelift" } ], - "time": "2022-02-25T20:21:48+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { - "name": "composer/semver", - "version": "3.3.2", + "name": "masterminds/html5", + "version": "2.7.6", "source": { "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "897eb517a343a2281f11bc5556d6548db7d93947" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/897eb517a343a2281f11bc5556d6548db7d93947", + "reference": "897eb517a343a2281f11bc5556d6548db7d93947", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" + "ext-ctype": "*", + "ext-dom": "*", + "ext-libxml": "*", + "php": ">=5.3.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.x-dev" + "dev-master": "2.7-dev" } }, "autoload": { "psr-4": { - "Composer\\Semver\\": "src" + "Masterminds\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -167,374 +168,303 @@ ], "authors": [ { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" + "name": "Matt Butcher", + "email": "technosophos@gmail.com" }, { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "Matt Farina", + "email": "matt@mattfarina.com" }, { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" } ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", "keywords": [ - "semantic", - "semver", - "validation", - "versioning" + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" ], "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.2" + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.7.6" }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-04-01T19:23:25+00:00" + "time": "2022-08-18T16:18:26+00:00" }, { - "name": "composer/xdebug-handler", - "version": "3.0.3", + "name": "myclabs/deep-copy", + "version": "1.11.0", "source": { "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "ced299686f41dce890debac69273b47ffe98a40c" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", - "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", "shasum": "" }, "require": { - "composer/pcre": "^1 || ^2 || ^3", - "php": "^7.2.5 || ^8.0", - "psr/log": "^1 || ^2 || ^3" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" }, "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^6.0" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], "psr-4": { - "Composer\\XdebugHandler\\": "src" + "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", + "description": "Create deep copies (clones) of your objects", "keywords": [ - "Xdebug", - "performance" + "clone", + "copy", + "duplicate", + "object", + "object graph" ], "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" }, "funding": [ { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", "type": "tidelift" } ], - "time": "2022-02-25T21:32:43+00:00" + "time": "2022-03-03T13:19:32+00:00" }, { - "name": "doctrine/annotations", - "version": "1.13.2", + "name": "nikic/php-parser", + "version": "v4.15.3", "source": { "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "5b668aef16090008790395c02c893b1ba13f7e08" + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/5b668aef16090008790395c02c893b1ba13f7e08", - "reference": "5b668aef16090008790395c02c893b1ba13f7e08", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", "shasum": "" }, "require": { - "doctrine/lexer": "1.*", "ext-tokenizer": "*", - "php": "^7.1 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" + "php": ">=7.0" }, "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^6.0 || ^8.1", - "phpstan/phpstan": "^0.12.20", - "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", - "symfony/cache": "^4.4 || ^5.2" + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, + "bin": [ + "bin/php-parse" + ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, "autoload": { "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + "PhpParser\\": "lib/PhpParser" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Nikita Popov" } ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "description": "A PHP parser written in PHP", "keywords": [ - "annotations", - "docblock", - "parser" + "parser", + "php" ], "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.13.2" + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" }, - "time": "2021-08-05T19:00:23+00:00" + "time": "2023-01-16T22:05:37+00:00" }, { - "name": "doctrine/lexer", - "version": "1.2.3", + "name": "phar-io/manifest", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9.0", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.11" + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" }, { - "name": "Roman Borschel", - "email": "roman@code-factory.org" + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" }, { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.2.3" + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" - } - ], - "time": "2022-02-28T11:07:21+00:00" + "time": "2021-07-20T11:28:43+00:00" }, { - "name": "friendsofphp/php-cs-fixer", - "version": "v3.8.0", + "name": "phar-io/version", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3" + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3", - "reference": "cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "composer/semver": "^3.2", - "composer/xdebug-handler": "^3.0.3", - "doctrine/annotations": "^1.13", - "ext-json": "*", - "ext-tokenizer": "*", - "php": "^7.4 || ^8.0", - "php-cs-fixer/diff": "^2.0", - "symfony/console": "^5.4 || ^6.0", - "symfony/event-dispatcher": "^5.4 || ^6.0", - "symfony/filesystem": "^5.4 || ^6.0", - "symfony/finder": "^5.4 || ^6.0", - "symfony/options-resolver": "^5.4 || ^6.0", - "symfony/polyfill-mbstring": "^1.23", - "symfony/polyfill-php80": "^1.25", - "symfony/polyfill-php81": "^1.25", - "symfony/process": "^5.4 || ^6.0", - "symfony/stopwatch": "^5.4 || ^6.0" - }, - "require-dev": { - "justinrainbow/json-schema": "^5.2", - "keradus/cli-executor": "^1.5", - "mikey179/vfsstream": "^1.6.10", - "php-coveralls/php-coveralls": "^2.5.2", - "php-cs-fixer/accessible-object": "^1.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", - "phpspec/prophecy": "^1.15", - "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "phpunitgoodpractices/polyfill": "^1.5", - "phpunitgoodpractices/traits": "^1.9.1", - "symfony/phpunit-bridge": "^6.0", - "symfony/yaml": "^5.4 || ^6.0" - }, - "suggest": { - "ext-dom": "For handling output formats in XML", - "ext-mbstring": "For handling non-UTF8 characters." + "php": "^7.2 || ^8.0" }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", + "type": "library", "autoload": { - "psr-4": { - "PhpCsFixer\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" }, { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "A tool to automatically fix PHP code style", + "description": "Library for handling version information and constraints", "support": { - "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", - "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.8.0" + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "funding": [ - { - "url": "https://github.com/keradus", - "type": "github" - } - ], - "time": "2022-03-18T17:20:59+00:00" + "time": "2022-02-21T01:04:05+00:00" }, { - "name": "php-cs-fixer/diff", - "version": "v2.0.2", + "name": "phpunit/php-code-coverage", + "version": "9.2.24", "source": { "type": "git", - "url": "https://github.com/PHP-CS-Fixer/diff.git", - "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/29dc0d507e838c4580d018bd8b5cb412474f7ec3", - "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed", + "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0 || ^8.0" + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.14", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", - "symfony/process": "^3.3" + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -547,1702 +477,1370 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "sebastian/diff v3 backport support for PHP 5.6+", - "homepage": "https://github.com/PHP-CS-Fixer", + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ - "diff" + "coverage", + "testing", + "xunit" ], "support": { - "issues": "https://github.com/PHP-CS-Fixer/diff/issues", - "source": "https://github.com/PHP-CS-Fixer/diff/tree/v2.0.2" + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24" }, - "time": "2020-10-14T08:32:19+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-01-26T08:26:55+00:00" }, { - "name": "psr/cache", - "version": "3.0.0", + "name": "phpunit/php-file-iterator", + "version": "3.0.6", "source": { "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Common interface for caching libraries", + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ - "cache", - "psr", - "psr-6" + "filesystem", + "iterator" ], "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, - "time": "2021-02-03T23:26:27+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" }, { - "name": "psr/container", - "version": "2.0.2", + "name": "phpunit/php-invoker", + "version": "3.1.1", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { - "php": ">=7.4.0" + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.1-dev" } }, "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "process" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" }, - "time": "2021-11-05T16:47:00+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" }, { - "name": "psr/event-dispatcher", - "version": "1.0.0", + "name": "phpunit/php-text-template", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { - "php": ">=7.2.0" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { - "psr-4": { - "Psr\\EventDispatcher\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Standard interfaces for event handling.", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "events", - "psr", - "psr-14" + "template" ], "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" }, - "time": "2019-01-08T18:20:26+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" }, { - "name": "roave/security-advisories", - "version": "dev-master", + "name": "phpunit/php-timer", + "version": "5.0.3", "source": { "type": "git", - "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "c99945852fb9e9de3bc89c90f24b4c8ef47668fd" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/c99945852fb9e9de3bc89c90f24b4c8ef47668fd", - "reference": "c99945852fb9e9de3bc89c90f24b4c8ef47668fd", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, - "conflict": { - "3f/pygmentize": "<1.2", - "admidio/admidio": "<4.1.9", - "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", - "akaunting/akaunting": "<2.1.13", - "alextselegidis/easyappointments": "<1.4.3", - "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", - "amazing/media2click": ">=1,<1.3.3", - "amphp/artax": "<1.0.6|>=2,<2.0.6", - "amphp/http": "<1.0.1", - "amphp/http-client": ">=4,<4.4", - "anchorcms/anchor-cms": "<=0.12.7", - "andreapollastri/cipi": "<=3.1.15", - "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6", - "appwrite/server-ce": "<0.11.1|>=0.12,<0.12.2", - "area17/twill": "<1.2.5|>=2,<2.5.3", - "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", - "aws/aws-sdk-php": ">=3,<3.2.1", - "bagisto/bagisto": "<0.1.5", - "barrelstrength/sprout-base-email": "<1.2.7", - "barrelstrength/sprout-forms": "<3.9", - "barryvdh/laravel-translation-manager": "<0.6.2", - "baserproject/basercms": "<4.5.4", - "billz/raspap-webgui": "<=2.6.6", - "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", - "bmarshall511/wordpress_zero_spam": "<5.2.13", - "bolt/bolt": "<3.7.2", - "bolt/core": "<=4.2", - "bottelet/flarepoint": "<2.2.1", - "brightlocal/phpwhois": "<=4.2.5", - "buddypress/buddypress": "<7.2.1", - "bugsnag/bugsnag-laravel": ">=2,<2.0.2", - "bytefury/crater": "<6.0.2", - "cachethq/cachet": "<2.5.1", - "cakephp/cakephp": "<4.0.6", - "cardgate/magento2": "<2.0.33", - "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", - "cartalyst/sentry": "<=2.1.6", - "catfan/medoo": "<1.7.5", - "centreon/centreon": "<20.10.7", - "cesnet/simplesamlphp-module-proxystatistics": "<3.1", - "codeception/codeception": "<3.1.3|>=4,<4.1.22", - "codeigniter/framework": "<=3.0.6", - "codeigniter4/framework": "<4.1.9", - "codiad/codiad": "<=2.8.4", - "composer/composer": "<1.10.26|>=2-alpha.1,<2.2.12|>=2.3,<2.3.5", - "concrete5/concrete5": "<9", - "concrete5/core": "<8.5.7", - "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/contao": ">=4,<4.4.56|>=4.5,<4.9.18|>=4.10,<4.11.7|>=4.13,<4.13.3", - "contao/core": ">=2,<3.5.39", - "contao/core-bundle": "<4.9.18|>=4.10,<4.11.7|>=4.13,<4.13.3|= 4.10.0", - "contao/listing-bundle": ">=4,<4.4.8", - "contao/managed-edition": "<=1.5", - "craftcms/cms": "<3.7.29", - "croogo/croogo": "<3.0.7", - "cuyz/valinor": ">=0.5,<0.7", - "czproject/git-php": "<4.0.3", - "darylldoyle/safe-svg": "<1.9.10", - "datadog/dd-trace": ">=0.30,<0.30.2", - "david-garcia/phpwhois": "<=4.3.1", - "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1", - "directmailteam/direct-mail": "<5.2.4", - "doctrine/annotations": ">=1,<1.2.7", - "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", - "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", - "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2|>=3,<3.1.4", - "doctrine/doctrine-bundle": "<1.5.2", - "doctrine/doctrine-module": "<=0.7.1", - "doctrine/mongodb-odm": ">=1,<1.0.2", - "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", - "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "<16|>= 3.3.beta1, < 13.0.2", - "dompdf/dompdf": "<1.2.1", - "drupal/core": ">=7,<7.88|>=8,<9.2.13|>=9.3,<9.3.6", - "drupal/drupal": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", - "dweeves/magmi": "<=0.7.24", - "ecodev/newsletter": "<=4", - "ectouch/ectouch": "<=2.7.2", - "elgg/elgg": "<3.3.24|>=4,<4.0.5", - "endroid/qr-code-bundle": "<3.4.2", - "enshrined/svg-sanitize": "<0.15", - "erusev/parsedown": "<1.7.2", - "ether/logs": "<3.0.4", - "ezsystems/demobundle": ">=5.4,<5.4.6.1", - "ezsystems/ez-support-tools": ">=2.2,<2.2.3", - "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1", - "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1", - "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", - "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.27", - "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", - "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<1.3.17", - "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", - "ezsystems/ezplatform-richtext": ">=2.3,<=2.3.7", - "ezsystems/ezplatform-user": ">=1,<1.0.1", - "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.28", - "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1", - "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", - "ezsystems/repository-forms": ">=2.3,<2.3.2.1", - "ezyang/htmlpurifier": "<4.1.1", - "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", - "facturascripts/facturascripts": "<2022.6", - "feehi/cms": "<=2.1.1", - "feehi/feehicms": "<=0.1.3", - "fenom/fenom": "<=2.12.1", - "firebase/php-jwt": "<2", - "flarum/core": ">=1,<=1.0.1", - "flarum/sticky": ">=0.1-beta.14,<=0.1-beta.15", - "flarum/tags": "<=0.1-beta.13", - "fluidtypo3/vhs": "<5.1.1", - "fooman/tcpdf": "<6.2.22", - "forkcms/forkcms": "<5.11.1", - "fossar/tcpdf-parser": "<6.2.22", - "francoisjacquet/rosariosis": "<8.1.1", - "friendsofsymfony/oauth2-php": "<1.3", - "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", - "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", - "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", - "froala/wysiwyg-editor": "<3.2.7", - "froxlor/froxlor": "<=0.10.22", - "fuel/core": "<1.8.1", - "gaoming13/wechat-php-sdk": "<=1.10.2", - "genix/cms": "<=1.1.11", - "getgrav/grav": "<1.7.31", - "getkirby/cms": "<3.5.8", - "getkirby/panel": "<2.5.14", - "gilacms/gila": "<=1.11.4", - "globalpayments/php-sdk": "<2", - "google/protobuf": "<3.15", - "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", - "gree/jose": "<=2.2", - "gregwar/rst": "<1.0.3", - "grumpydictator/firefly-iii": "<5.6.5", - "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", - "guzzlehttp/psr7": "<1.8.4|>=2,<2.1.1", - "helloxz/imgurl": "<=2.31", - "hillelcoren/invoice-ninja": "<5.3.35", - "hjue/justwriting": "<=1", - "hov/jobfair": "<1.0.13|>=2,<2.0.2", - "hyn/multi-tenant": ">=5.6,<5.7.2", - "ibexa/core": ">=4,<4.0.5|>=4.1,<4.1.2", - "ibexa/post-install": "<=1.0.4", - "icecoder/icecoder": "<=8.1", - "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", - "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.99999|>=4.2,<=4.2.99999|>=5,<=5.0.99999|>=5.1,<=5.1.99999|>=5.2,<=5.2.99999|>=5.3,<=5.3.99999|>=5.4,<=5.4.99999|>=5.5,<=5.5.49|>=5.6,<=5.6.99999|>=5.7,<=5.7.99999|>=5.8,<=5.8.99999|>=6,<6.18.31|>=7,<7.22.4", - "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", - "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", - "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", - "impresscms/impresscms": "<=1.4.3", - "in2code/femanager": "<5.5.1|>=6,<6.3.1", - "intelliants/subrion": "<=4.2.1", - "ivankristianto/phpwhois": "<=4.3", - "jackalope/jackalope-doctrine-dbal": "<1.7.4", - "james-heinrich/getid3": "<1.9.21", - "joomla/archive": "<1.1.12|>=2,<2.0.1", - "joomla/filesystem": "<1.6.2|>=2,<2.0.1", - "joomla/filter": "<1.4.4|>=2,<2.0.1", - "joomla/input": ">=2,<2.0.2", - "joomla/session": "<1.3.1", - "jsdecena/laracom": "<2.0.9", - "jsmitty12/phpwhois": "<5.1", - "kazist/phpwhois": "<=4.2.6", - "kevinpapst/kimai2": "<1.16.7", - "kitodo/presentation": "<3.1.2", - "klaviyo/magento2-extension": ">=1,<3", - "kreait/firebase-php": ">=3.2,<3.8.1", - "la-haute-societe/tcpdf": "<6.2.22", - "laminas/laminas-form": "<2.17.1|>=3,<3.0.2|>=3.1,<3.1.1", - "laminas/laminas-http": "<2.14.2", - "laravel/fortify": "<1.11.1", - "laravel/framework": "<6.20.42|>=7,<7.30.6|>=8,<8.75", - "laravel/laravel": "<=5.8.38", - "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", - "latte/latte": "<2.10.8", - "lavalite/cms": "<=5.8", - "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", - "league/commonmark": "<0.18.3", - "league/flysystem": "<1.1.4|>=2,<2.1.1", - "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", - "librenms/librenms": "<22.2.2", - "limesurvey/limesurvey": "<3.27.19", - "livehelperchat/livehelperchat": "<=3.91", - "livewire/livewire": ">2.2.4,<2.2.6", - "lms/routes": "<2.1.1", - "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", - "luyadev/yii-helpers": "<1.2.1", - "magento/community-edition": ">=2,<2.2.10|>=2.3,<2.3.3", - "magento/magento1ce": "<1.9.4.3", - "magento/magento1ee": ">=1,<1.14.4.3", - "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2-p.2", - "marcwillmann/turn": "<0.3.3", - "matyhtf/framework": "<3.0.6", - "mautic/core": "<4.2|= 2.13.1", - "mediawiki/core": ">=1.27,<1.27.6|>=1.29,<1.29.3|>=1.30,<1.30.2|>=1.31,<1.31.9|>=1.32,<1.32.6|>=1.32.99,<1.33.3|>=1.33.99,<1.34.3|>=1.34.99,<1.35", - "microweber/microweber": "<1.3", - "miniorange/miniorange-saml": "<1.4.3", - "mittwald/typo3_forum": "<1.2.1", - "modx/revolution": "<= 2.8.3-pl|<2.8", - "monolog/monolog": ">=1.8,<1.12", - "moodle/moodle": "<3.9.13|>=3.10-beta,<3.10.10|>=3.11,<3.11.6", - "mustache/mustache": ">=2,<2.14.1", - "namshi/jose": "<2.2", - "neoan3-apps/template": "<1.1.1", - "neorazorx/facturascripts": "<2022.4", - "neos/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", - "neos/form": ">=1.2,<4.3.3|>=5,<5.0.9|>=5.1,<5.1.3", - "neos/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.9.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", - "neos/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", - "netgen/tagsbundle": ">=3.4,<3.4.11|>=4,<4.0.15", - "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", - "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", - "nilsteampassnet/teampass": "<=2.1.27.36", - "nukeviet/nukeviet": "<4.3.4", - "nystudio107/craft-seomatic": "<3.4.12", - "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", - "october/backend": "<1.1.2", - "october/cms": "= 1.1.1|= 1.0.471|= 1.0.469|>=1.0.319,<1.0.469", - "october/october": ">=1.0.319,<1.0.466|>=2.1,<2.1.12", - "october/rain": "<1.0.472|>=1.1,<1.1.2", - "october/system": "<1.0.475|>=1.1,<1.1.11|>=2,<2.1.27", - "onelogin/php-saml": "<2.10.4", - "oneup/uploader-bundle": "<1.9.3|>=2,<2.1.5", - "open-web-analytics/open-web-analytics": "<1.7.4", - "opencart/opencart": "<=3.0.3.2", - "openid/php-openid": "<2.3", - "openmage/magento-lts": "<19.4.15|>=20,<20.0.13", - "orchid/platform": ">=9,<9.4.4", - "oro/crm": ">=1.7,<1.7.4|>=3.1,<4.1.17|>=4.2,<4.2.7", - "oro/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<4.2.8", - "padraic/humbug_get_contents": "<1.1.2", - "pagarme/pagarme-php": ">=0,<3", - "pagekit/pagekit": "<=1.0.18", - "paragonie/random_compat": "<2", - "passbolt/passbolt_api": "<2.11", - "paypal/merchant-sdk-php": "<3.12", - "pear/archive_tar": "<1.4.14", - "pear/crypt_gpg": "<1.6.7", - "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", - "personnummer/personnummer": "<3.0.2", - "phanan/koel": "<5.1.4", - "phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7", - "phpmailer/phpmailer": "<6.5", - "phpmussel/phpmussel": ">=1,<1.6", - "phpmyadmin/phpmyadmin": "<5.1.3", - "phpoffice/phpexcel": "<1.8", - "phpoffice/phpspreadsheet": "<1.16", - "phpseclib/phpseclib": "<2.0.31|>=3,<3.0.7", - "phpservermon/phpservermon": "<=3.5.2", - "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5,<5.6.3", - "phpwhois/phpwhois": "<=4.2.5", - "phpxmlrpc/extras": "<0.6.1", - "pimcore/data-hub": "<1.2.4", - "pimcore/pimcore": "<10.4", - "pocketmine/bedrock-protocol": "<8.0.2", - "pocketmine/pocketmine-mp": "<4.2.9", - "pressbooks/pressbooks": "<5.18", - "prestashop/autoupgrade": ">=4,<4.10.1", - "prestashop/contactform": ">1.0.1,<4.3", - "prestashop/gamification": "<2.3.2", - "prestashop/prestashop": ">=1.7,<=1.7.8.2", - "prestashop/productcomments": ">=4,<4.2.1", - "prestashop/ps_emailsubscription": "<2.6.1", - "prestashop/ps_facetedsearch": "<3.4.1", - "prestashop/ps_linklist": "<3.1", - "privatebin/privatebin": "<1.4", - "propel/propel": ">=2-alpha.1,<=2-alpha.7", - "propel/propel1": ">=1,<=1.7.1", - "pterodactyl/panel": "<1.7", - "ptrofimov/beanstalk_console": "<1.7.14", - "pusher/pusher-php-server": "<2.2.1", - "pwweb/laravel-core": "<=0.3.6-beta", - "rainlab/debugbar-plugin": "<3.1", - "remdex/livehelperchat": "<3.99", - "rmccue/requests": ">=1.6,<1.8", - "robrichards/xmlseclibs": "<3.0.4", - "rudloff/alltube": "<3.0.3", - "s-cart/s-cart": "<6.7.2", - "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", - "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", - "scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11", - "sensiolabs/connect": "<4.2.3", - "serluck/phpwhois": "<=4.2.6", - "shopware/core": "<=6.4.9", - "shopware/platform": "<=6.4.9", - "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<5.7.9", - "shopware/storefront": "<=6.4.8.1", - "showdoc/showdoc": "<2.10.4", - "silverstripe/admin": ">=1,<1.8.1", - "silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2", - "silverstripe/cms": "<4.3.6|>=4.4,<4.4.4", - "silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1", - "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": "<4.10.1", - "silverstripe/graphql": "<3.5.2|>=4-alpha.1,<4-alpha.2|= 4.0.0-alpha1", - "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", - "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4", - "silverstripe/subsites": ">=2,<2.1.1", - "silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1", - "silverstripe/userforms": "<3", - "simple-updates/phpwhois": "<=1", - "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", - "simplesamlphp/simplesamlphp": "<1.18.6", - "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", - "simplito/elliptic-php": "<1.0.6", - "slim/slim": "<2.6", - "smarty/smarty": "<3.1.43|>=4,<4.0.3", - "snipe/snipe-it": "<5.4.3|>= 6.0.0-RC-1, <= 6.0.0-RC-5", - "socalnick/scn-social-auth": "<1.15.2", - "socialiteproviders/steam": "<1.1", - "spipu/html2pdf": "<5.2.4", - "spoonity/tcpdf": "<6.2.22", - "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", - "ssddanbrown/bookstack": "<22.2.3", - "statamic/cms": "<3.2.39|>=3.3,<3.3.2", - "stormpath/sdk": ">=0,<9.9.99", - "studio-42/elfinder": "<2.1.59", - "subrion/cms": "<=4.2.1", - "sulu/sulu": "= 2.4.0-RC1|<1.6.44|>=2,<2.2.18|>=2.3,<2.3.8", - "swiftmailer/swiftmailer": ">=4,<5.4.5", - "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", - "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", - "sylius/grid-bundle": "<1.10.1", - "sylius/paypal-plugin": ">=1,<1.2.4|>=1.3,<1.3.1", - "sylius/resource-bundle": "<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", - "sylius/sylius": "<1.9.10|>=1.10,<1.10.11|>=1.11,<1.11.2", - "symbiote/silverstripe-multivaluefield": ">=3,<3.0.99", - "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", - "symbiote/silverstripe-versionedfiles": "<=2.0.3", - "symfont/process": ">=0,<4", - "symfony/cache": ">=3.1,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", - "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", - "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", - "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", - "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<=5.3.14|>=5.4.3,<=5.4.3|>=6.0.3,<=6.0.3|= 6.0.3|= 5.4.3|= 5.3.14", - "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", - "symfony/http-kernel": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.4.13|>=5,<5.1.5|>=5.2,<5.3.12", - "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1", - "symfony/mime": ">=4.3,<4.3.8", - "symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", - "symfony/polyfill": ">=1,<1.10", - "symfony/polyfill-php55": ">=1,<1.10", - "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", - "symfony/routing": ">=2,<2.0.19", - "symfony/security": ">=2,<2.7.51|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.8", - "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11|>=5.3,<5.3.12", - "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9", - "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", - "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.3.2", - "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", - "symfony/symfony": ">=2,<3.4.49|>=4,<4.4.35|>=5,<5.3.12|>=5.3.14,<=5.3.14|>=5.4.3,<=5.4.3|>=6.0.3,<=6.0.3", - "symfony/translation": ">=2,<2.0.17", - "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", - "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", - "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", - "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", - "t3/dce": ">=2.2,<2.6.2", - "t3g/svg-sanitizer": "<1.0.3", - "tastyigniter/tastyigniter": "<3.3", - "tecnickcom/tcpdf": "<6.2.22", - "terminal42/contao-tablelookupwizard": "<3.3.5", - "thelia/backoffice-default-template": ">=2.1,<2.1.2", - "thelia/thelia": ">=2.1-beta.1,<2.1.3", - "theonedemon/phpwhois": "<=4.2.5", - "tinymce/tinymce": "<5.10", - "titon/framework": ">=0,<9.9.99", - "topthink/framework": "<6.0.9", - "topthink/think": "<=6.0.9", - "topthink/thinkphp": "<=3.2.3", - "tribalsystems/zenario": "<9.2.55826", - "truckersmp/phpwhois": "<=4.3.1", - "twig/twig": "<1.38|>=2,<2.14.11|>=3,<3.3.8", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.38|>=9,<9.5.29|>=10,<10.4.19|>=11,<11.5", - "typo3/cms-backend": ">=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", - "typo3/cms-core": ">=6.2,<=6.2.56|>=7,<=7.6.52|>=8,<=8.7.41|>=9,<9.5.29|>=10,<10.4.19|>=11,<11.5", - "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", - "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", - "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", - "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", - "typo3/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", - "typo3fluid/fluid": ">=2,<2.0.8|>=2.1,<2.1.7|>=2.2,<2.2.4|>=2.3,<2.3.7|>=2.4,<2.4.4|>=2.5,<2.5.11|>=2.6,<2.6.10", - "ua-parser/uap-php": "<3.8", - "unisharp/laravel-filemanager": "<=2.3", - "userfrosting/userfrosting": ">=0.3.1,<4.6.3", - "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", - "vanilla/safecurl": "<0.9.2", - "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4", - "vrana/adminer": "<4.8.1", - "wallabag/tcpdf": "<6.2.22", - "wanglelecc/laracms": "<=1.0.3", - "web-auth/webauthn-framework": ">=3.3,<3.3.4", - "webcoast/deferred-image-processing": "<1.0.2", - "wikimedia/parsoid": "<0.12.2", - "willdurand/js-translation-bundle": "<2.1.1", - "wp-cli/wp-cli": "<2.5", - "wpanel/wpanel4-cms": "<=4.3.1", - "wwbn/avideo": "<=11.6", - "yeswiki/yeswiki": "<4.1", - "yetiforce/yetiforce-crm": "<=6.3", - "yidashi/yii2cmf": "<=2", - "yii2mod/yii2-cms": "<1.9.2", - "yiisoft/yii": ">=1.1.14,<1.1.15", - "yiisoft/yii2": "<2.0.38", - "yiisoft/yii2-bootstrap": "<2.0.4", - "yiisoft/yii2-dev": "<2.0.43", - "yiisoft/yii2-elasticsearch": "<2.0.5", - "yiisoft/yii2-gii": "<2.0.4", - "yiisoft/yii2-jui": "<2.0.4", - "yiisoft/yii2-redis": "<2.0.8", - "yoast-seo-for-typo3/yoast_seo": "<7.2.3", - "yourls/yourls": "<=1.8.2", - "zendesk/zendesk_api_client_php": "<2.2.11", - "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", - "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", - "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", - "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", - "zendframework/zend-developer-tools": ">=1.2.2,<1.2.3", - "zendframework/zend-diactoros": "<1.8.4", - "zendframework/zend-feed": "<2.10.3", - "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-http": "<2.8.1", - "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", - "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", - "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", - "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4", - "zendframework/zend-validator": ">=2.3,<2.3.6", - "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", - "zendframework/zendframework": "<=3", - "zendframework/zendframework1": "<1.12.20", - "zendframework/zendopenid": ">=2,<2.0.2", - "zendframework/zendxml": ">=1,<1.0.1", - "zetacomponents/mail": "<1.8.2", - "zf-commons/zfc-user": "<1.2.2", - "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", - "zfr/zfr-oauth2-server-module": "<0.1.2", - "zoujingli/thinkadmin": "<6.0.22" - }, - "type": "metapackage", + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "role": "maintainer" - }, - { - "name": "Ilya Tribusean", - "email": "slash3b@gmail.com", - "role": "maintainer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], "support": { - "issues": "https://github.com/Roave/SecurityAdvisories/issues", - "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" }, "funding": [ { - "url": "https://github.com/Ocramius", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/roave/security-advisories", - "type": "tidelift" } ], - "time": "2022-05-06T13:19:01+00:00" + "time": "2020-10-26T13:16:10+00:00" }, { - "name": "symfony/console", - "version": "v6.0.8", + "name": "phpunit/phpunit", + "version": "9.5.28", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "0d00aa289215353aa8746a31d101f8e60826285c" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0d00aa289215353aa8746a31d101f8e60826285c", - "reference": "0d00aa289215353aa8746a31d101f8e60826285c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e", + "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.4|^6.0" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" + "doctrine/instantiator": "^1.3.1 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/version": "^3.0.2" }, "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "ext-soap": "*", + "ext-xdebug": "*" }, + "bin": [ + "phpunit" + ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", "keywords": [ - "cli", - "command line", - "console", - "terminal" + "phpunit", + "testing", + "xunit" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.8" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28" }, "funding": [ { - "url": "https://symfony.com/sponsor", + "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2022-04-20T15:01:42+00:00" + "time": "2023-01-14T12:32:24+00:00" }, { - "name": "symfony/deprecation-contracts", - "version": "v3.0.1", + "name": "sebastian/cli-parser", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "dev-master": "1.0-dev" } }, "autoload": { - "files": [ - "function.php" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1" + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2020-09-28T06:08:49+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v6.0.3", + "name": "sebastian/code-unit", + "version": "1.0.8", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "6472ea2dd415e925b90ca82be64b8bc6157f3934" + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/6472ea2dd415e925b90ca82be64b8bc6157f3934", - "reference": "6472ea2dd415e925b90ca82be64b8bc6157f3934", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/event-dispatcher-contracts": "^2|^3" - }, - "conflict": { - "symfony/dependency-injection": "<5.4" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0|3.0" + "php": ">=7.3" }, "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/error-handler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^5.4|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "phpunit/phpunit": "^9.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.3" + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2020-10-26T13:08:54+00:00" }, { - "name": "symfony/event-dispatcher-contracts", - "version": "v3.0.1", + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "7bc61cc2db649b4637d331240c5346dcc7708051" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7bc61cc2db649b4637d331240c5346dcc7708051", - "reference": "7bc61cc2db649b4637d331240c5346dcc7708051", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { - "php": ">=8.0.2", - "psr/event-dispatcher": "^1" + "php": ">=7.3" }, - "suggest": { - "symfony/event-dispatcher-implementation": "" + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "dev-master": "2.0-dev" } }, "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.1" + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2020-09-28T05:30:19+00:00" }, { - "name": "symfony/filesystem", - "version": "v6.0.7", + "name": "sebastian/comparator", + "version": "4.0.8", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "6c9e4c41f2c51dfde3db298594ed9cba55dbf5ff" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/6c9e4c41f2c51dfde3db298594ed9cba55dbf5ff", - "reference": "6c9e4c41f2c51dfde3db298594ed9cba55dbf5ff", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8" + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.0.7" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-04-01T12:54:51+00:00" + "time": "2022-09-14T12:41:17+00:00" }, { - "name": "symfony/finder", - "version": "v6.0.8", + "name": "sebastian/complexity", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "af7edab28d17caecd1f40a9219fc646ae751c21f" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/af7edab28d17caecd1f40a9219fc646ae751c21f", - "reference": "af7edab28d17caecd1f40a9219fc646ae751c21f", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", "shasum": "" }, "require": { - "php": ">=8.0.2" + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "source": "https://github.com/symfony/finder/tree/v6.0.8" + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-04-15T08:07:58+00:00" + "time": "2020-10-26T15:52:27+00:00" }, { - "name": "symfony/inflector", - "version": "v5.4.3", + "name": "sebastian/diff", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/inflector.git", - "reference": "6157dac05bbd287d341b82d67a549fdf468f86d1" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/inflector/zipball/6157dac05bbd287d341b82d67a549fdf468f86d1", - "reference": "6157dac05bbd287d341b82d67a549fdf468f86d1", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16", - "symfony/string": "^5.3.10|^6.0" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Inflector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], - "description": "Converts words between their singular and plural forms (English only)", - "homepage": "https://symfony.com", + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "inflection", - "pluralize", - "singularize", - "string", - "symfony", - "words" + "diff", + "udiff", + "unidiff", + "unified diff" ], "support": { - "source": "https://github.com/symfony/inflector/tree/v5.4.3" + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "abandoned": "EnglishInflector from the String component", - "time": "2022-01-02T09:53:40+00:00" + "time": "2020-10-26T13:10:38+00:00" }, { - "name": "symfony/options-resolver", - "version": "v6.0.3", + "name": "sebastian/environment", + "version": "5.1.4", "source": { "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "51f7006670febe4cbcbae177cbffe93ff833250d" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/51f7006670febe4cbcbae177cbffe93ff833250d", - "reference": "51f7006670febe4cbcbae177cbffe93ff833250d", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", "keywords": [ - "config", - "configuration", - "options" + "Xdebug", + "environment", + "hhvm" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.0.3" + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-04-03T09:37:03+00:00" }, { - "name": "symfony/phpunit-bridge", - "version": "v6.0.8", + "name": "sebastian/exporter", + "version": "4.0.5", "source": { "type": "git", - "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "4959a1eedd473bdb3f19db5b1525d5415dfab471" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/4959a1eedd473bdb3f19db5b1525d5415dfab471", - "reference": "4959a1eedd473bdb3f19db5b1525d5415dfab471", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "shasum": "" }, "require": { - "php": ">=7.1.3" - }, - "conflict": { - "phpunit/phpunit": "<7.5|9.1.2" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "symfony/deprecation-contracts": "^2.1|^3.0", - "symfony/error-handler": "^5.4|^6.0" - }, - "suggest": { - "symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" }, - "bin": [ - "bin/simple-phpunit" - ], - "type": "symfony-bridge", + "type": "library", "extra": { - "thanks": { - "name": "phpunit/phpunit", - "url": "https://github.com/sebastianbergmann/phpunit" + "branch-alias": { + "dev-master": "4.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Bridge\\PhpUnit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Provides utilities for PHPUnit, especially user deprecation notices management", - "homepage": "https://symfony.com", + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v6.0.8" + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-04-12T16:11:42+00:00" + "time": "2022-09-14T06:03:37+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.25.0", + "name": "sebastian/global-state", + "version": "5.0.5", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "30885182c981ab175d4d034db0f6f469898070ab" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", - "reference": "30885182c981ab175d4d034db0f6f469898070ab", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, - "provide": { - "ext-ctype": "*" + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-ctype": "For best performance" + "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "dev-master": "5.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" + "global state" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2021-10-20T20:35:02+00:00" + "time": "2022-02-14T08:28:10+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.25.0", + "name": "sebastian/lines-of-code", + "version": "1.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "81b86b50cf841a64252b439e738e97f4a34e2783" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783", - "reference": "81b86b50cf841a64252b439e738e97f4a34e2783", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", "shasum": "" }, "require": { - "php": ">=7.1" + "nikic/php-parser": "^4.6", + "php": ">=7.3" }, - "suggest": { - "ext-intl": "For best performance" + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "dev-master": "1.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "time": "2021-11-23T21:10:46+00:00" + "time": "2020-10-26T13:14:26+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.25.0", + "name": "sebastian/recursion-context", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, - "suggest": { - "ext-intl": "For best performance" + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "dev-master": "4.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, "classmap": [ - "Resources/stubs" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, + } + ], + "time": "2020-10-26T13:17:30+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "time": "2021-02-19T12:13:01+00:00" + "time": "2020-09-28T06:45:17+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.25.0", + "name": "sebastian/type", + "version": "3.2.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, - "provide": { - "ext-mbstring": "*" + "require-dev": { + "phpunit/phpunit": "^9.5" }, - "suggest": { - "ext-mbstring": "For best performance" + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-12T14:47:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "dev-master": "3.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2021-11-30T18:21:41+00:00" + "time": "2020-09-28T06:39:44+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.25.0", + "name": "symfony/browser-kit", + "version": "v6.2.5", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" + "url": "https://github.com/symfony/browser-kit.git", + "reference": "ea591a69d714216d29cb67b519b509bd32b735a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", - "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ea591a69d714216d29cb67b519b509bd32b735a2", + "reference": "ea591a69d714216d29cb67b519b509bd32b735a2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1", + "symfony/dom-crawler": "^5.4|^6.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } + "require-dev": { + "symfony/css-selector": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/mime": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0" + }, + "suggest": { + "symfony/process": "" }, + "type": "library", "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" + "Symfony\\Component\\BrowserKit\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2251,28 +1849,18 @@ ], "authors": [ { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" + "source": "https://github.com/symfony/browser-kit/tree/v6.2.5" }, "funding": [ { @@ -2288,44 +1876,32 @@ "type": "tidelift" } ], - "time": "2022-03-04T08:16:47+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.25.0", + "name": "symfony/css-selector", + "version": "v6.2.5", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f" + "url": "https://github.com/symfony/css-selector.git", + "reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", - "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1", + "reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Component\\CssSelector\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2334,24 +1910,22 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0" + "source": "https://github.com/symfony/css-selector/tree/v6.2.5" }, "funding": [ { @@ -2367,29 +1941,38 @@ "type": "tidelift" } ], - "time": "2021-09-13T13:58:11+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { - "name": "symfony/process", - "version": "v6.0.8", + "name": "symfony/dom-crawler", + "version": "v6.2.5", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "d074154ea8b1443a96391f6e39f9e547b2dd01b9" + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "19aa4962a0687e96941f0bdb27b794c5b73e2394" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d074154ea8b1443a96391f6e39f9e547b2dd01b9", - "reference": "d074154ea8b1443a96391f6e39f9e547b2dd01b9", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/19aa4962a0687e96941f0bdb27b794c5b73e2394", + "reference": "19aa4962a0687e96941f0bdb27b794c5b73e2394", "shasum": "" }, "require": { - "php": ">=8.0.2" + "masterminds/html5": "^2.6", + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "^5.4|^6.0" + }, + "suggest": { + "symfony/css-selector": "" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Component\\DomCrawler\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2409,10 +1992,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Executes commands in sub-processes", + "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.0.8" + "source": "https://github.com/symfony/dom-crawler/tree/v6.2.5" }, "funding": [ { @@ -2428,49 +2011,51 @@ "type": "tidelift" } ], - "time": "2022-04-12T16:11:42+00:00" + "time": "2023-01-20T17:45:48+00:00" }, { - "name": "symfony/property-info", - "version": "v6.0.7", + "name": "symfony/phpunit-bridge", + "version": "v6.2.5", "source": { "type": "git", - "url": "https://github.com/symfony/property-info.git", - "reference": "0f26f0870f05d65d5c06681ecbf36e546204f4b5" + "url": "https://github.com/symfony/phpunit-bridge.git", + "reference": "d759e5372de414bef53a688c7aa7e240e4fd8aa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/0f26f0870f05d65d5c06681ecbf36e546204f4b5", - "reference": "0f26f0870f05d65d5c06681ecbf36e546204f4b5", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/d759e5372de414bef53a688c7aa7e240e4fd8aa2", + "reference": "d759e5372de414bef53a688c7aa7e240e4fd8aa2", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/string": "^5.4|^6.0" + "php": ">=7.1.3" }, "conflict": { - "phpdocumentor/reflection-docblock": "<5.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/dependency-injection": "<5.4" + "phpunit/phpunit": "<7.5|9.1.2" }, "require-dev": { - "doctrine/annotations": "^1.10.4", - "phpdocumentor/reflection-docblock": "^5.2", - "phpstan/phpdoc-parser": "^1.0", - "symfony/cache": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0" + "symfony/deprecation-contracts": "^2.1|^3.0", + "symfony/error-handler": "^5.4|^6.0" }, "suggest": { - "phpdocumentor/reflection-docblock": "To use the PHPDoc", - "psr/cache-implementation": "To cache results", - "symfony/doctrine-bridge": "To use Doctrine metadata", - "symfony/serializer": "To use Serializer metadata" + "symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + }, + "bin": [ + "bin/simple-phpunit" + ], + "type": "symfony-bridge", + "extra": { + "thanks": { + "name": "phpunit/phpunit", + "url": "https://github.com/sebastianbergmann/phpunit" + } }, - "type": "library", "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\PropertyInfo\\": "" + "Symfony\\Bridge\\PhpUnit\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2482,26 +2067,18 @@ ], "authors": [ { - "name": "Kévin Dunglas", - "email": "dunglas@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Extracts information about PHP class' properties using metadata of popular sources", + "description": "Provides utilities for PHPUnit, especially user deprecation notices management", "homepage": "https://symfony.com", - "keywords": [ - "doctrine", - "phpdoc", - "property", - "symfony", - "type", - "validator" - ], "support": { - "source": "https://github.com/symfony/property-info/tree/v6.0.7" + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.2.5" }, "funding": [ { @@ -2517,45 +2094,47 @@ "type": "tidelift" } ], - "time": "2022-03-31T17:18:25+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { - "name": "symfony/service-contracts", - "version": "v3.0.1", + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e517458f278c2131ca9f262f8fbaf01410f2c65c", - "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "shasum": "" }, "require": { - "php": ">=8.0.2", - "psr/container": "^2.0" + "php": ">=7.1" }, - "conflict": { - "ext-psr": "<1.1|>=2" + "provide": { + "ext-ctype": "*" }, "suggest": { - "symfony/service-implementation": "" + "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "1.27-dev" }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Contracts\\Service\\": "" + "Symfony\\Polyfill\\Ctype\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2564,26 +2143,24 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to writing services", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "compatibility", + "ctype", + "polyfill", + "portable" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.0.1" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" }, "funding": [ { @@ -2599,34 +2176,48 @@ "type": "tidelift" } ], - "time": "2022-03-13T20:10:05+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/stopwatch", - "version": "v6.0.5", + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "f2c1780607ec6502f2121d9729fd8150a655d337" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f2c1780607ec6502f2121d9729fd8150a655d337", - "reference": "f2c1780607ec6502f2121d9729fd8150a655d337", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/service-contracts": "^1|^2|^3" + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2634,18 +2225,25 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a way to profile code", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.0.5" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -2661,76 +2259,43 @@ "type": "tidelift" } ], - "time": "2022-02-21T17:15:17+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/string", - "version": "v6.0.8", + "name": "symfony/test-pack", + "version": "1.0.10", "source": { "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d" + "url": "https://github.com/symfony/test-pack.git", + "reference": "03ab012f4aab784690d21417f7cdd7b432fd4e73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ac0aa5c2282e0de624c175b68d13f2c8f2e2649d", - "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d", + "url": "https://api.github.com/repos/symfony/test-pack/zipball/03ab012f4aab784690d21417f7cdd7b432fd4e73", + "reference": "03ab012f4aab784690d21417f7cdd7b432fd4e73", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.0" + "phpunit/phpunit": "*", + "symfony/browser-kit": "*", + "symfony/css-selector": "*", + "symfony/phpunit-bridge": "*" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/translation-contracts": "^2.0|^3.0", - "symfony/var-exporter": "^5.4|^6.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "phpunit/phpunit": "*", + "symfony/browser-kit": "*", + "symfony/css-selector": "*", + "symfony/phpunit-bridge": "*" }, + "type": "symfony-pack", "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": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], + "description": "A pack for functional and end-to-end testing within a Symfony app", "support": { - "source": "https://github.com/symfony/string/tree/v6.0.8" + "issues": "https://github.com/symfony/test-pack/issues", + "source": "https://github.com/symfony/test-pack/tree/1.0.10" }, "funding": [ { @@ -2746,108 +2311,68 @@ "type": "tidelift" } ], - "time": "2022-04-22T08:18:02+00:00" + "time": "2022-04-15T12:25:45+00:00" }, { - "name": "symfony/var-dumper", - "version": "v6.0.8", + "name": "theseer/tokenizer", + "version": "1.2.1", "source": { "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "fa61dfb4bd3068df2492013dc65f3190e9f550c0" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/fa61dfb4bd3068df2492013dc65f3190e9f550c0", - "reference": "fa61dfb4bd3068df2492013dc65f3190e9f550c0", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<5.4" - }, - "require-dev": { - "ext-iconv": "*", - "symfony/console": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/uid": "^5.4|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" }, - "bin": [ - "Resources/bin/var-dump-server" - ], "type": "library", "autoload": { - "files": [ - "Resources/functions/dump.php" - ], - "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "Provides mechanisms for walking through any arbitrary PHP variable", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.0.8" + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/theseer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2022-04-26T13:22:23+00:00" + "time": "2021-07-28T10:34:58+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "roave/security-advisories": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.2", + "php": ">=8.1", "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.3.0" } diff --git a/docs/index.md b/docs/index.md index 2f81814..0cb0e0e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -28,12 +28,12 @@ for HTTP requests. If missing, PHP native streams are used. Odoo database support --------------------- -| Odoo series | Compatibility | Comment | -| --- | --- | --- | -| Newer | Unknown | Needs feedbacks | -| v13.0 | Yes | Some Odoo model names changed (e.g account.invoice > account.move) | -| v12.0 | Yes | First tested version | -| Older | Unknown | Needs feedbacks | +| Odoo series | Compatibility | Comment | +|-------------|---------------|--------------------------------------------------------------------| +| Newer | Unknown | Needs feedbacks | +| v13.0 | Yes | Some Odoo model names changed (e.g account.invoice > account.move) | +| v12.0 | Yes | First tested version | +| Older | Unknown | Needs feedbacks | Installation ------------ diff --git a/phpstan.neon b/phpstan.neon index fc1d4be..dccb5b2 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,6 +5,6 @@ parameters: paths: - %currentWorkingDirectory%/src ignoreErrors: - - '#Method Ang3\\Component\\Odoo\\Client\:\:findBy\(\) should return array but returns array\.#' + - '#Method .* should return array<\w+> but returns array\.#' - '#Cannot cast mixed to int\.#' - '#Cannot cast mixed to string\.#' \ No newline at end of file diff --git a/src/Client.php b/src/Client.php index 8c654a8..f9e1aad 100644 --- a/src/Client.php +++ b/src/Client.php @@ -1,5 +1,14 @@ connection = $connection; + public function __construct( + private readonly Connection $connection, + ?TransportInterface $transport = null, + private ?LoggerInterface $logger = null + ) { $this->expressionBuilder = new ExpressionBuilder(); $this->transport = $transport ?: new JsonRpcPhpStreamTransport($this->connection); - $this->logger = $logger; } /** @@ -82,23 +68,26 @@ public function __construct(Connection $connection, TransportInterface $transpor * * @throws MissingConfigParameterException when a required parameter is missing */ - public static function create(array $config, TransportInterface $transport = null, LoggerInterface $logger = null): self - { + public static function create( + array $config, + ?TransportInterface $transport = null, + ?LoggerInterface $logger = null + ): self { return new self(Connection::create($config), $transport, $logger); } /** * Creates a new record and returns the new ID. * - * @throws InvalidArgumentException when $data is empty - * @throws RequestException when request failed - * * @return int the ID of the new record + * + * @throws \InvalidArgumentException when $data is empty + * @throws RequestException when request failed */ public function insert(string $modelName, array $data): int { if (!$data) { - throw new InvalidArgumentException('Data cannot be empty'); + throw new \InvalidArgumentException('Data cannot be empty'); } /** @var int[] $result */ @@ -110,13 +99,11 @@ public function insert(string $modelName, array $data): int /** * Read records. * - * @param array|int $ids - * * @throws RequestException when request failed */ - public function read(string $modelName, $ids, array $options = []): array + public function read(string $modelName, int|array $ids, array $options = []): array { - $ids = is_int($ids) ? [$ids] : (array) $ids; + $ids = \is_int($ids) ? [$ids] : $ids; return (array) $this->execute($modelName, self::READ, [$ids], $options); } @@ -124,38 +111,34 @@ public function read(string $modelName, $ids, array $options = []): array /** * Update a record(s). * - * @param array|int $ids - * * @throws RequestException when request failed */ - public function update(string $modelName, $ids, array $data = []): void + public function update(string $modelName, int|array $ids, array $data = []): void { if (!$data) { return; } - $ids = is_array($ids) ? $ids : [(int) $ids]; + $ids = \is_array($ids) ? $ids : [$ids]; $this->execute($modelName, self::WRITE, [$ids, $data]); } /** * Delete record(s). * - * @param array|int $ids - * * @throws RequestException when request failed */ - public function delete(string $modelName, $ids): void + public function delete(string $modelName, int|array $ids): void { - $ids = is_array($ids) ? $ids : [(int) $ids]; + $ids = \is_array($ids) ? $ids : [(int) $ids]; $this->execute($modelName, self::UNLINK, [$ids]); } /** * Search one ID of record by criteria and options. * - * @throws InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed + * @throws \InvalidArgumentException when $criteria value is not valid + * @throws RequestException when request failed */ public function searchOne(string $modelName, iterable $criteria = null, array $options = []): ?int { @@ -168,10 +151,10 @@ public function searchOne(string $modelName, iterable $criteria = null, array $o /** * Search all ID of record(s) with options. * - * @throws InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed - * * @return array + * + * @throws \InvalidArgumentException when $criteria value is not valid + * @throws RequestException when request failed */ public function searchAll(string $modelName, array $options = []): array { @@ -183,21 +166,18 @@ public function searchAll(string $modelName, array $options = []): array /** * Find ID of record(s) by criteria and options. * - * @throws InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed - * * @return array + * + * @throws \InvalidArgumentException when $criteria value is not valid + * @throws RequestException when request failed */ public function search(string $modelName, iterable $criteria = null, array $options = []): array { - if (array_key_exists('fields', $options)) { + if (\array_key_exists('fields', $options)) { unset($options['fields']); } - /** @var int[] $result */ - $result = (array) $this->execute($modelName, self::SEARCH, [$this->expressionBuilder->normalizeDomains($criteria)], $options); - - return $result; + return (array) $this->execute($modelName, self::SEARCH, [$this->expressionBuilder->normalizeDomains($criteria)], $options); } /** @@ -215,8 +195,8 @@ public function find(string $modelName, int $id, array $options = []): ?array /** * Find ONE record by criteria and options. * - * @throws InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed + * @throws \InvalidArgumentException when $criteria value is not valid + * @throws RequestException when request failed */ public function findOneBy(string $modelName, iterable $criteria = null, array $options = []): ?array { @@ -228,9 +208,9 @@ public function findOneBy(string $modelName, iterable $criteria = null, array $o /** * Find all record(s) with options. * - * @throws RequestException when request failed - * * @return array + * + * @throws RequestException when request failed */ public function findAll(string $modelName, array $options = []): array { @@ -240,10 +220,10 @@ public function findAll(string $modelName, array $options = []): array /** * Find record(s) by criteria and options. * - * @throws InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed - * * @return array[] + * + * @throws \InvalidArgumentException when $criteria value is not valid + * @throws RequestException when request failed */ public function findBy(string $modelName, iterable $criteria = null, array $options = []): array { @@ -265,8 +245,8 @@ public function exists(string $modelName, int $id): bool /** * Count all records for a model. * - * @throws InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed + * @throws \InvalidArgumentException when $criteria value is not valid + * @throws RequestException when request failed */ public function countAll(string $modelName): int { @@ -276,8 +256,8 @@ public function countAll(string $modelName): int /** * Count number of records for a model and criteria. * - * @throws InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed + * @throws \InvalidArgumentException when $criteria value is not valid + * @throws RequestException when request failed */ public function count(string $modelName, iterable $criteria = null): int { @@ -348,7 +328,7 @@ public function request(string $service, string $method, ...$arguments) 'service' => $service, 'method' => $method, 'uid' => (int) $this->uid, - 'arguments' => array_slice($arguments, 3), + 'arguments' => \array_slice($arguments, 3), 'request_id' => uniqid('rpc', true), ]; @@ -372,7 +352,7 @@ public function request(string $service, string $method, ...$arguments) ]); } - if (is_array($payload['error'] ?? null)) { + if (\is_array($payload['error'] ?? null)) { throw RemoteException::create($payload); } @@ -384,6 +364,22 @@ public function getConnection(): Connection return $this->connection; } + public function getTransport(): TransportInterface + { + return $this->transport; + } + + public function setTransport(TransportInterface $transport): self + { + if ($transport !== $this->transport) { + $this->uid = null; + } + + $this->transport = $transport; + + return $this; + } + public function getExpressionBuilder(): ExpressionBuilder { return $this->expressionBuilder; diff --git a/src/Connection.php b/src/Connection.php index 9a6982d..b57f660 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -1,5 +1,14 @@ @@ -31,7 +38,7 @@ class Comparison implements DomainInterface /** * @var string[] */ - private static $operators = [ + private static array $operators = [ self::UNSET_OR_EQUAL_TO, self::EQUAL_TO, self::NOT_EQUAL_TO, @@ -49,39 +56,18 @@ class Comparison implements DomainInterface self::NOT_IN, ]; - /** - * @var string - */ - private $fieldName; - - /** - * @var string - */ - private $operator; - - /** - * @var mixed - */ - private $value; - - /** - * @param mixed $value - */ - public function __construct(string $fieldName, string $operator, $value) + public function __construct(private string $fieldName, private string $operator, private mixed $value) { - $this->fieldName = $fieldName; - $this->operator = $operator; - $this->value = $value; } public function __clone() { - $this->value = is_object($this->value) ? clone $this->value : $this->value; + $this->value = \is_object($this->value) ? clone $this->value : $this->value; } - public function getIterator(): ArrayIterator + public function getIterator(): \ArrayIterator { - return new ArrayIterator($this->toArray()); + return new \ArrayIterator($this->toArray()); } public function toArray(): array @@ -121,18 +107,12 @@ public function setOperator(string $operator): self return $this; } - /** - * @return mixed - */ - public function getValue() + public function getValue(): mixed { return $this->value; } - /** - * @param mixed $value - */ - public function setValue($value): self + public function setValue(mixed $value): self { $this->value = $value; diff --git a/src/Expression/Domain/CompositeDomain.php b/src/Expression/Domain/CompositeDomain.php index 6ddc1dd..6297af4 100644 --- a/src/Expression/Domain/CompositeDomain.php +++ b/src/Expression/Domain/CompositeDomain.php @@ -1,8 +1,15 @@ @@ -19,26 +26,17 @@ class CompositeDomain implements DomainInterface /** * @var string[] */ - private static $operators = [ + private static array $operators = [ self::AND, self::OR, self::NOT, ]; /** - * @var string + * @param DomainInterface[] $domains */ - private $operator; - - /** - * @var DomainInterface[] - */ - private $domains = []; - - public function __construct(string $operator, array $domains = []) + public function __construct(private string $operator, private array $domains = []) { - $this->operator = $operator; - $this->setDomains($domains); } public function __clone() @@ -51,9 +49,9 @@ public function __clone() /** * {@inheritdoc} * - * @return DomainInterface[]|Generator + * @return DomainInterface[]|\Generator */ - public function getIterator(): Generator + public function getIterator(): \Generator { foreach ($this->getDomains() as $key => $domain) { yield $key => $domain; @@ -105,7 +103,7 @@ public function toArray(): array private function prepare(): ?DomainInterface { $domains = $this->domains; - $nbDomains = count($domains); + $nbDomains = \count($domains); if (0 === $nbDomains) { return null; @@ -118,7 +116,7 @@ private function prepare(): ?DomainInterface if (self::NOT === $this->operator) { $andX = new self(self::AND, $domains); - return new self($this->operator, [$andX->prepare()]); + return new self($this->operator, array_filter([$andX->prepare()])); } if (2 === $nbDomains) { @@ -136,11 +134,11 @@ private function prepare(): ?DomainInterface } $firstDomain = array_shift($domains); - $subDomain = new self($this->operator, $domains); + $subDomain = new self($this->operator, array_filter($domains)); - return new self($this->operator, [ + return new self($this->operator, array_filter([ $firstDomain, $subDomain->prepare(), - ]); + ])); } public function getOperator(): string @@ -200,16 +198,16 @@ public function remove(DomainInterface $domain): self public function has(DomainInterface $domain): bool { - return in_array($domain, $this->domains, true); + return \in_array($domain, $this->domains, true); } public function count(): int { - return count($this->domains); + return \count($this->domains); } public function isEmpty(): bool { - return 0 === count($this->domains); + return 0 === \count($this->domains); } } diff --git a/src/Expression/Domain/CustomDomain.php b/src/Expression/Domain/CustomDomain.php index 42c7d88..bd1e541 100644 --- a/src/Expression/Domain/CustomDomain.php +++ b/src/Expression/Domain/CustomDomain.php @@ -1,27 +1,31 @@ */ class CustomDomain implements DomainInterface { - /** - * @var array - */ - private $data; + private array $data; public function __construct(array $data = []) { $this->data = $data; } - public function getIterator(): ArrayIterator + public function getIterator(): \ArrayIterator { - return new ArrayIterator($this->toArray()); + return new \ArrayIterator($this->toArray()); } public function toArray(): array diff --git a/src/Expression/Domain/DomainInterface.php b/src/Expression/Domain/DomainInterface.php index 7b28379..bbd24d6 100644 --- a/src/Expression/Domain/DomainInterface.php +++ b/src/Expression/Domain/DomainInterface.php @@ -1,13 +1,20 @@ */ -interface DomainInterface extends IteratorAggregate +interface DomainInterface extends \IteratorAggregate { public function toArray(): array; } diff --git a/src/Expression/Exception/ConversionException.php b/src/Expression/Exception/ConversionException.php index 42deecc..f3ed2c3 100644 --- a/src/Expression/Exception/ConversionException.php +++ b/src/Expression/Exception/ConversionException.php @@ -1,5 +1,14 @@ @@ -45,70 +49,56 @@ public function notX(DomainInterface ...$domains): CompositeDomain /** * Check if the field is EQUAL TO the value. - * - * @param mixed $value */ - public function eq(string $fieldName, $value): Comparison + public function eq(string $fieldName, mixed $value): Comparison { return new Comparison($fieldName, Comparison::EQUAL_TO, $value); } /** * Check if the field is NOT EQUAL TO the value. - * - * @param mixed $value */ - public function neq(string $fieldName, $value): Comparison + public function neq(string $fieldName, mixed $value): Comparison { return new Comparison($fieldName, Comparison::NOT_EQUAL_TO, $value); } /** * Check if the field is UNSET OR EQUAL TO the value. - * - * @param mixed $value */ - public function ueq(string $fieldName, $value): Comparison + public function ueq(string $fieldName, mixed $value): Comparison { return new Comparison($fieldName, Comparison::UNSET_OR_EQUAL_TO, $value); } /** * Check if the field is LESS THAN the value. - * - * @param mixed $value */ - public function lt(string $fieldName, $value): Comparison + public function lt(string $fieldName, mixed $value): Comparison { return new Comparison($fieldName, Comparison::LESS_THAN, $value); } /** * Check if the field is LESS THAN OR EQUAL the value. - * - * @param mixed $value */ - public function lte(string $fieldName, $value): Comparison + public function lte(string $fieldName, mixed $value): Comparison { return new Comparison($fieldName, Comparison::LESS_THAN_OR_EQUAL, $value); } /** * Check if the field is GREATER THAN the value. - * - * @param mixed $value */ - public function gt(string $fieldName, $value): Comparison + public function gt(string $fieldName, mixed $value): Comparison { return new Comparison($fieldName, Comparison::GREATER_THAN, $value); } /** * Check if the field is GREATER THAN OR EQUAL the value. - * - * @param mixed $value */ - public function gte(string $fieldName, $value): Comparison + public function gte(string $fieldName, mixed $value): Comparison { return new Comparison($fieldName, Comparison::GREATER_THAN_OR_EQUAL, $value); } @@ -120,10 +110,8 @@ public function gte(string $fieldName, $value): Comparison * A percent sign % matches any string of zero or more characters. * * If $strict is set to FALSE, the value pattern is "%value%" (automatically wrapped into signs %). - * - * @param mixed $value */ - public function like(string $fieldName, $value, bool $strict = false, bool $caseSensitive = true): Comparison + public function like(string $fieldName, mixed $value, bool $strict = false, bool $caseSensitive = true): Comparison { if ($strict) { $operator = $caseSensitive ? Comparison::EQUAL_LIKE : Comparison::INSENSITIVE_EQUAL_LIKE; @@ -136,10 +124,8 @@ public function like(string $fieldName, $value, bool $strict = false, bool $case /** * Check if the field is IS NOT LIKE the value. - * - * @param mixed $value */ - public function notLike(string $fieldName, $value, bool $caseSensitive = true): Comparison + public function notLike(string $fieldName, mixed $value, bool $caseSensitive = true): Comparison { $operator = $caseSensitive ? Comparison::NOT_LIKE : Comparison::INSENSITIVE_NOT_LIKE; @@ -148,38 +134,32 @@ public function notLike(string $fieldName, $value, bool $caseSensitive = true): /** * Check if the field is IN values list. - * - * @param bool|int|float|string|array $values */ - public function in(string $fieldName, $values): Comparison + public function in(string $fieldName, float|array|bool|int|string $values): Comparison { return new Comparison($fieldName, Comparison::IN, $this->getValues($values)); } /** * Check if the field is NOT IN values list. - * - * @param bool|int|float|string|array $values */ - public function notIn(string $fieldName, $values): Comparison + public function notIn(string $fieldName, float|array|bool|int|string $values): Comparison { return new Comparison($fieldName, Comparison::NOT_IN, $this->getValues($values)); } /** * @internal - * - * @param bool|int|float|string|array $values */ - private function getValues($values): array + private function getValues(float|array|bool|int|string $values): array { - return is_array($values) ? $values : [$values]; + return \is_array($values) ? $values : [$values]; } /** * Adds a new record created from data. * - * @throws InvalidArgumentException when $data is empty + * @throws \InvalidArgumentException when $data is empty */ public function createRecord(array $data): CollectionOperation { @@ -190,12 +170,12 @@ public function createRecord(array $data): CollectionOperation * Updates an existing record of id $id with data. * /!\ Can not be used in record create operation. * - * @throws InvalidArgumentException when $data is empty + * @throws \InvalidArgumentException when $data is empty */ public function updateRecord(int $id, array $data): CollectionOperation { if (!$data) { - throw new InvalidArgumentException('Data cannot be empty'); + throw new \InvalidArgumentException('Data cannot be empty'); } return CollectionOperation::update($id, $data); @@ -246,8 +226,8 @@ public function clearRecords(): CollectionOperation } /** - * @throws InvalidArgumentException when $criteria value is not valid - * @throws ConversionException on data conversion failure + * @throws \InvalidArgumentException when $criteria value is not valid + * @throws ConversionException on data conversion failure */ public function normalizeDomains(iterable $criteria = null): array { @@ -255,13 +235,13 @@ public function normalizeDomains(iterable $criteria = null): array return []; } - if (is_array($criteria)) { + if (\is_array($criteria)) { $normalizedCriteria = $this->andX(); foreach ($criteria as $fieldName => $value) { $comparison = $this->eq($fieldName, $this->formatValue($value)); - if (1 === count($criteria)) { + if (1 === \count($criteria)) { $normalizedCriteria = $comparison; break; } @@ -273,7 +253,7 @@ public function normalizeDomains(iterable $criteria = null): array } if (!$criteria instanceof DomainInterface) { - throw new InvalidArgumentException(sprintf('Expected parameter #1 of type %s|array<%s|array>, %s given', DomainInterface::class, DomainInterface::class, gettype($criteria))); + throw new \InvalidArgumentException(sprintf('Expected parameter #1 of type %s|array<%s|array>, %s given', DomainInterface::class, DomainInterface::class, \gettype($criteria))); } /** @var array $criteriaArray */ @@ -295,19 +275,17 @@ public function normalizeData(array $data = []): array } /** - * @param mixed $value + * @internal * * @throws ConversionException on data conversion failure - * - * @return mixed */ - private function formatValue($value) + private function formatValue(mixed $value): mixed { - if (is_scalar($value)) { + if (\is_scalar($value)) { return $value; } - if (is_array($value) || is_iterable($value)) { + if (\is_array($value) || is_iterable($value)) { $values = []; foreach ($value as $key => $aValue) { @@ -317,7 +295,7 @@ private function formatValue($value) return $values; } - if (is_object($value)) { + if (\is_object($value)) { if ($value instanceof DomainInterface) { return $this->formatValue($value->toArray()); } @@ -326,16 +304,17 @@ private function formatValue($value) return $this->formatValue($value->toArray()); } - if ($value instanceof DateTimeInterface) { + if ($value instanceof \DateTimeInterface) { try { - $date = new DateTime(sprintf('@%s', $value->getTimestamp())); - } catch (Exception $e) { + $date = new \DateTime(sprintf('@%s', $value->getTimestamp())); + } catch (\Exception $e) { throw new ConversionException(sprintf('Failed to convert date from timestamp "%d"', $value->getTimestamp()), 0, $e); } return $date - ->setTimezone(new DateTimeZone('UTC')) - ->format('Y-m-d H:i:s'); + ->setTimezone(new \DateTimeZone('UTC')) + ->format('Y-m-d H:i:s') + ; } } diff --git a/src/Expression/Operation/CollectionOperation.php b/src/Expression/Operation/CollectionOperation.php index 44904e3..1728dfa 100644 --- a/src/Expression/Operation/CollectionOperation.php +++ b/src/Expression/Operation/CollectionOperation.php @@ -1,8 +1,15 @@ @@ -20,50 +27,29 @@ class CollectionOperation implements OperationInterface public const CLEAR = 5; public const REPLACE = 6; - /** - * @var int - */ - private $type; - - /** - * @var int - */ - private $id = 0; - - /** - * @var array|int - */ - private $data = 0; - - /** - * @param array|int $data - */ - public function __construct(int $type, int $id = 0, $data = 0) + public function __construct(private int $type, private int $id = 0, private int|array $data = 0) { - $this->type = $type; - $this->id = $id; - $this->data = $data; } /** - * @throws InvalidArgumentException when data is empty + * @throws \InvalidArgumentException when data is empty */ public static function create(array $data): self { if (!$data) { - throw new InvalidArgumentException('Data cannot be empty'); + throw new \InvalidArgumentException('Data cannot be empty'); } return new self(self::CREATE, 0, $data); } /** - * @throws InvalidArgumentException when data is empty + * @throws \InvalidArgumentException when data is empty */ public static function update(int $id, array $data = []): self { if (!$data) { - throw new InvalidArgumentException('Data cannot be empty'); + throw new \InvalidArgumentException('Data cannot be empty'); } return new self(self::UPDATE, $id, $data); @@ -121,18 +107,12 @@ public function setId(int $id): self return $this; } - /** - * @return array|int - */ - public function getData() + public function getData(): int|array { return $this->data; } - /** - * @param array|int $data - */ - public function setData($data): self + public function setData(int|array $data): self { $this->data = $data; diff --git a/src/Expression/Operation/OperationInterface.php b/src/Expression/Operation/OperationInterface.php index a3bd525..eb070ea 100644 --- a/src/Expression/Operation/OperationInterface.php +++ b/src/Expression/Operation/OperationInterface.php @@ -1,5 +1,14 @@ connection = $connection; - $this->timeOut = $timeOut; } public function request(string $service, string $method, array $arguments = []): array diff --git a/src/Transport/TransportInterface.php b/src/Transport/TransportInterface.php index 9f16788..0f19aad 100644 --- a/src/Transport/TransportInterface.php +++ b/src/Transport/TransportInterface.php @@ -1,5 +1,14 @@ assertEquals(['foo', '=', 'bar'], $comparison->toArray()); + static::assertSame(['foo', '=', 'bar'], $comparison->toArray()); } } diff --git a/tests/Expression/CompositeDomainTest.php b/tests/Expression/CompositeDomainTest.php index 1974cbc..be62b7d 100644 --- a/tests/Expression/CompositeDomainTest.php +++ b/tests/Expression/CompositeDomainTest.php @@ -1,24 +1,34 @@ assertEquals($expectedResult, $domain->toArray(), $message); + static::assertSame($expectedResult, $domain->toArray(), $message); } /** @@ -138,7 +149,8 @@ protected function createFakeDomain($expression): DomainInterface $fakeDomain = $this->createMock(DomainInterface::class); $fakeDomain ->method('toArray') - ->willReturn(is_array($expression) ? $expression : (array) $expression); + ->willReturn(\is_array($expression) ? $expression : (array) $expression) + ; return $fakeDomain; } diff --git a/tests/Utils/Debugger.php b/tests/Utils/Debugger.php index 92de05f..1a58036 100644 --- a/tests/Utils/Debugger.php +++ b/tests/Utils/Debugger.php @@ -1,5 +1,14 @@ class->getShortName(), $propertyName ); @@ -121,10 +128,11 @@ public function assertHasser(string $propertyName, $value, array $context = []): return $this->assertPropertyMethod($propertyName, self::ACCESSOR_HAS, $context); } - public function assertGetter(string $propertyName, array $context = []): ?ReflectionMethod + public function assertGetter(string $propertyName, array $context = []): ?\ReflectionMethod { - $context[self::IS_FLUENT] = $context[self::IS_FLUENT] ?? false; - $context[self::MESSAGE] = sprintf('Asserting getter for property %s::$%s', + $context[self::IS_FLUENT] ??= false; + $context[self::MESSAGE] = sprintf( + 'Asserting getter for property %s::$%s', $this->class->getShortName(), $propertyName ); @@ -135,11 +143,12 @@ public function assertGetter(string $propertyName, array $context = []): ?Reflec /** * @param mixed $value */ - public function assertAdder(string $propertyName, $value, array $context = []): ?ReflectionMethod + public function assertAdder(string $propertyName, $value, array $context = []): ?\ReflectionMethod { $context[self::VALUE] = $value; - $context[self::IS_FLUENT] = $context[self::IS_FLUENT] ?? true; - $context[self::MESSAGE] = sprintf('Asserting adder for property %s::$%s', + $context[self::IS_FLUENT] ??= true; + $context[self::MESSAGE] = sprintf( + 'Asserting adder for property %s::$%s', $this->class->getShortName(), $propertyName ); @@ -150,11 +159,12 @@ public function assertAdder(string $propertyName, $value, array $context = []): /** * @param mixed $value */ - public function assertRemover(string $propertyName, $value, array $context = []): ?ReflectionMethod + public function assertRemover(string $propertyName, $value, array $context = []): ?\ReflectionMethod { $context[self::VALUE] = $value; - $context[self::IS_FLUENT] = $context[self::IS_FLUENT] ?? true; - $context[self::MESSAGE] = sprintf('Asserting remover for property %s::$%s', + $context[self::IS_FLUENT] ??= true; + $context[self::MESSAGE] = sprintf( + 'Asserting remover for property %s::$%s', $this->class->getShortName(), $propertyName ); @@ -167,11 +177,12 @@ public function assertRemover(string $propertyName, $value, array $context = []) * * @param mixed $value */ - public function assertSetter(string $propertyName, $value = null, array $context = []): ?ReflectionMethod + public function assertSetter(string $propertyName, $value = null, array $context = []): ?\ReflectionMethod { $context[self::VALUE] = $value; - $context[self::IS_FLUENT] = $context[self::IS_FLUENT] ?? true; - $context[self::MESSAGE] = sprintf('Asserting setter for property %s::$%s', + $context[self::IS_FLUENT] ??= true; + $context[self::MESSAGE] = sprintf( + 'Asserting setter for property %s::$%s', $this->class->getShortName(), $propertyName ); @@ -182,13 +193,13 @@ public function assertSetter(string $propertyName, $value = null, array $context /** * Test and return the specific method of a property. */ - public function assertPropertyMethod(string $propertyName, string $prefix, array $context = []): ?ReflectionMethod + public function assertPropertyMethod(string $propertyName, string $prefix, array $context = []): ?\ReflectionMethod { $context = $this->getContext($context); try { $property = $this->reflector->getProperty($this->class, $propertyName); - } catch (ReflectionException $e) { + } catch (\ReflectionException $e) { $this->testCase::fail($this->getContextErrorMessage('The property was not found', $context)); return null; @@ -216,7 +227,7 @@ public function assertPropertyMethod(string $propertyName, string $prefix, array try { $method = $class->getMethod($methodName); - } catch (ReflectionException $e) { + } catch (\ReflectionException $e) { $errorMessage = sprintf('None of methods "%s()" was found', implode('"(), "', $tested)); $this->testCase::fail($this->getContextErrorMessage($errorMessage, $context)); @@ -230,9 +241,10 @@ public function assertPropertyMethod(string $propertyName, string $prefix, array $result = $method->invokeArgs($this->object, $args); if ((bool) ($context[self::IS_FLUENT] ?? false)) { - $this->testCase::assertEquals($this->object, $result, $this->getContextErrorMessage(sprintf( - 'The method is fluent and should return the object instance' - ), $context)); + $this->testCase::assertEquals($this->object, $result, $this->getContextErrorMessage( + 'The method is fluent and should return the object instance', + $context + )); return $method; } @@ -276,7 +288,8 @@ public function assertPropertyMethod(string $propertyName, string $prefix, array switch ($prefix) { case self::ACCESSOR_HAS: $this->testCase::assertEquals($result, $hasValue, $this->getContextErrorMessage( - sprintf('The property collection %s the value but the hasser returns %s', + sprintf( + 'The property collection %s the value but the hasser returns %s', $hasValue ? 'contains' : 'does not contain', $this->debugger->debugBool($result) ), @@ -299,7 +312,7 @@ public function assertPropertyMethod(string $propertyName, string $prefix, array return $method; } - break; + break; } return $method; @@ -341,7 +354,7 @@ public function getObject(): object } /** - * @throws ReflectionException + * @throws \ReflectionException */ public function setObject(object $object): self { @@ -351,7 +364,7 @@ public function setObject(object $object): self return $this; } - public function getClass(): ReflectionClass + public function getClass(): \ReflectionClass { return $this->class; } diff --git a/tests/Utils/Reflector.php b/tests/Utils/Reflector.php index 0617bd7..9e26068 100644 --- a/tests/Utils/Reflector.php +++ b/tests/Utils/Reflector.php @@ -1,18 +1,22 @@ getProperty($object, $propertyName); @@ -36,9 +40,9 @@ public function setObjectValue(object $object, string $propertyName, $value): Re /** * @param object|string $objectOrClass * - * @throws ReflectionException + * @throws \ReflectionException */ - public function getMethod($objectOrClass, string $methodName, bool $setAccessible = true): ?ReflectionMethod + public function getMethod($objectOrClass, string $methodName, bool $setAccessible = true): ?\ReflectionMethod { $class = $this->getClass($objectOrClass); $method = $class->getMethod($methodName); @@ -53,9 +57,9 @@ public function getMethod($objectOrClass, string $methodName, bool $setAccessibl /** * @param object|string $objectOrClass * - * @throws ReflectionException + * @throws \ReflectionException */ - public function getProperty($objectOrClass, string $propertyName, bool $setAccessible = true): ?ReflectionProperty + public function getProperty($objectOrClass, string $propertyName, bool $setAccessible = true): ?\ReflectionProperty { $class = $this->getClass($objectOrClass); $property = $class->getProperty($propertyName); @@ -70,17 +74,17 @@ public function getProperty($objectOrClass, string $propertyName, bool $setAcces /** * @param object|string $objectOrClass * - * @throws ReflectionException + * @throws \ReflectionException */ - public function getClass($objectOrClass): ReflectionClass + public function getClass($objectOrClass): \ReflectionClass { - if (is_string($objectOrClass)) { - /* @var class-string $class */ + if (\is_string($objectOrClass)) { + /** @var class-string $class */ $class = $objectOrClass; - return new ReflectionClass($class); + return new \ReflectionClass($class); } - return $objectOrClass instanceof ReflectionClass ? $objectOrClass : new ReflectionClass($objectOrClass); + return $objectOrClass instanceof \ReflectionClass ? $objectOrClass : new \ReflectionClass($objectOrClass); } } diff --git a/tests/Utils/TestDecorator.php b/tests/Utils/TestDecorator.php index f7d8ace..266beac 100644 --- a/tests/Utils/TestDecorator.php +++ b/tests/Utils/TestDecorator.php @@ -1,12 +1,18 @@ Date: Thu, 2 Feb 2023 14:27:04 +0100 Subject: [PATCH 34/80] DBAL feature --- README.md | 3 +- UPGRADE-8.0.md | 6 +- docs/client/expression_builder.md | 2 +- src/Client.php | 2 +- .../Expression/Domain/Comparison.php | 2 +- .../Expression/Domain/CompositeDomain.php | 2 +- .../Expression/Domain/CustomDomain.php | 2 +- .../Expression/Domain/DomainInterface.php | 2 +- .../Exception/ConversionException.php | 2 +- .../Expression/ExpressionBuilder.php | 16 +- .../Operation/CollectionOperation.php | 2 +- .../Operation/OperationInterface.php | 2 +- src/DBAL/Query/AbstractQuery.php | 103 ++++ src/DBAL/Query/NativeQuery.php | 16 + src/DBAL/Query/NoResultException.php | 20 + src/DBAL/Query/NoUniqueResultException.php | 20 + src/DBAL/Query/OrmQuery.php | 193 +++++++ src/DBAL/Query/QueryBuilder.php | 485 ++++++++++++++++++ src/DBAL/Query/QueryException.php | 16 + src/DBAL/Query/QueryInterface.php | 23 + src/DBAL/RecordManager.php | 291 +++++++++++ .../Repository/RecordNotFoundException.php | 30 ++ src/DBAL/Repository/RecordRepository.php | 254 +++++++++ src/DBAL/Schema/Choice.php | 38 ++ src/DBAL/Schema/Field.php | 204 ++++++++ src/DBAL/Schema/Model.php | 132 +++++ src/DBAL/Schema/Schema.php | 126 +++++ src/DBAL/Schema/SchemaException.php | 25 + src/DBAL/Schema/Selection.php | 79 +++ src/Transport/JsonRpcPhpStreamTransport.php | 3 +- 30 files changed, 2077 insertions(+), 24 deletions(-) rename src/{ => DBAL}/Expression/Domain/Comparison.php (98%) rename src/{ => DBAL}/Expression/Domain/CompositeDomain.php (98%) rename src/{ => DBAL}/Expression/Domain/CustomDomain.php (93%) rename src/{ => DBAL}/Expression/Domain/DomainInterface.php (87%) rename src/{ => DBAL}/Expression/Exception/ConversionException.php (85%) rename src/{ => DBAL}/Expression/ExpressionBuilder.php (95%) rename src/{ => DBAL}/Expression/Operation/CollectionOperation.php (97%) rename src/{ => DBAL}/Expression/Operation/OperationInterface.php (85%) create mode 100644 src/DBAL/Query/AbstractQuery.php create mode 100644 src/DBAL/Query/NativeQuery.php create mode 100644 src/DBAL/Query/NoResultException.php create mode 100644 src/DBAL/Query/NoUniqueResultException.php create mode 100644 src/DBAL/Query/OrmQuery.php create mode 100644 src/DBAL/Query/QueryBuilder.php create mode 100644 src/DBAL/Query/QueryException.php create mode 100644 src/DBAL/Query/QueryInterface.php create mode 100644 src/DBAL/RecordManager.php create mode 100644 src/DBAL/Repository/RecordNotFoundException.php create mode 100644 src/DBAL/Repository/RecordRepository.php create mode 100644 src/DBAL/Schema/Choice.php create mode 100644 src/DBAL/Schema/Field.php create mode 100644 src/DBAL/Schema/Model.php create mode 100644 src/DBAL/Schema/Schema.php create mode 100644 src/DBAL/Schema/SchemaException.php create mode 100644 src/DBAL/Schema/Selection.php diff --git a/README.md b/README.md index 816c3a6..c5fc066 100644 --- a/README.md +++ b/README.md @@ -535,8 +535,7 @@ For example, let's create the repository for your companies and define a query t ```php namespace App\Odoo\Repository; -use Ang3\Component\Odoo\DBAL\RecordManager; -use Ang3\Component\Odoo\DBAL\Repository\RecordRepository; +use Ang3\Component\Odoo\DBAL\RecordManager;use Ang3\Component\Odoo\DBAL\Repository\RecordRepository; class CompanyRepository extends RecordRepository { diff --git a/UPGRADE-8.0.md b/UPGRADE-8.0.md index 06793e7..ef949e7 100644 --- a/UPGRADE-8.0.md +++ b/UPGRADE-8.0.md @@ -8,17 +8,17 @@ Global - The PHP extension ```php-xmlrpc``` is not used anymore. - The PHP extension ```php-json``` is now required. -- DBAL features was moved to the package [ang3/php-odoo-dbal](...) - - You must install it to be able to use query features. +- Github workflows Client ------ - Renamed method ```Client::create()``` to ```Client::insert()``` - Renamed static method ```Client::createFromConfig()``` to ```Client::create()``` +- All methods fixed and tested. Expression builder ------------------ -- New folder/namespace architecture. +- Domain classes moved from `Ang3\Component\Odoo\DBAL\Expression` to `Ang3\Component\Odoo\DBAL\Expression\Domain` - Domain expressions fixes. \ No newline at end of file diff --git a/docs/client/expression_builder.md b/docs/client/expression_builder.md index f3b3f6c..c191b9d 100644 --- a/docs/client/expression_builder.md +++ b/docs/client/expression_builder.md @@ -16,7 +16,7 @@ $expr = $client->getExpressionBuilder(); You can still use the expression builder as standalone by creating a new instance: ```php -use Ang3\Component\Odoo\Expression\ExpressionBuilder; +use Ang3\Component\Odoo\DBAL\Expression\ExpressionBuilder; $expr = new ExpressionBuilder(); ``` diff --git a/src/Client.php b/src/Client.php index f9e1aad..6b7dc45 100644 --- a/src/Client.php +++ b/src/Client.php @@ -11,11 +11,11 @@ namespace Ang3\Component\Odoo; +use Ang3\Component\Odoo\DBAL\Expression\ExpressionBuilder; use Ang3\Component\Odoo\Exception\AuthenticationException; use Ang3\Component\Odoo\Exception\MissingConfigParameterException; use Ang3\Component\Odoo\Exception\RemoteException; use Ang3\Component\Odoo\Exception\RequestException; -use Ang3\Component\Odoo\Expression\ExpressionBuilder; use Ang3\Component\Odoo\Transport\JsonRpcPhpStreamTransport; use Ang3\Component\Odoo\Transport\TransportInterface; use Psr\Log\LoggerInterface; diff --git a/src/Expression/Domain/Comparison.php b/src/DBAL/Expression/Domain/Comparison.php similarity index 98% rename from src/Expression/Domain/Comparison.php rename to src/DBAL/Expression/Domain/Comparison.php index 93cb51c..085de8c 100644 --- a/src/Expression/Domain/Comparison.php +++ b/src/DBAL/Expression/Domain/Comparison.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Expression\Domain; +namespace Ang3\Component\Odoo\DBAL\Expression\Domain; /** * @author Joanis ROUANET diff --git a/src/Expression/Domain/CompositeDomain.php b/src/DBAL/Expression/Domain/CompositeDomain.php similarity index 98% rename from src/Expression/Domain/CompositeDomain.php rename to src/DBAL/Expression/Domain/CompositeDomain.php index 6297af4..0f87611 100644 --- a/src/Expression/Domain/CompositeDomain.php +++ b/src/DBAL/Expression/Domain/CompositeDomain.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Expression\Domain; +namespace Ang3\Component\Odoo\DBAL\Expression\Domain; /** * @author Joanis ROUANET diff --git a/src/Expression/Domain/CustomDomain.php b/src/DBAL/Expression/Domain/CustomDomain.php similarity index 93% rename from src/Expression/Domain/CustomDomain.php rename to src/DBAL/Expression/Domain/CustomDomain.php index bd1e541..8a28627 100644 --- a/src/Expression/Domain/CustomDomain.php +++ b/src/DBAL/Expression/Domain/CustomDomain.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Expression\Domain; +namespace Ang3\Component\Odoo\DBAL\Expression\Domain; /** * @author Joanis ROUANET diff --git a/src/Expression/Domain/DomainInterface.php b/src/DBAL/Expression/Domain/DomainInterface.php similarity index 87% rename from src/Expression/Domain/DomainInterface.php rename to src/DBAL/Expression/Domain/DomainInterface.php index bbd24d6..94fbde3 100644 --- a/src/Expression/Domain/DomainInterface.php +++ b/src/DBAL/Expression/Domain/DomainInterface.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Expression\Domain; +namespace Ang3\Component\Odoo\DBAL\Expression\Domain; /** * @author Joanis ROUANET diff --git a/src/Expression/Exception/ConversionException.php b/src/DBAL/Expression/Exception/ConversionException.php similarity index 85% rename from src/Expression/Exception/ConversionException.php rename to src/DBAL/Expression/Exception/ConversionException.php index f3ed2c3..514ff45 100644 --- a/src/Expression/Exception/ConversionException.php +++ b/src/DBAL/Expression/Exception/ConversionException.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Expression\Exception; +namespace Ang3\Component\Odoo\DBAL\Expression\Exception; /** * @author Joanis ROUANET diff --git a/src/Expression/ExpressionBuilder.php b/src/DBAL/Expression/ExpressionBuilder.php similarity index 95% rename from src/Expression/ExpressionBuilder.php rename to src/DBAL/Expression/ExpressionBuilder.php index 70f04e5..ac2bf99 100644 --- a/src/Expression/ExpressionBuilder.php +++ b/src/DBAL/Expression/ExpressionBuilder.php @@ -9,14 +9,14 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Expression; - -use Ang3\Component\Odoo\Expression\Domain\Comparison; -use Ang3\Component\Odoo\Expression\Domain\CompositeDomain; -use Ang3\Component\Odoo\Expression\Domain\DomainInterface; -use Ang3\Component\Odoo\Expression\Exception\ConversionException; -use Ang3\Component\Odoo\Expression\Operation\CollectionOperation; -use Ang3\Component\Odoo\Expression\Operation\OperationInterface; +namespace Ang3\Component\Odoo\DBAL\Expression; + +use Ang3\Component\Odoo\DBAL\Expression\Domain\Comparison; +use Ang3\Component\Odoo\DBAL\Expression\Domain\CompositeDomain; +use Ang3\Component\Odoo\DBAL\Expression\Domain\DomainInterface; +use Ang3\Component\Odoo\DBAL\Expression\Exception\ConversionException; +use Ang3\Component\Odoo\DBAL\Expression\Operation\CollectionOperation; +use Ang3\Component\Odoo\DBAL\Expression\Operation\OperationInterface; /** * @author Joanis ROUANET diff --git a/src/Expression/Operation/CollectionOperation.php b/src/DBAL/Expression/Operation/CollectionOperation.php similarity index 97% rename from src/Expression/Operation/CollectionOperation.php rename to src/DBAL/Expression/Operation/CollectionOperation.php index 1728dfa..f402927 100644 --- a/src/Expression/Operation/CollectionOperation.php +++ b/src/DBAL/Expression/Operation/CollectionOperation.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Expression\Operation; +namespace Ang3\Component\Odoo\DBAL\Expression\Operation; /** * @author Joanis ROUANET diff --git a/src/Expression/Operation/OperationInterface.php b/src/DBAL/Expression/Operation/OperationInterface.php similarity index 85% rename from src/Expression/Operation/OperationInterface.php rename to src/DBAL/Expression/Operation/OperationInterface.php index eb070ea..99db7d9 100644 --- a/src/Expression/Operation/OperationInterface.php +++ b/src/DBAL/Expression/Operation/OperationInterface.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Expression\Operation; +namespace Ang3\Component\Odoo\DBAL\Expression\Operation; /** * @author Joanis ROUANET diff --git a/src/DBAL/Query/AbstractQuery.php b/src/DBAL/Query/AbstractQuery.php new file mode 100644 index 0000000..c10cd8b --- /dev/null +++ b/src/DBAL/Query/AbstractQuery.php @@ -0,0 +1,103 @@ +name; + } + + public function setName(string $name): static + { + $this->name = $name; + + return $this; + } + + public function getMethod(): string + { + return $this->method; + } + + public function setMethod(string $method): static + { + $this->method = $method; + + return $this; + } + + public function getParameters(): array + { + return $this->parameters; + } + + public function setParameters(array $parameters = []): static + { + $this->parameters = $parameters; + + return $this; + } + + public function getOptions(): array + { + return $this->options; + } + + public function setOptions(array $options = []): static + { + $this->options = $options; + + return $this; + } + + /** + * Add an option on the query. + */ + public function addOption(string $name, mixed $value): static + { + $this->options[$name] = $value; + + return $this; + } + + /** + * Execute the query. + * Allowed methods: all. + */ + public function execute(): mixed + { + return $this->recordManager->executeQuery($this); + } + + /** + * Gets the related manager of the query. + */ + public function getRecordManager(): RecordManager + { + return $this->recordManager; + } +} diff --git a/src/DBAL/Query/NativeQuery.php b/src/DBAL/Query/NativeQuery.php new file mode 100644 index 0000000..b164e72 --- /dev/null +++ b/src/DBAL/Query/NativeQuery.php @@ -0,0 +1,16 @@ +method = $method; + + return $this; + } + + /** + * Counts the number of records from parameters. + * Allowed methods: SEARCH, SEARCH_READ. + * + * @throws QueryException on invalid query method + */ + public function count(): int + { + if (!\in_array($this->method, [self::SEARCH, self::SEARCH_READ], true)) { + throw new QueryException(sprintf('You can count results with method "%s" and "%s" only.', self::SEARCH, self::SEARCH_READ)); + } + + $query = new self($this->recordManager, $this->name, self::SEARCH_COUNT); + $query->setParameters($this->recordManager->getExpressionBuilder()->normalizeDomains($this->parameters)); + + return (int) $query->execute(); + } + + /** + * Gets just ONE scalar result. + * Allowed methods: SEARCH, SEARCH_READ. + * + * @throws NoUniqueResultException on no unique result + * @throws NoResultException on no result + * @throws QueryException on invalid query method + */ + public function getSingleScalarResult(): float|bool|int|string + { + $result = $this->getOneOrNullScalarResult(); + + if (!$result) { + throw new NoResultException(); + } + + return $result; + } + + /** + * Gets one or NULL scalar result. + * Allowed methods: SEARCH, SEARCH_READ. + * + * @throws NoUniqueResultException on no unique result + * @throws QueryException on invalid query method + */ + public function getOneOrNullScalarResult(): float|bool|int|string|null + { + $result = $this->getScalarResult(); + + if (\count($result) > 1) { + throw new NoUniqueResultException(); + } + + return array_shift($result); + } + + /** + * Gets a list of scalar result. + * Allowed methods: SEARCH, SEARCH_READ. + * + * @return array + * + * @throws QueryException on invalid query method + */ + public function getScalarResult(): array + { + $result = $this->getResult(); + + if (self::SEARCH === $this->method) { + return $result; + } + + $selectedFields = $this->options['fields'] ?? []; + if (\count($selectedFields) > 1) { + throw new QueryException('More than one field selected.'); + } + + $selectedFieldName = $selectedFields[0] ?? 'id'; + + foreach ($result as $key => $value) { + $result[$key] = $value[$selectedFieldName] ?? null; + } + + return $result; + } + + /** + * Gets one row. + * Allowed methods: SEARCH, SEARCH_READ. + * + * @throws NoUniqueResultException on no unique result + * @throws NoResultException on no result + * @throws QueryException on invalid query method + */ + public function getSingleResult(): array + { + $result = $this->getOneOrNullResult(); + $result = \is_array($result) ? array_shift($result) : null; + + if (!$result) { + throw new NoResultException(); + } + + return $result; + } + + /** + * Gets one or NULL row. + * Allowed methods: SEARCH, SEARCH_READ. + * + * @throws NoUniqueResultException on no unique result + * @throws QueryException on invalid query method + */ + public function getOneOrNullResult(): ?array + { + $result = $this->getResult(); + + if (\count($result) > 1) { + throw new NoUniqueResultException(); + } + + return array_shift($result); + } + + /** + * Gets all result rows. + * Allowed methods: SEARCH, SEARCH_READ. + * + * @throws QueryException on invalid query method + */ + public function getResult(): array + { + if (!\in_array($this->method, [self::SEARCH, self::SEARCH_READ], true)) { + throw new QueryException(sprintf('You can get results with methods "%s" and "%s" only.', self::SEARCH, self::SEARCH_READ)); + } + + return (array) $this->execute(); + } +} diff --git a/src/DBAL/Query/QueryBuilder.php b/src/DBAL/Query/QueryBuilder.php new file mode 100644 index 0000000..6295ab1 --- /dev/null +++ b/src/DBAL/Query/QueryBuilder.php @@ -0,0 +1,485 @@ +type = self::SELECT; + $this->select = []; + $this->values = []; + $this->ids = []; + + $fields = $fields ? (array) $fields : []; + + foreach ($fields as $fieldName) { + $this->addSelect($fieldName); + } + + return $this; + } + + /** + * Defines the query of type "SEARCH". + */ + public function search(): self + { + $this->type = self::SEARCH; + $this->select = []; + $this->values = []; + $this->ids = []; + + return $this; + } + + /** + * Defines the query of type "INSERT". + */ + public function insert(): self + { + $this->type = self::INSERT; + $this->select = []; + $this->ids = []; + $this->where = null; + + return $this; + } + + /** + * Defines the query of type "UPDATE" with ids of records to update. + * + * @param int[] $ids + */ + public function update(array $ids): self + { + $this->type = self::UPDATE; + $this->select = []; + + return $this->setIds($ids); + } + + /** + * Defines the query of type "DELETE" with ids of records to delete. + */ + public function delete(array $ids): self + { + $this->type = self::DELETE; + $this->select = []; + $this->values = []; + + return $this->setIds($ids); + } + + /** + * Adds a field to select. + * + * @throws QueryException when the type of the query is not "SELECT" + */ + public function addSelect(string $fieldName): self + { + if (self::SELECT !== $this->type) { + throw new QueryException('You can select fields in query of type "SELECT" only.'); + } + + if (!\in_array($fieldName, $this->select, true)) { + $this->select[] = $fieldName; + } + + return $this; + } + + /** + * Gets selected fields. + */ + public function getSelect(): array + { + return $this->select; + } + + /** + * Sets the target model name. + */ + public function from(string $modelName): self + { + $this->from = $modelName; + + return $this; + } + + /** + * Gets the target model name of the query. + */ + public function getFrom(): ?string + { + return $this->from; + } + + /** + * Sets target IDs in case of query of type "UPDATE" or "DELETE". + * + * @throws QueryException when the type of the query is not "UPDATE" nor "DELETE" + */ + public function setIds(array $ids): self + { + $this->ids = []; + + foreach ($ids as $id) { + $this->addId($id); + } + + return $this; + } + + /** + * Adds target ID in case of query of type "UPDATE" or "DELETE". + * + * @throws QueryException when the type of the query is not "UPDATE" nor "DELETE" + */ + public function addId(int $id): self + { + if (!\in_array($this->type, [self::UPDATE, self::DELETE], true)) { + throw new QueryException('You can set indexes in query of type "UPDATE" or "DELETE" only.'); + } + + if (!\in_array($id, $this->ids, true)) { + $this->ids[] = $id; + } + + return $this; + } + + /** + * Sets field values in case of query of type "INSERT" or "UPDATE". + * + * @throws QueryException when the type of the query is not "INSERT" nor "UPDATE" + */ + public function setValues(array $values = []): self + { + $this->values = []; + + foreach ($values as $fieldName => $value) { + $this->set($fieldName, $value); + } + + return $this; + } + + /** + * Set a field value in case of query of type "INSERT" or "UPDATE". + * + * @param mixed $value + * + * @throws QueryException when the type of the query is not "INSERT" nor "UPDATE" + */ + public function set(string $fieldName, $value): self + { + if (!\in_array($this->type, [self::INSERT, self::UPDATE], true)) { + throw new QueryException('You can set values in query of type "INSERT" or "UPDATE" only.'); + } + + $this->values[$fieldName] = $value; + + return $this; + } + + /** + * Gets field values set in case of query of type "INSERT" or "UPDATE". + */ + public function getValues(): array + { + return $this->values; + } + + /** + * Sets criteria for queries of type "SELECT" and "SEARCH". + * + * @throws QueryException when the type of the query is not "SELECT" not "SEARCH" + */ + public function where(?DomainInterface $domain = null): self + { + $this->assertSupportsWhereClause(); + $this->where = $domain; + + return $this; + } + + /** + * Takes the WHERE clause and adds a node with logical operator AND. + * + * @throws QueryException when the type of the query is not "SELECT" nor "SEARCH" + */ + public function andWhere(DomainInterface $domain): self + { + $this->assertSupportsWhereClause(); + $this->where = $this->where ? $this->expr()->andX($this->where, $domain) : $domain; + + return $this; + } + + /** + * Takes the WHERE clause and adds a node with logical operator OR. + * + * @throws QueryException when the type of the query is not "SELECT" nor "SEARCH" + */ + public function orWhere(DomainInterface $domain): self + { + $this->assertSupportsWhereClause(); + $this->where = $this->where ? $this->expr()->orX($this->where, $domain) : $domain; + + return $this; + } + + /** + * Gets the WHERE clause. + */ + public function getWhere(): ?DomainInterface + { + return $this->where; + } + + /** + * @internal + * + * @throws QueryException when the type of the query is not "SELECT" nor "SEARCH" + */ + private function assertSupportsWhereClause(): void + { + if (!\in_array($this->type, [self::SELECT, self::SEARCH], true)) { + throw new QueryException('You can set criteria in query of type "SELECT" or "SEARCH" only.'); + } + } + + /** + * Sets orders. + */ + public function setOrders(array $orders = []): self + { + $this->orders = []; + + foreach ($orders as $fieldName => $isAsc) { + $this->addOrderBy($fieldName, $isAsc); + } + + return $this; + } + + /** + * Clears orders and adds one. + */ + public function orderBy(string $fieldName, bool $isAsc = true): self + { + $this->orders = []; + + return $this->addOrderBy($fieldName, $isAsc); + } + + /** + * Adds order. + * + * @throws QueryException when the query type is not valid + */ + public function addOrderBy(string $fieldName, bool $isAsc = true): self + { + if (!\in_array($this->type, [self::SELECT, self::SEARCH], true)) { + throw new QueryException('You can set orders in query of type "SELECT", "SEARCH" only.'); + } + + $this->orders[$fieldName] = $isAsc; + + return $this; + } + + /** + * Gets ordered fields. + */ + public function getOrders(): array + { + return $this->orders; + } + + /** + * Sets the max results of the query (limit). + */ + public function setMaxResults(?int $maxResults): self + { + $this->maxResults = $maxResults; + + return $this; + } + + /** + * Gets the max results of the query. + */ + public function getMaxResults(): ?int + { + return $this->maxResults; + } + + /** + * Sets the first results of the query (offset). + */ + public function setFirstResult(?int $firstResult): self + { + $this->firstResult = $firstResult; + + return $this; + } + + /** + * Gets the first results of the query. + */ + public function getFirstResult(): ?int + { + return $this->firstResult; + } + + /** + * Computes and returns the query. + * + * @throws QueryException on invalid query + * @throws ConversionException on data conversion failure + */ + public function getQuery(): OrmQuery + { + $method = match ($this->type) { + self::SELECT => OrmQuery::SEARCH_READ, + self::SEARCH => OrmQuery::SEARCH, + self::INSERT => OrmQuery::CREATE, + self::UPDATE => OrmQuery::WRITE, + self::DELETE => OrmQuery::UNLINK, + default => throw new \InvalidArgumentException(sprintf('The query type "%s" is not valid.', $this->type)), + }; + + $query = new OrmQuery($this->recordManager, $this->from, $method); + + if (\in_array($this->type, [self::SELECT, self::SEARCH], true)) { + $parameters = $this->expr()->normalizeDomains($this->where); + } elseif (self::DELETE === $this->type) { + if (!$this->ids) { + throw new QueryException('You must set indexes for queries of type "DELETE".'); + } + + $parameters = [$this->ids]; + } else { + if (!$this->values) { + throw new QueryException('You must set values for queries of type "INSERT" and "UPDATE".'); + } + + $parameters = $this->expr()->normalizeData($this->values); + + if (self::UPDATE === $this->type) { + if (!$this->ids) { + throw new QueryException('You must set indexes for queries of type "UPDATE".'); + } + + $parameters = [$this->ids, $parameters]; + } + } + + $query->setParameters($parameters); + + if (\in_array($this->type, [self::SELECT, self::SEARCH], true)) { + $options = []; + + if (self::SELECT === $this->type && $this->select) { + $options['fields'] = $this->select; + } + + $orders = $this->orders; + + if ($orders) { + foreach ($orders as $fieldName => $isAsc) { + $orders[$fieldName] = sprintf('%s %s', $fieldName, $isAsc ? 'asc' : 'desc'); + } + + $options['order'] = implode(', ', $orders); + } + + if ($this->firstResult) { + $options['offset'] = $this->firstResult; + } + + if ($this->maxResults) { + $options['limit'] = $this->maxResults; + } + + $query->setOptions($options); + } + + return $query; + } + + /** + * Gets the type of the query. + */ + public function getType(): string + { + return $this->type; + } + + /** + * Gets the related manager of the query. + */ + public function getRecordManager(): RecordManager + { + return $this->recordManager; + } + + /** + * Shortcut to the expression builder of the related client. + */ + public function expr(): ExpressionBuilder + { + return $this->recordManager->getExpressionBuilder(); + } +} diff --git a/src/DBAL/Query/QueryException.php b/src/DBAL/Query/QueryException.php new file mode 100644 index 0000000..e1c3ad9 --- /dev/null +++ b/src/DBAL/Query/QueryException.php @@ -0,0 +1,16 @@ +schema = new Schema($client); + $this->expressionBuilder = new ExpressionBuilder(); + } + + /** + * Create a new record. + * + * @return int the ID of the new record + */ + public function create(string $modelName, array $data): int + { + return $this + ->getRepository($modelName) + ->insert($data) + ; + } + + /** + * Update record(s). + * + * NB: It is not currently possible to perform “computed” updates (by criteria). + * To do it, you have to perform a search then an update with search result IDs. + * + * @param array|int $ids + */ + public function update(string $modelName, $ids, array $data = []): void + { + $this + ->getRepository($modelName) + ->update($ids, $data) + ; + } + + /** + * Delete record(s). + * + * NB: It is not currently possible to perform “computed” deletes (by criteria). + * To do it, you have to perform a search then a delete with search result IDs. + * + * @param array|int $ids + */ + public function delete(string $modelName, $ids): void + { + $this + ->getRepository($modelName) + ->delete($ids) + ; + } + + /** + * Search one ID of record by criteria. + */ + public function searchOne(string $modelName, ?DomainInterface $criteria): ?int + { + return $this + ->getRepository($modelName) + ->searchOne($criteria) + ; + } + + /** + * Search all ID of record(s). + * + * @return int[] + */ + public function searchAll(string $modelName, array $orders = [], int $limit = null, int $offset = null): array + { + return $this + ->getRepository($modelName) + ->searchAll($orders, $limit, $offset) + ; + } + + /** + * Search ID of record(s) by criteria. + * + * @return int[] + */ + public function search(string $modelName, ?DomainInterface $criteria = null, array $orders = [], int $limit = null, int $offset = null): array + { + return $this + ->getRepository($modelName) + ->search($criteria, $orders, $limit, $offset) + ; + } + + /** + * Find ONE record by ID. + * + * @throws RecordNotFoundException when the record was not found + */ + public function read(string $modelName, int $id, array $fields = []): array + { + return $this + ->getRepository($modelName) + ->read($id, $fields) + ; + } + + /** + * Find ONE record by ID. + */ + public function find(string $modelName, int $id, array $fields = []): ?array + { + return $this + ->getRepository($modelName) + ->find($id, $fields) + ; + } + + /** + * Find ONE record by criteria. + */ + public function findOneBy(string $modelName, ?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $offset = null): ?array + { + return $this + ->getRepository($modelName) + ->findOneBy($criteria, $fields, $orders, $offset) + ; + } + + /** + * Find all records. + * + * @return array[] + */ + public function findAll(string $modelName, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array + { + return $this + ->getRepository($modelName) + ->findAll($fields, $orders, $limit, $offset) + ; + } + + /** + * Find record(s) by criteria. + * + * @return array[] + */ + public function findBy(string $modelName, ?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array + { + return $this + ->getRepository($modelName) + ->findBy($criteria, $fields, $orders, $limit, $offset) + ; + } + + /** + * Check if a record exists. + */ + public function exists(string $modelName, int $id): bool + { + return $this + ->getRepository($modelName) + ->exists($id) + ; + } + + /** + * Count number of all records for the model. + */ + public function countAll(string $modelName): int + { + return $this + ->getRepository($modelName) + ->countAll() + ; + } + + /** + * Count number of records for a model and criteria. + */ + public function count(string $modelName, ?DomainInterface $criteria = null): int + { + return $this + ->getRepository($modelName) + ->count($criteria) + ; + } + + public function getRepository(string $modelName): RecordRepository + { + if (!\array_key_exists($modelName, $this->repositories)) { + $repository = new RecordRepository($this, $modelName); + $this->addRepository($repository); + + return $repository; + } + + return $this->repositories[$modelName]; + } + + public function setRepositories(array $repositories = []): self + { + $this->repositories = []; + + foreach ($repositories as $repository) { + $this->addRepository($repository); + } + + return $this; + } + + public function addRepository(RecordRepository $repository): self + { + $this->repositories[$repository->getModelName()] = $repository; + $repository->setRecordManager($this); + + return $this; + } + + public function createQueryBuilder(string $modelName): QueryBuilder + { + return new QueryBuilder($this, $modelName); + } + + public function createOrmQuery(string $name, string $method): OrmQuery + { + return new OrmQuery($this, $name, $method); + } + + public function createNativeQuery(string $name, string $method): NativeQuery + { + return new NativeQuery($this, $name, $method); + } + + public function executeQuery(QueryInterface $query): mixed + { + $options = $query->getOptions(); + + if (!$options) { + return $this->client->request($query->getName(), $query->getMethod(), $query->getParameters()); + } + + return $this->client->request($query->getName(), $query->getMethod(), $query->getParameters(), $options); + } + + public function getClient(): Client + { + return $this->client; + } + + public function getSchema(): Schema + { + return $this->schema; + } + + public function getRepositories(): array + { + return $this->repositories; + } + + public function getExpressionBuilder(): ExpressionBuilder + { + return $this->expressionBuilder; + } +} diff --git a/src/DBAL/Repository/RecordNotFoundException.php b/src/DBAL/Repository/RecordNotFoundException.php new file mode 100644 index 0000000..be21790 --- /dev/null +++ b/src/DBAL/Repository/RecordNotFoundException.php @@ -0,0 +1,30 @@ +modelName, $this->id)); + } + + public function getModelName(): string + { + return $this->modelName; + } + + public function getId(): int + { + return $this->id; + } +} diff --git a/src/DBAL/Repository/RecordRepository.php b/src/DBAL/Repository/RecordRepository.php new file mode 100644 index 0000000..20551eb --- /dev/null +++ b/src/DBAL/Repository/RecordRepository.php @@ -0,0 +1,254 @@ +addRepository($this); + } + + /** + * Insert a new record. + * + * @return int the ID of the new record + * + * @throws \InvalidArgumentException when $data is empty + */ + public function insert(array $data): int + { + if (!$data) { + throw new \InvalidArgumentException('Data cannot be empty'); + } + + $result = $this + ->createQueryBuilder() + ->insert() + ->setValues($data) + ->getQuery() + ->execute() + ; + + return \is_scalar($result) ? (int) $result : 0; + } + + /** + * Update record(s). + * + * NB: It is not currently possible to perform “computed” updates + * (where the value being set depends on an existing value of a record). + */ + public function update(int|array $ids, array $data = []): void + { + if (!$data) { + return; + } + + $this + ->createQueryBuilder() + ->update((array) $ids) + ->setValues($data) + ->getQuery() + ->execute() + ; + } + + /** + * Delete record(s). + */ + public function delete(int|array $ids): void + { + if (!$ids) { + return; + } + + $this + ->createQueryBuilder() + ->delete((array) $ids) + ->getQuery() + ->execute() + ; + } + + /** + * Search one ID of record by criteria. + */ + public function searchOne(?DomainInterface $criteria): ?int + { + return (int) $this + ->createQueryBuilder() + ->search() + ->where($criteria) + ->getQuery() + ->getOneOrNullScalarResult() + ; + } + + /** + * Search all ID of record(s). + * + * @return int[] + */ + public function searchAll(array $orders = [], int $limit = null, int $offset = null): array + { + return $this->search(null, $orders, $limit, $offset); + } + + /** + * Search ID of record(s) by criteria. + * + * @return int[] + */ + public function search(?DomainInterface $criteria = null, array $orders = [], int $limit = null, int $offset = null): array + { + /** @var int[] $result */ + return $this + ->createQueryBuilder() + ->search() + ->where($criteria) + ->setOrders($orders) + ->setFirstResult($offset) + ->setMaxResults($limit) + ->getQuery() + ->getScalarResult() + ; + } + + /** + * Find ONE record by ID. + * + * @throws RecordNotFoundException when the record was not found + */ + public function read(int $id, array $fields = []): array + { + $record = $this->find($id, $fields); + + if (!$record) { + throw new RecordNotFoundException($this->modelName, $id); + } + + return $record; + } + + /** + * Find ONE record by ID. + */ + public function find(int $id, array $fields = []): ?array + { + return $this->findOneBy($this->expr()->eq('id', $id), $fields); + } + + /** + * Find ONE record by criteria. + */ + public function findOneBy(?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $offset = null): ?array + { + $result = $this->findBy($criteria, $fields, $orders, 1, $offset); + + return array_pop($result); + } + + /** + * Find all records. + * + * @return array[] + */ + public function findAll(array $fields = [], array $orders = [], int $limit = null, int $offset = null): array + { + return $this->findBy(null, $fields, $orders, $limit, $offset); + } + + /** + * Find record(s) by criteria. + * + * @return array[] + */ + public function findBy(?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array + { + return $this + ->createQueryBuilder() + ->select($fields) + ->where($criteria) + ->setOrders($orders) + ->setFirstResult($offset) + ->setMaxResults($limit) + ->getQuery() + ->getResult() + ; + } + + /** + * Check if a record exists. + */ + public function exists(int $id): bool + { + return 1 === $this->count($this->expr()->eq('id', $id)); + } + + /** + * Count number of all records for the model. + */ + public function countAll(): int + { + return $this->count(); + } + + /** + * Count number of records for a model and criteria. + */ + public function count(?DomainInterface $criteria = null): int + { + return $this + ->createQueryBuilder() + ->select() + ->where($criteria) + ->getQuery() + ->count() + ; + } + + public function createQueryBuilder(): QueryBuilder + { + return $this->recordManager + ->createQueryBuilder($this->modelName) + ->select() + ; + } + + public function setRecordManager(RecordManager $recordManager): self + { + $this->recordManager = $recordManager; + + return $this; + } + + public function getRecordManager(): RecordManager + { + return $this->recordManager; + } + + public function getModelName(): string + { + return $this->modelName; + } + + public function expr(): ExpressionBuilder + { + return $this->recordManager->getExpressionBuilder(); + } +} diff --git a/src/DBAL/Schema/Choice.php b/src/DBAL/Schema/Choice.php new file mode 100644 index 0000000..8750938 --- /dev/null +++ b/src/DBAL/Schema/Choice.php @@ -0,0 +1,38 @@ +id; + } + + public function getName(): string + { + return $this->name; + } + + public function getValue(): string + { + return $this->value; + } +} diff --git a/src/DBAL/Schema/Field.php b/src/DBAL/Schema/Field.php new file mode 100644 index 0000000..e864461 --- /dev/null +++ b/src/DBAL/Schema/Field.php @@ -0,0 +1,204 @@ +id = (int) $data['id']; + $this->name = (string) $data['name']; + $this->type = (string) $data['ttype']; + $this->required = (bool) $data['required']; + $this->readOnly = (bool) $data['readonly']; + $this->displayName = $data['display_name'] ?? null; + $this->size = $data['size'] ?? null; + $this->selection = $data['selection'] ?? null; + $this->targetModelName = $data['relation'] ?? null; + $this->targetFieldName = $data['relation_field'] ?? null; + } + + public function getModel(): Model + { + return $this->model; + } + + public function setModel(Model $model): self + { + $this->model = $model; + + return $this; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getName(): string + { + return $this->name; + } + + public function getType(): string + { + return $this->type; + } + + public function isRequired(): bool + { + return $this->required; + } + + public function isReadOnly(): bool + { + return $this->readOnly; + } + + public function getDisplayName(): string + { + return $this->displayName ?: $this->name; + } + + public function getSize(): ?int + { + return $this->size; + } + + public function getSelection(): ?Selection + { + return $this->selection; + } + + public function getTargetModelName(): ?string + { + return $this->targetModelName; + } + + public function getTargetFieldName(): ?string + { + return $this->targetFieldName; + } + + public function isIdentifier(): bool + { + return 'id' === $this->name; + } + + public function isBinary(): bool + { + return self::T_BINARY === $this->type; + } + + public function isBoolean(): bool + { + return self::T_BOOLEAN === $this->type; + } + + public function isInteger(): bool + { + return self::T_INTEGER === $this->type; + } + + public function isFloat(): bool + { + return \in_array($this->type, [self::T_FLOAT, self::T_MONETARY], true); + } + + public function isNumber(): bool + { + return $this->isInteger() || $this->isFloat(); + } + + public function isString(): bool + { + return \in_array($this->type, [self::T_CHAR, self::T_TEXT, self::T_HTML], true); + } + + public function isDate(): bool + { + return \in_array($this->type, [self::T_DATE, self::T_DATETIME], true); + } + + public function getDateFormat(): string + { + return self::T_DATETIME === $this->type ? self::DATETIME_FORMAT : self::DATE_FORMAT; + } + + public function isSelection(): bool + { + return self::T_SELECTION === $this->type; + } + + public function isSelectable(): bool + { + return null !== $this->selection; + } + + public function isAssociation(): bool + { + return \in_array($this->type, [ + self::T_MANY_TO_ONE, + self::T_MANY_TO_MANY, + self::T_ONE_TO_MANY, + ], true); + } + + public function isSingleAssociation(): bool + { + return self::T_MANY_TO_ONE === $this->type; + } + + public function isMultipleAssociation(): bool + { + return \in_array($this->type, [ + self::T_MANY_TO_MANY, + self::T_ONE_TO_MANY, + ], true); + } +} diff --git a/src/DBAL/Schema/Model.php b/src/DBAL/Schema/Model.php new file mode 100644 index 0000000..0895f3d --- /dev/null +++ b/src/DBAL/Schema/Model.php @@ -0,0 +1,132 @@ +id = (int) $data['id']; + $this->name = (string) $data['model']; + $this->displayName = (string) $data['name']; + $this->transient = (bool) $data['transient']; + + foreach ($fields as $field) { + $this->addField($field); + } + } + + public function getSchema(): Schema + { + return $this->schema; + } + + public function getId(): int + { + return $this->id; + } + + public function getName(): string + { + return $this->name; + } + + public function getDisplayName(): string + { + return $this->displayName ?: $this->name; + } + + public function isTransient(): bool + { + return $this->transient; + } + + public function hasField(string $fieldName): bool + { + try { + $this->getField($fieldName); + } catch (SchemaException $exception) { + return false; + } + + return true; + } + + /** + * @throws SchemaException when the field was not found + */ + public function getField(string $fieldName): Field + { + $model = $this; + $fields = explode('.', $fieldName); + $lastKey = \count($fields) - 1; + + foreach ($fields as $key => $subFieldName) { + $field = $model->getField($subFieldName); + + if ($lastKey === $key) { + break; + } + + $targetModel = $field->getTargetModelName(); + + if (!$targetModel) { + throw SchemaException::fieldNotFound($fieldName, $this); + } + + $model = $this->schema->getModel($targetModel); + } + + return $field; + } + + public function getFields(): array + { + return $this->fields; + } + + /** + * @return array + */ + public function getFieldNames(): array + { + $fieldNames = []; + + foreach ($this->fields as $field) { + $fieldNames[] = $field->getName(); + } + + return $fieldNames; + } + + /** + * @internal + */ + private function addField(Field $field): void + { + $field->setModel($this); + $this->fields[] = $field; + } +} diff --git a/src/DBAL/Schema/Schema.php b/src/DBAL/Schema/Schema.php new file mode 100644 index 0000000..b167462 --- /dev/null +++ b/src/DBAL/Schema/Schema.php @@ -0,0 +1,126 @@ +hasModel($modelName)) { + throw SchemaException::modelNotFound($modelName); + } + + if (!isset($this->loadedModels[$modelName])) { + $expr = $this->client->getExpressionBuilder(); + $modelData = (array) $this->client->request(self::IR_MODEL, OrmQuery::SEARCH_READ, $expr->normalizeDomains($expr->eq('model', $modelName))); + $modelData = $modelData[0] ?? null; + + if (!\is_array($modelData)) { + throw new SchemaException(sprintf('The model "%s" was not found.', $modelName)); + } + + $this->loadedModels[$modelName] = $this->createModel($modelData); + } + + return $this->loadedModels[$modelName]; + } + + public function hasModel(string $modelName): bool + { + return \in_array($modelName, $this->getModelNames(), true); + } + + /** + * Gets all model names. + * + * @return string[] + */ + public function getModelNames(): array + { + if (!$this->modelNames) { + $this->modelNames = array_column((array) $this->client->request(self::IR_MODEL, OrmQuery::SEARCH_READ, [[]], [ + 'fields' => ['model'], + ]), 'model'); + } + + return $this->modelNames; + } + + /** + * @internal + */ + private function createModel(array $modelData): Model + { + $expr = $this->client->getExpressionBuilder(); + $fields = (array) $this->client->request( + self::IR_MODEL_FIELDS, + OrmQuery::SEARCH_READ, + $expr->normalizeDomains($expr->eq('model_id', $modelData['id'])) + ); + + foreach ($fields as $key => $fieldData) { + $choices = []; + $selectionsIds = array_filter($fieldData['selection_ids'] ?? []); + + if (!empty($selectionsIds)) { + $choices = (array) $this->client->request( + self::IR_MODEL_FIELD_SELECTION, + OrmQuery::SEARCH_READ, + $expr->normalizeDomains($expr->eq('field_id', $fieldData['id'])) + ); + + foreach ($choices as $index => $choice) { + if (\is_array($choice)) { + $choices[$index] = new Choice((string) $choice['name'], $choice['value'], (int) $choice['id']); + } + } + } elseif (!empty($fieldData['selection'])) { + if (preg_match_all('#^\[\s*(\(\'(\w+)\'\,\s*\'(\w+)\'\)\s*\,?\s*)*\s*\]$#', trim($fieldData['selection']), $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + if (isset($match[2], $match[3])) { + $choices[] = new Choice($match[3], $match[2]); + } + } + } + } + + if ($choices) { + $fieldData['selection'] = $choices; + } + + $fields[$key] = new Field($fieldData); + } + + return new Model($this, $modelData, $fields); + } +} diff --git a/src/DBAL/Schema/SchemaException.php b/src/DBAL/Schema/SchemaException.php new file mode 100644 index 0000000..08b62db --- /dev/null +++ b/src/DBAL/Schema/SchemaException.php @@ -0,0 +1,25 @@ +getName())); + } + + public static function modelNotFound(string $modelName): self + { + return new self(sprintf('The model "%s" was not found on the database', $modelName)); + } +} diff --git a/src/DBAL/Schema/Selection.php b/src/DBAL/Schema/Selection.php new file mode 100644 index 0000000..648fff8 --- /dev/null +++ b/src/DBAL/Schema/Selection.php @@ -0,0 +1,79 @@ +addChoice($choice); + } + } + + public function getIds(): array + { + $ids = []; + + foreach ($this->choices as $choice) { + $ids[] = $choice->getId(); + } + + return $ids; + } + + public function getNames(): array + { + $names = []; + + foreach ($this->choices as $choice) { + $names[] = $choice->getName(); + } + + return $names; + } + + public function getValues(): array + { + $values = []; + + foreach ($this->choices as $choice) { + $values[] = $choice->getValue(); + } + + return $values; + } + + /** + * @return Choice[] + */ + public function getChoices(): array + { + return $this->choices; + } + + /** + * @internal + */ + private function addChoice(Choice $choice): void + { + $this->choices[] = $choice; + } +} diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index b8408c3..a13aabe 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -28,8 +28,7 @@ class JsonRpcPhpStreamTransport extends AbstractRpcTransport public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) - { + ) { } public function request(string $service, string $method, array $arguments = []): array From c4cae455f289dea4eca2731d4fbc780c2df00232 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Thu, 2 Feb 2023 14:31:39 +0100 Subject: [PATCH 35/80] Update README.md --- README.md | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index c5fc066..3e1adf3 100644 --- a/README.md +++ b/README.md @@ -10,20 +10,21 @@ Odoo API client using [XML-RPC Odoo ORM External API](https://www.odoo.com/documentation/12.0/webservices/odoo.html). It allows you call your odoo instance and manage records easily. -**You are reading the documentation of version ```7.0```, if your version is older, please read -[this documentation (6.1.3)](https://github.com/Ang3/php-odoo-api-client/tree/v6.1.3).** -Please see the file [UPGRADE-7.0.md](https://github.com/Ang3/php-odoo-api-client/blob/7.0/UPGRADE-7.0.md) +**You are reading the documentation of version ```8.0```, if your version is older, please read +[this documentation (6.1.3)](https://github.com/Ang3/php-odoo-api-client/tree/v7.0.6).** +Please see the file [UPGRADE-8.0.md](https://github.com/Ang3/php-odoo-api-client/blob/7.0/UPGRADE-8.0.md) to upgrade your version easily. **Main features** -- Authentication ```<7.0``` -- Basic XML-RPC calls ```<7.0``` -- Expression builder ```<7.0``` -- Database Abstraction Layer (DBAL) ```>=7.0``` +- Authentication `<7.0` +- Basic JSON-RPC calls `>=8.0` +- Expression builder `<7.0` +- Database Abstraction Layer (DBAL) `>=7.0` - Record manager - - Repositories` + - Repositories - Query builder + - Paginator `>=8.0` **Good to know** @@ -33,14 +34,14 @@ If you are in Symfony application you should be interested in the bundle Requirements ============ -- The PHP extension ```php-xmlrpc``` must be enabled. +- The PHP extension ```php-json``` must be enabled for JSON RPC calls. -| Odoo server | Compatibility | Comment | -| --- | --- | --- | -| newer | Unknown | Needs feddback | -| v13.0 | Yes | Some Odoo model names changed (e.g account.invoice > account.move) | -| v12.0 | Yes | First tested version | -| < v12 | Unknown | Needs feddback | +| Odoo server | Compatibility | Comment | +|-------------|---------------|--------------------------------------------------------------------| +| newer | Unknown | Needs feddback | +| v13.0 | Yes | Some Odoo model names changed (e.g account.invoice > account.move) | +| v12.0 | Yes | First tested version | +| < v12 | Unknown | Needs feddback | Installation ============ @@ -82,7 +83,7 @@ $client = Client::create([ Exceptions: - ```Ang3\Component\Odoo\Exception\MissingConfigParameterException``` when a required parameter is missing -from the static method ```createFromConfig()```. +from the static method ```create()```. Then, make your call: @@ -131,7 +132,7 @@ $recordManager = new RecordManager($myClient); Here is all built-in ORM methods provided by the record manager: ```php -use Ang3\Component\Odoo\DBAL\Expression\DomainInterface; +use Ang3\Component\Odoo\DBAL\Expression\Domain\DomainInterface; /** * Create a new record. @@ -440,7 +441,7 @@ Then, build your query like below: $query = $queryBuilder->getQuery(); ``` -Your query is an instance of ```Ang3\Component\Odoo\Query\OrmQuery```. +Your query is an instance of ```Ang3\Component\Odoo\DBAL\Query\OrmQuery```. ### Execute your query @@ -604,7 +605,7 @@ with a *polish notation* for logical operations (```AND```, ```OR``` and ```NOT` It could be quickly ugly to do a complex domain, but don't worry the builder makes all for you. :-) -Each domain builder method creates an instance of ```Ang3\Component\Odoo\Expression\DomainInterface```. +Each domain builder method creates an instance of ```Ang3\Component\Odoo\Expression\Domain\DomainInterface```. The only one method of this interface is ```toArray()``` to get a normalized array of the expression. To illustrate how to work with it, here is an example using ```ExpressionBuilder``` helper methods: @@ -745,7 +746,7 @@ Please read the [ORM documentation](https://www.odoo.com/documentation/13.0/refe to known what we are talking about. The expression builder provides helper methods to build a well-formed *operation command*: -each operation method returns an instance of ```Ang3\Component\Odoo\DBAL\Expression\CollectionOperation```. +each operation method returns an instance of ```Ang3\Component\Odoo\DBAL\Expression\Operation\CollectionOperation```. Like domains, the only one method of this interface is ```toArray()``` to get a normalized array of the expression. To illustrate how to work with operations, here is an example using ```ExpressionBuilder``` helper methods: From b0fecdd4a3bd27799c1bf3dabc2b6ae1ba4d115b Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Thu, 2 Feb 2023 14:32:06 +0100 Subject: [PATCH 36/80] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e1adf3..b661b0e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Odoo API client using you call your odoo instance and manage records easily. **You are reading the documentation of version ```8.0```, if your version is older, please read -[this documentation (6.1.3)](https://github.com/Ang3/php-odoo-api-client/tree/v7.0.6).** +[this documentation (7.0.6)](https://github.com/Ang3/php-odoo-api-client/tree/v7.0.6).** Please see the file [UPGRADE-8.0.md](https://github.com/Ang3/php-odoo-api-client/blob/7.0/UPGRADE-8.0.md) to upgrade your version easily. From f4ac6681f260e394d6e95c7ca54448f26db1e4a8 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Thu, 2 Feb 2023 14:32:24 +0100 Subject: [PATCH 37/80] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b661b0e..2fcd20e 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ use Ang3\Component\Odoo\Client; // Option 1: by calling the constructor... $client = new Client('', '', '', '', $logger = null); -// Option 2 : by calling the static method ::createFromConfig() with configuration as array +// Option 2 : by calling the static method ::create() with configuration as array $client = Client::create([ 'url' => '', 'database' => '', From de0a4a0c603af9146b073842039dc6c277499da2 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Thu, 2 Feb 2023 14:33:07 +0100 Subject: [PATCH 38/80] Update UPGRADE-8.0.md --- UPGRADE-8.0.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/UPGRADE-8.0.md b/UPGRADE-8.0.md index ef949e7..7460828 100644 --- a/UPGRADE-8.0.md +++ b/UPGRADE-8.0.md @@ -20,5 +20,7 @@ Client Expression builder ------------------ -- Domain classes moved from `Ang3\Component\Odoo\DBAL\Expression` to `Ang3\Component\Odoo\DBAL\Expression\Domain` +Architecture update: + - Domain classes moved from `Ang3\Component\Odoo\DBAL\Expression` to `Ang3\Component\Odoo\DBAL\Expression\Domain` + - Operation classes moved from `Ang3\Component\Odoo\DBAL\Expression` to `Ang3\Component\Odoo\DBAL\Expression\Operation` - Domain expressions fixes. \ No newline at end of file From 7461e5f1f841b83b97dfcd4760c588d1ef0aac2c Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Thu, 2 Feb 2023 14:33:22 +0100 Subject: [PATCH 39/80] Updated docs --- UPGRADE-7.0.md | 2 +- UPGRADE-8.0.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UPGRADE-7.0.md b/UPGRADE-7.0.md index 849f799..8f220db 100644 --- a/UPGRADE-7.0.md +++ b/UPGRADE-7.0.md @@ -35,4 +35,4 @@ Expression builder - Implemented non-scalar values support: - Dates into string - Iterable / Generator into array - - Object into string \ No newline at end of file + - Object into string diff --git a/UPGRADE-8.0.md b/UPGRADE-8.0.md index 7460828..4b201bb 100644 --- a/UPGRADE-8.0.md +++ b/UPGRADE-8.0.md @@ -23,4 +23,4 @@ Expression builder Architecture update: - Domain classes moved from `Ang3\Component\Odoo\DBAL\Expression` to `Ang3\Component\Odoo\DBAL\Expression\Domain` - Operation classes moved from `Ang3\Component\Odoo\DBAL\Expression` to `Ang3\Component\Odoo\DBAL\Expression\Operation` -- Domain expressions fixes. \ No newline at end of file +- Domain expressions fixes. From 7419d60e8297e38a547d12325d717133bd5b98b8 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Thu, 2 Feb 2023 14:35:18 +0100 Subject: [PATCH 40/80] Fixed code --- src/DBAL/Query/AbstractQuery.php | 3 +-- src/DBAL/Repository/RecordRepository.php | 2 +- src/DBAL/Schema/Choice.php | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/DBAL/Query/AbstractQuery.php b/src/DBAL/Query/AbstractQuery.php index c10cd8b..97b164f 100644 --- a/src/DBAL/Query/AbstractQuery.php +++ b/src/DBAL/Query/AbstractQuery.php @@ -22,8 +22,7 @@ public function __construct( protected readonly RecordManager $recordManager, protected string $name, protected string $method - ) - { + ) { } public function getName(): string diff --git a/src/DBAL/Repository/RecordRepository.php b/src/DBAL/Repository/RecordRepository.php index 20551eb..60644ed 100644 --- a/src/DBAL/Repository/RecordRepository.php +++ b/src/DBAL/Repository/RecordRepository.php @@ -116,7 +116,7 @@ public function searchAll(array $orders = [], int $limit = null, int $offset = n */ public function search(?DomainInterface $criteria = null, array $orders = [], int $limit = null, int $offset = null): array { - /** @var int[] $result */ + /* @var int[] $result */ return $this ->createQueryBuilder() ->search() diff --git a/src/DBAL/Schema/Choice.php b/src/DBAL/Schema/Choice.php index 8750938..d194021 100644 --- a/src/DBAL/Schema/Choice.php +++ b/src/DBAL/Schema/Choice.php @@ -17,8 +17,7 @@ public function __construct( private readonly string $name, private readonly string $value, private readonly ?int $id = null - ) - { + ) { } public function getId(): ?int From cade159e6017e1a393aff44d7f5eecf7add3d467 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Thu, 2 Feb 2023 14:36:49 +0100 Subject: [PATCH 41/80] Fixed tests code --- .../Expression/AbstractDomainTest.php | 11 ++++------- tests/{ => DBAL}/Expression/ComparisonTest.php | 6 +++--- .../Expression/CompositeDomainTest.php | 17 ++++++----------- 3 files changed, 13 insertions(+), 21 deletions(-) rename tests/{ => DBAL}/Expression/AbstractDomainTest.php (73%) rename tests/{ => DBAL}/Expression/ComparisonTest.php (86%) rename tests/{ => DBAL}/Expression/CompositeDomainTest.php (91%) diff --git a/tests/Expression/AbstractDomainTest.php b/tests/DBAL/Expression/AbstractDomainTest.php similarity index 73% rename from tests/Expression/AbstractDomainTest.php rename to tests/DBAL/Expression/AbstractDomainTest.php index 1799f3a..18f3ee1 100644 --- a/tests/Expression/AbstractDomainTest.php +++ b/tests/DBAL/Expression/AbstractDomainTest.php @@ -9,18 +9,15 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Tests\Expression; +namespace Ang3\Component\Odoo\Tests\DBAL\Expression; -use Ang3\Component\Odoo\DBAL\Expression\Comparison; -use Ang3\Component\Odoo\Expression\CompositeDomain; +use Ang3\Component\Odoo\DBAL\Expression\Domain\Comparison; +use Ang3\Component\Odoo\DBAL\Expression\Domain\CompositeDomain; use Ang3\Component\Odoo\Tests\AbstractTest; abstract class AbstractDomainTest extends AbstractTest { - /** - * @param mixed $value - */ - public function createComparison(string $operator, string $fieldName, $value): Comparison + public function createComparison(string $operator, string $fieldName, mixed $value): Comparison { return new Comparison($operator, $fieldName, $value); } diff --git a/tests/Expression/ComparisonTest.php b/tests/DBAL/Expression/ComparisonTest.php similarity index 86% rename from tests/Expression/ComparisonTest.php rename to tests/DBAL/Expression/ComparisonTest.php index a5570be..af7e2fa 100644 --- a/tests/Expression/ComparisonTest.php +++ b/tests/DBAL/Expression/ComparisonTest.php @@ -9,12 +9,12 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Tests\Expression; +namespace Ang3\Component\Odoo\Tests\DBAL\Expression; -use Ang3\Component\Odoo\DBAL\Expression\Comparison; +use Ang3\Component\Odoo\DBAL\Expression\Domain\Comparison; /** - * @coversDefaultClass \Ang3\Component\Odoo\DBAL\Expression\Comparison + * @coversDefaultClass \Ang3\Component\Odoo\DBAL\Expression\Domain\Comparison * * @internal */ diff --git a/tests/Expression/CompositeDomainTest.php b/tests/DBAL/Expression/CompositeDomainTest.php similarity index 91% rename from tests/Expression/CompositeDomainTest.php rename to tests/DBAL/Expression/CompositeDomainTest.php index be62b7d..913a2b6 100644 --- a/tests/Expression/CompositeDomainTest.php +++ b/tests/DBAL/Expression/CompositeDomainTest.php @@ -9,13 +9,13 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Tests\Expression; +namespace Ang3\Component\Odoo\Tests\DBAL\Expression; -use Ang3\Component\Odoo\DBAL\Expression\CompositeDomain; -use Ang3\Component\Odoo\DBAL\Expression\DomainInterface; +use Ang3\Component\Odoo\DBAL\Expression\Domain\CompositeDomain; +use Ang3\Component\Odoo\DBAL\Expression\Domain\DomainInterface; /** - * @coversDefaultClass \Ang3\Component\Odoo\DBAL\Expression\CompositeDomain + * @coversDefaultClass \Ang3\Component\Odoo\DBAL\Expression\Domain\CompositeDomain * * @internal */ @@ -132,19 +132,14 @@ public function provideToArrayDataSet(): array * @covers ::toArray * * @dataProvider provideToArrayDataSet - * - * @param mixed $expectedResult */ - public function testToArray(string $operator, array $domains = [], $expectedResult = null, string $message = ''): void + public function testToArray(string $operator, array $domains = [], mixed $expectedResult = null, string $message = ''): void { $domain = new CompositeDomain($operator, $domains); static::assertSame($expectedResult, $domain->toArray(), $message); } - /** - * @param mixed $expression - */ - protected function createFakeDomain($expression): DomainInterface + protected function createFakeDomain(mixed $expression): DomainInterface { $fakeDomain = $this->createMock(DomainInterface::class); $fakeDomain From 3ba83eea4863d37b623c8f0667d527c143068e10 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Thu, 2 Feb 2023 16:40:07 +0100 Subject: [PATCH 42/80] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2fcd20e..9e81585 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ PHP Odoo API client =================== -[![Build Status](https://travis-ci.org/Ang3/php-odoo-api-client.svg?branch=master)](https://travis-ci.org/Ang3/php-odoo-api-client) +[![Code Quality](https://github.com/ang3/php-odoo-api-client/actions/workflows/php_lint.yml/badge.svg)](https://github.com/ang3/php-odoo-api-client/actions/workflows/php_lint.yml) +[![PHPUnit tests](https://github.com/ang3/php-odoo-api-client/actions/workflows/phpunit.yml/badge.svg)](https://github.com/ang3/php-odoo-api-client/actions/workflows/phpunit.yml) [![Latest Stable Version](https://poser.pugx.org/ang3/php-odoo-api-client/v/stable)](https://packagist.org/packages/ang3/php-odoo-api-client) [![Latest Unstable Version](https://poser.pugx.org/ang3/php-odoo-api-client/v/unstable)](https://packagist.org/packages/ang3/php-odoo-api-client) [![Total Downloads](https://poser.pugx.org/ang3/php-odoo-api-client/downloads)](https://packagist.org/packages/ang3/php-odoo-api-client) From f558a5b7cdbe3ceb08db617e8d648f09b120b9aa Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Thu, 2 Feb 2023 16:42:23 +0100 Subject: [PATCH 43/80] Update Schema.php --- src/DBAL/Schema/Schema.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/DBAL/Schema/Schema.php b/src/DBAL/Schema/Schema.php index b167462..5f4b0e1 100644 --- a/src/DBAL/Schema/Schema.php +++ b/src/DBAL/Schema/Schema.php @@ -34,6 +34,9 @@ public function __construct(private readonly Client $client) { } + /** + * @throws SchemaException when the model was not found + */ public function getModel(string $modelName): Model { if (!$this->hasModel($modelName)) { @@ -46,7 +49,7 @@ public function getModel(string $modelName): Model $modelData = $modelData[0] ?? null; if (!\is_array($modelData)) { - throw new SchemaException(sprintf('The model "%s" was not found.', $modelName)); + throw SchemaException::modelNotFound($modelName); } $this->loadedModels[$modelName] = $this->createModel($modelData); From 4bcefb3610fa82d691324ae452f4345a106aabf8 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Sun, 5 Nov 2023 15:22:46 +0100 Subject: [PATCH 44/80] Fix update --- composer.json | 7 +- composer.lock | 401 ++++++++++++------ phpstan.neon | 3 +- src/Client.php | 264 +----------- .../Expression/Domain/CompositeDomain.php | 13 +- src/DBAL/Expression/ExpressionBuilder.php | 24 +- src/DBAL/Query/OrmQuery.php | 2 +- src/DBAL/Query/QueryBuilder.php | 8 +- src/DBAL/RecordManager.php | 185 +------- src/DBAL/Repository/RecordRepository.php | 37 +- src/DBAL/Schema/Enum/DateTimeFormat.php | 18 + src/DBAL/Schema/Enum/FieldType.php | 33 ++ src/DBAL/Schema/Field.php | 63 +-- src/DBAL/Schema/Model.php | 10 +- src/Enum/OdooMethod.php | 19 + src/Enum/OdooService.php | 18 + src/Metadata/Version.php | 81 ++++ src/Transport/JsonRpcPhpStreamTransport.php | 7 +- src/Transport/TransportException.php | 21 + src/Transport/TransportInterface.php | 4 +- tests/DBAL/Expression/CompositeDomainTest.php | 4 +- tests/Utils/Debugger.php | 35 -- tests/Utils/ObjectTester.php | 73 +--- tests/Utils/Reflector.php | 41 +- tests/Utils/TestDecorator.php | 5 +- 25 files changed, 596 insertions(+), 780 deletions(-) create mode 100644 src/DBAL/Schema/Enum/DateTimeFormat.php create mode 100644 src/DBAL/Schema/Enum/FieldType.php create mode 100644 src/Enum/OdooMethod.php create mode 100644 src/Enum/OdooService.php create mode 100644 src/Metadata/Version.php create mode 100644 src/Transport/TransportException.php delete mode 100644 tests/Utils/Debugger.php diff --git a/composer.json b/composer.json index b447901..909166b 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "ang3/php-odoo-api-client", "type": "component", "description": "Odoo API client", - "keywords": ["odoo", "api", "client", "domain", "operation", "builder", "json"], + "keywords": ["odoo", "api", "client", "domain", "operation", "builder", "trransport", "json"], "license": "MIT", "authors": [ { @@ -19,9 +19,10 @@ "require": { "php": ">=8.1", "ext-json": "*", - "psr/log": "^1.1||^2.0||3.0" + "psr/log": "^1.1|^2.0|^3.0" }, "require-dev": { - "symfony/test-pack": "^1.0" + "symfony/test-pack": "^1.0", + "symfony/var-dumper": "^6.3" } } diff --git a/composer.lock b/composer.lock index 2202d9c..409bdf7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e246e4552146e2ba330c1da7be51a1a5", + "content-hash": "545eda0d995615c70edd2c291bf3f04c", "packages": [ { "name": "psr/log", @@ -130,26 +130,24 @@ }, { "name": "masterminds/html5", - "version": "2.7.6", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/Masterminds/html5-php.git", - "reference": "897eb517a343a2281f11bc5556d6548db7d93947" + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/897eb517a343a2281f11bc5556d6548db7d93947", - "reference": "897eb517a343a2281f11bc5556d6548db7d93947", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", "shasum": "" }, "require": { - "ext-ctype": "*", "ext-dom": "*", - "ext-libxml": "*", "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7" + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8" }, "type": "library", "extra": { @@ -193,22 +191,22 @@ ], "support": { "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.7.6" + "source": "https://github.com/Masterminds/html5-php/tree/2.8.1" }, - "time": "2022-08-18T16:18:26+00:00" + "time": "2023-05-10T11:58:31+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.0", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "shasum": "" }, "require": { @@ -246,7 +244,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" }, "funding": [ { @@ -254,20 +252,20 @@ "type": "tidelift" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2023-03-08T13:26:56+00:00" }, { "name": "nikic/php-parser", - "version": "v4.15.3", + "version": "v4.17.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "shasum": "" }, "require": { @@ -308,9 +306,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" }, - "time": "2023-01-16T22:05:37+00:00" + "time": "2023-08-13T19:53:39+00:00" }, { "name": "phar-io/manifest", @@ -425,23 +423,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.24", + "version": "9.2.29", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed" + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed", - "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.14", + "nikic/php-parser": "^4.15", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -456,8 +454,8 @@ "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { @@ -490,7 +488,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24" + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" }, "funding": [ { @@ -498,7 +497,7 @@ "type": "github" } ], - "time": "2023-01-26T08:26:55+00:00" + "time": "2023-09-19T04:57:46+00:00" }, { "name": "phpunit/php-file-iterator", @@ -743,16 +742,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.28", + "version": "9.6.13", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e" + "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e", - "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f3d767f7f9e191eab4189abe41ab37797e30b1be", + "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be", "shasum": "" }, "require": { @@ -767,7 +766,7 @@ "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-code-coverage": "^9.2.28", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", @@ -785,8 +784,8 @@ "sebastian/version": "^3.0.2" }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "bin": [ "phpunit" @@ -794,7 +793,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.5-dev" + "dev-master": "9.6-dev" } }, "autoload": { @@ -825,7 +824,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.13" }, "funding": [ { @@ -841,7 +841,7 @@ "type": "tidelift" } ], - "time": "2023-01-14T12:32:24+00:00" + "time": "2023-09-19T05:39:22+00:00" }, { "name": "sebastian/cli-parser", @@ -1143,16 +1143,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "shasum": "" }, "require": { @@ -1197,7 +1197,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" }, "funding": [ { @@ -1205,20 +1205,20 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2023-05-07T05:35:17+00:00" }, { "name": "sebastian/environment", - "version": "5.1.4", + "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { @@ -1260,7 +1260,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { @@ -1268,7 +1268,7 @@ "type": "github" } ], - "time": "2022-04-03T09:37:03+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", @@ -1349,16 +1349,16 @@ }, { "name": "sebastian/global-state", - "version": "5.0.5", + "version": "5.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + "reference": "bde739e7565280bda77be70044ac1047bc007e34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", + "reference": "bde739e7565280bda77be70044ac1047bc007e34", "shasum": "" }, "require": { @@ -1401,7 +1401,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" }, "funding": [ { @@ -1409,7 +1409,7 @@ "type": "github" } ], - "time": "2022-02-14T08:28:10+00:00" + "time": "2023-08-02T09:26:13+00:00" }, { "name": "sebastian/lines-of-code", @@ -1582,16 +1582,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { @@ -1630,10 +1630,10 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { @@ -1641,7 +1641,7 @@ "type": "github" } ], - "time": "2020-10-26T13:17:30+00:00" + "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", @@ -1700,16 +1700,16 @@ }, { "name": "sebastian/type", - "version": "3.2.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { @@ -1744,7 +1744,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { @@ -1752,7 +1752,7 @@ "type": "github" } ], - "time": "2022-09-12T14:47:03+00:00" + "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", @@ -1809,16 +1809,16 @@ }, { "name": "symfony/browser-kit", - "version": "v6.2.5", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "ea591a69d714216d29cb67b519b509bd32b735a2" + "reference": "ca4a988488f61ac18f8f845445eabdd36f89aa8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ea591a69d714216d29cb67b519b509bd32b735a2", - "reference": "ea591a69d714216d29cb67b519b509bd32b735a2", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ca4a988488f61ac18f8f845445eabdd36f89aa8d", + "reference": "ca4a988488f61ac18f8f845445eabdd36f89aa8d", "shasum": "" }, "require": { @@ -1831,9 +1831,6 @@ "symfony/mime": "^5.4|^6.0", "symfony/process": "^5.4|^6.0" }, - "suggest": { - "symfony/process": "" - }, "type": "library", "autoload": { "psr-4": { @@ -1860,7 +1857,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v6.2.5" + "source": "https://github.com/symfony/browser-kit/tree/v6.3.2" }, "funding": [ { @@ -1876,20 +1873,20 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:38:09+00:00" + "time": "2023-07-06T06:56:43+00:00" }, { "name": "symfony/css-selector", - "version": "v6.2.5", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1" + "reference": "883d961421ab1709877c10ac99451632a3d6fa57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1", - "reference": "bf1b9d4ad8b1cf0dbde8b08e0135a2f6259b9ba1", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57", + "reference": "883d961421ab1709877c10ac99451632a3d6fa57", "shasum": "" }, "require": { @@ -1925,7 +1922,74 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.2.5" + "source": "https://github.com/symfony/css-selector/tree/v6.3.2" + }, + "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": "2023-07-12T16:00:22+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-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.3.0" }, "funding": [ { @@ -1941,20 +2005,20 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:38:09+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "symfony/dom-crawler", - "version": "v6.2.5", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "19aa4962a0687e96941f0bdb27b794c5b73e2394" + "reference": "3fdd2a3d5fdc363b2e8dbf817f9726a4d013cbd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/19aa4962a0687e96941f0bdb27b794c5b73e2394", - "reference": "19aa4962a0687e96941f0bdb27b794c5b73e2394", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/3fdd2a3d5fdc363b2e8dbf817f9726a4d013cbd1", + "reference": "3fdd2a3d5fdc363b2e8dbf817f9726a4d013cbd1", "shasum": "" }, "require": { @@ -1966,9 +2030,6 @@ "require-dev": { "symfony/css-selector": "^5.4|^6.0" }, - "suggest": { - "symfony/css-selector": "" - }, "type": "library", "autoload": { "psr-4": { @@ -1995,7 +2056,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v6.2.5" + "source": "https://github.com/symfony/dom-crawler/tree/v6.3.4" }, "funding": [ { @@ -2011,20 +2072,20 @@ "type": "tidelift" } ], - "time": "2023-01-20T17:45:48+00:00" + "time": "2023-08-01T07:43:40+00:00" }, { "name": "symfony/phpunit-bridge", - "version": "v6.2.5", + "version": "v6.3.6", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "d759e5372de414bef53a688c7aa7e240e4fd8aa2" + "reference": "c6f1df6a76c2c12bd14a0a5bf7c556dd935efe1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/d759e5372de414bef53a688c7aa7e240e4fd8aa2", - "reference": "d759e5372de414bef53a688c7aa7e240e4fd8aa2", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/c6f1df6a76c2c12bd14a0a5bf7c556dd935efe1d", + "reference": "c6f1df6a76c2c12bd14a0a5bf7c556dd935efe1d", "shasum": "" }, "require": { @@ -2034,11 +2095,9 @@ "phpunit/phpunit": "<7.5|9.1.2" }, "require-dev": { - "symfony/deprecation-contracts": "^2.1|^3.0", - "symfony/error-handler": "^5.4|^6.0" - }, - "suggest": { - "symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/polyfill-php81": "^1.27" }, "bin": [ "bin/simple-phpunit" @@ -2078,7 +2137,7 @@ "description": "Provides utilities for PHPUnit, especially user deprecation notices management", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v6.2.5" + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.3.6" }, "funding": [ { @@ -2094,20 +2153,20 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:38:09+00:00" + "time": "2023-10-12T15:02:41+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "shasum": "" }, "require": { @@ -2122,7 +2181,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2160,7 +2219,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" }, "funding": [ { @@ -2176,20 +2235,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "42292d99c55abe617799667f454222c54c60e229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", "shasum": "" }, "require": { @@ -2204,7 +2263,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2243,7 +2302,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" }, "funding": [ { @@ -2259,30 +2318,30 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { "name": "symfony/test-pack", - "version": "1.0.10", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/symfony/test-pack.git", - "reference": "03ab012f4aab784690d21417f7cdd7b432fd4e73" + "reference": "7b708c588cb3e1c8f0281889f273b920cbddff9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/test-pack/zipball/03ab012f4aab784690d21417f7cdd7b432fd4e73", - "reference": "03ab012f4aab784690d21417f7cdd7b432fd4e73", + "url": "https://api.github.com/repos/symfony/test-pack/zipball/7b708c588cb3e1c8f0281889f273b920cbddff9b", + "reference": "7b708c588cb3e1c8f0281889f273b920cbddff9b", "shasum": "" }, "require": { - "phpunit/phpunit": "*", + "phpunit/phpunit": "^9.5", "symfony/browser-kit": "*", "symfony/css-selector": "*", "symfony/phpunit-bridge": "*" }, "require-dev": { - "phpunit/phpunit": "*", + "phpunit/phpunit": "^9.5", "symfony/browser-kit": "*", "symfony/css-selector": "*", "symfony/phpunit-bridge": "*" @@ -2295,7 +2354,91 @@ "description": "A pack for functional and end-to-end testing within a Symfony app", "support": { "issues": "https://github.com/symfony/test-pack/issues", - "source": "https://github.com/symfony/test-pack/tree/1.0.10" + "source": "https://github.com/symfony/test-pack/tree/v1.1.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": "2023-02-06T16:00:54+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "999ede244507c32b8e43aebaa10e9fce20de7c97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/999ede244507c32b8e43aebaa10e9fce20de7c97", + "reference": "999ede244507c32b8e43aebaa10e9fce20de7c97", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.3.6" }, "funding": [ { @@ -2311,7 +2454,7 @@ "type": "tidelift" } ], - "time": "2022-04-15T12:25:45+00:00" + "time": "2023-10-12T18:45:56+00:00" }, { "name": "theseer/tokenizer", diff --git a/phpstan.neon b/phpstan.neon index dccb5b2..30af974 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,9 +2,10 @@ parameters: inferPrivatePropertyTypeFromConstructor: true checkGenericClassInNonGenericObjectType: false checkMissingIterableValueType: false + treatPhpDocTypesAsCertain: false paths: - %currentWorkingDirectory%/src ignoreErrors: - - '#Method .* should return array<\w+> but returns array\.#' + - '#Method .* should return array but returns array\.#' - '#Cannot cast mixed to int\.#' - '#Cannot cast mixed to string\.#' \ No newline at end of file diff --git a/src/Client.php b/src/Client.php index 6b7dc45..f8c285c 100644 --- a/src/Client.php +++ b/src/Client.php @@ -12,11 +12,15 @@ namespace Ang3\Component\Odoo; use Ang3\Component\Odoo\DBAL\Expression\ExpressionBuilder; +use Ang3\Component\Odoo\Enum\OdooMethod; +use Ang3\Component\Odoo\Enum\OdooService; use Ang3\Component\Odoo\Exception\AuthenticationException; use Ang3\Component\Odoo\Exception\MissingConfigParameterException; use Ang3\Component\Odoo\Exception\RemoteException; use Ang3\Component\Odoo\Exception\RequestException; +use Ang3\Component\Odoo\Metadata\Version; use Ang3\Component\Odoo\Transport\JsonRpcPhpStreamTransport; +use Ang3\Component\Odoo\Transport\TransportException; use Ang3\Component\Odoo\Transport\TransportInterface; use Psr\Log\LoggerInterface; @@ -25,28 +29,6 @@ */ class Client { - /** - * Services. - */ - public const SERVICE_COMMON = 'common'; - public const SERVICE_OBJECT = 'object'; - - /** - * Query ORM methods. - */ - public const CREATE = 'create'; - public const WRITE = 'write'; - public const READ = 'read'; - public const UNLINK = 'unlink'; - public const SEARCH_READ = 'search_read'; - public const SEARCH = 'search'; - public const SEARCH_COUNT = 'search_count'; - - /** - * Special commands. - */ - public const LIST_FIELDS = 'fields_get'; - private TransportInterface $transport; private ExpressionBuilder $expressionBuilder; private ?int $uid = null; @@ -76,210 +58,11 @@ public static function create( return new self(Connection::create($config), $transport, $logger); } - /** - * Creates a new record and returns the new ID. - * - * @return int the ID of the new record - * - * @throws \InvalidArgumentException when $data is empty - * @throws RequestException when request failed - */ - public function insert(string $modelName, array $data): int - { - if (!$data) { - throw new \InvalidArgumentException('Data cannot be empty'); - } - - /** @var int[] $result */ - $result = (array) $this->execute($modelName, self::CREATE, [[$data]]); - - return (int) array_shift($result); - } - - /** - * Read records. - * - * @throws RequestException when request failed - */ - public function read(string $modelName, int|array $ids, array $options = []): array - { - $ids = \is_int($ids) ? [$ids] : $ids; - - return (array) $this->execute($modelName, self::READ, [$ids], $options); - } - - /** - * Update a record(s). - * - * @throws RequestException when request failed - */ - public function update(string $modelName, int|array $ids, array $data = []): void - { - if (!$data) { - return; - } - - $ids = \is_array($ids) ? $ids : [$ids]; - $this->execute($modelName, self::WRITE, [$ids, $data]); - } - - /** - * Delete record(s). - * - * @throws RequestException when request failed - */ - public function delete(string $modelName, int|array $ids): void - { - $ids = \is_array($ids) ? $ids : [(int) $ids]; - $this->execute($modelName, self::UNLINK, [$ids]); - } - - /** - * Search one ID of record by criteria and options. - * - * @throws \InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed - */ - public function searchOne(string $modelName, iterable $criteria = null, array $options = []): ?int - { - $options['limit'] = 1; - $result = $this->search($modelName, $criteria, $options); - - return array_shift($result); - } - - /** - * Search all ID of record(s) with options. - * - * @return array - * - * @throws \InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed - */ - public function searchAll(string $modelName, array $options = []): array - { - $options['fields'] = ['id']; - - return array_column($this->findBy($modelName, null, $options), 'id'); - } - - /** - * Find ID of record(s) by criteria and options. - * - * @return array - * - * @throws \InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed - */ - public function search(string $modelName, iterable $criteria = null, array $options = []): array - { - if (\array_key_exists('fields', $options)) { - unset($options['fields']); - } - - return (array) $this->execute($modelName, self::SEARCH, [$this->expressionBuilder->normalizeDomains($criteria)], $options); - } - - /** - * Find ONE record by ID and options. - * - * @throws RequestException when request failed - */ - public function find(string $modelName, int $id, array $options = []): ?array - { - return $this->findOneBy($modelName, [ - 'id' => $id, - ], $options); - } - - /** - * Find ONE record by criteria and options. - * - * @throws \InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed - */ - public function findOneBy(string $modelName, iterable $criteria = null, array $options = []): ?array - { - $result = $this->findBy($modelName, $criteria, $options); - - return array_pop($result); - } - - /** - * Find all record(s) with options. - * - * @return array - * - * @throws RequestException when request failed - */ - public function findAll(string $modelName, array $options = []): array - { - return $this->findBy($modelName, null, $options); - } - - /** - * Find record(s) by criteria and options. - * - * @return array[] - * - * @throws \InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed - */ - public function findBy(string $modelName, iterable $criteria = null, array $options = []): array - { - return (array) $this->execute($modelName, self::SEARCH_READ, [$this->expressionBuilder->normalizeDomains($criteria)], $options); - } - - /** - * Check if a record exists. - * - * @throws RequestException when request failed - */ - public function exists(string $modelName, int $id): bool - { - return 1 === $this->count($modelName, [ - 'id' => $id, - ]); - } - - /** - * Count all records for a model. - * - * @throws \InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed - */ - public function countAll(string $modelName): int - { - return $this->count($modelName); - } - - /** - * Count number of records for a model and criteria. - * - * @throws \InvalidArgumentException when $criteria value is not valid - * @throws RequestException when request failed - */ - public function count(string $modelName, iterable $criteria = null): int - { - return (int) $this->execute($modelName, self::SEARCH_COUNT, [$this->expressionBuilder->normalizeDomains($criteria)]); - } - - /** - * List model fields. - */ - public function listFields(string $modelName, array $options = []): array - { - return (array) $this->execute($modelName, self::LIST_FIELDS, [], $options); - } - - /** - * @return mixed - */ - public function execute(string $name, string $method, array $parameters = [], array $options = []) + public function execute(string $name, string $method, array $parameters = [], array $options = []): mixed { return $this->request( - self::SERVICE_OBJECT, - 'execute_kw', + OdooService::Object->value, + OdooMethod::ExecuteKw->value, $this->connection->getDatabase(), $this->authenticate(), $this->connection->getPassword(), @@ -290,9 +73,9 @@ public function execute(string $name, string $method, array $parameters = [], ar ); } - public function version(): array + public function version(): Version { - return (array) $this->request(self::SERVICE_COMMON, 'version'); + return Version::create((array) $this->request(OdooService::Common->value, OdooMethod::Version->value)); } /** @@ -302,8 +85,8 @@ public function authenticate(): int { if (null === $this->uid) { $this->uid = (int) $this->request( - self::SERVICE_COMMON, - 'login', + OdooService::Common->value, + OdooMethod::Login->value, $this->connection->getDatabase(), $this->connection->getUsername(), $this->connection->getPassword() @@ -320,9 +103,10 @@ public function authenticate(): int /** * @param mixed ...$arguments * - * @return mixed + * @throws RequestException on request errors + * @throws TransportException on transport errors */ - public function request(string $service, string $method, ...$arguments) + public function request(string $service, string $method, ...$arguments): mixed { $context = [ 'service' => $service, @@ -332,25 +116,17 @@ public function request(string $service, string $method, ...$arguments) 'request_id' => uniqid('rpc', true), ]; - if ($this->logger) { - $this->logger->info('JSON RPC request #{request_id} started - {service}::{method}({arguments}) (uid: #{uid})', $context); - } + $this->logger?->info('Odoo request #{request_id} - {service}::{method}({arguments}) (uid: #{uid})', $context); $runtime = microtime(true); $payload = $this->transport->request($service, $method, $arguments); $runtime = microtime(true) - $runtime; - if ($this->logger) { - $this->logger->info('JSON RPC request #{request_id} finished - Runtime: {runtime}s.', [ - 'request_id' => $context['request_id'], - 'runtime' => number_format($runtime, 3, '.', ' '), - ]); - - $this->logger->debug('JSON RPC payload debug.', [ - 'request_id' => $context['request_id'], - 'payload' => $payload, - ]); - } + $this->logger?->debug('Odoo request #{request_id} finished - Runtime: {runtime}s.', [ + 'request_id' => $context['request_id'], + 'runtime' => number_format($runtime, 3, '.', ' '), + 'payload' => $payload, + ]); if (\is_array($payload['error'] ?? null)) { throw RemoteException::create($payload); diff --git a/src/DBAL/Expression/Domain/CompositeDomain.php b/src/DBAL/Expression/Domain/CompositeDomain.php index 0f87611..1072d5f 100644 --- a/src/DBAL/Expression/Domain/CompositeDomain.php +++ b/src/DBAL/Expression/Domain/CompositeDomain.php @@ -39,6 +39,17 @@ public function __construct(private string $operator, private array $domains = [ { } + public static function criteria(array $criteria = []): self + { + $domains = []; + + foreach ($criteria as $fieldName => $value) { + $domains[] = new Comparison($fieldName, Comparison::EQUAL_TO, $value); + } + + return new self(self::AND, $domains); + } + public function __clone() { foreach ($this->domains as $key => $domain) { @@ -73,7 +84,7 @@ public function toArray(): array $domain = $this->prepare(); if (!($domain instanceof self)) { - return $domain ? $domain->toArray() : []; + return $domain ? [$domain->toArray()] : []; } $result = [$domain->getOperator()]; diff --git a/src/DBAL/Expression/ExpressionBuilder.php b/src/DBAL/Expression/ExpressionBuilder.php index ac2bf99..6b78867 100644 --- a/src/DBAL/Expression/ExpressionBuilder.php +++ b/src/DBAL/Expression/ExpressionBuilder.php @@ -232,38 +232,20 @@ public function clearRecords(): CollectionOperation public function normalizeDomains(iterable $criteria = null): array { if (!$criteria) { - return []; + return [[]]; } if (\is_array($criteria)) { - $normalizedCriteria = $this->andX(); - - foreach ($criteria as $fieldName => $value) { - $comparison = $this->eq($fieldName, $this->formatValue($value)); - - if (1 === \count($criteria)) { - $normalizedCriteria = $comparison; - break; - } - - $normalizedCriteria->add($comparison); - } - - $criteria = $normalizedCriteria; + $criteria = CompositeDomain::criteria($criteria); } if (!$criteria instanceof DomainInterface) { throw new \InvalidArgumentException(sprintf('Expected parameter #1 of type %s|array<%s|array>, %s given', DomainInterface::class, DomainInterface::class, \gettype($criteria))); } - /** @var array $criteriaArray */ $criteriaArray = $this->formatValue($criteria->toArray()); - if (!$criteriaArray) { - return $this->normalizeDomains(); - } - - return $criteria instanceof CompositeDomain ? $criteriaArray : [$criteriaArray]; + return $criteria instanceof CompositeDomain ? [$criteriaArray] : [[$criteriaArray]]; } /** diff --git a/src/DBAL/Query/OrmQuery.php b/src/DBAL/Query/OrmQuery.php index 48ae0ee..979d156 100644 --- a/src/DBAL/Query/OrmQuery.php +++ b/src/DBAL/Query/OrmQuery.php @@ -66,7 +66,7 @@ public function count(): int } $query = new self($this->recordManager, $this->name, self::SEARCH_COUNT); - $query->setParameters($this->recordManager->getExpressionBuilder()->normalizeDomains($this->parameters)); + $query->setParameters($this->parameters); return (int) $query->execute(); } diff --git a/src/DBAL/Query/QueryBuilder.php b/src/DBAL/Query/QueryBuilder.php index 6295ab1..872a815 100644 --- a/src/DBAL/Query/QueryBuilder.php +++ b/src/DBAL/Query/QueryBuilder.php @@ -60,7 +60,7 @@ public function select($fields = null): self $this->values = []; $this->ids = []; - $fields = $fields ? (array) $fields : []; + $fields = array_filter(\is_array($fields) ? $fields : [$fields]); foreach ($fields as $fieldName) { $this->addSelect($fieldName); @@ -217,11 +217,9 @@ public function setValues(array $values = []): self /** * Set a field value in case of query of type "INSERT" or "UPDATE". * - * @param mixed $value - * * @throws QueryException when the type of the query is not "INSERT" nor "UPDATE" */ - public function set(string $fieldName, $value): self + public function set(string $fieldName, mixed $value): self { if (!\in_array($this->type, [self::INSERT, self::UPDATE], true)) { throw new QueryException('You can set values in query of type "INSERT" or "UPDATE" only.'); @@ -423,6 +421,8 @@ public function getQuery(): OrmQuery } $parameters = [$this->ids, $parameters]; + } else { + $parameters = [$parameters]; } } diff --git a/src/DBAL/RecordManager.php b/src/DBAL/RecordManager.php index 47b6739..4da0d86 100644 --- a/src/DBAL/RecordManager.php +++ b/src/DBAL/RecordManager.php @@ -12,16 +12,17 @@ namespace Ang3\Component\Odoo\DBAL; use Ang3\Component\Odoo\Client; -use Ang3\Component\Odoo\DBAL\Expression\Domain\DomainInterface; use Ang3\Component\Odoo\DBAL\Expression\ExpressionBuilder; use Ang3\Component\Odoo\DBAL\Query\NativeQuery; use Ang3\Component\Odoo\DBAL\Query\OrmQuery; use Ang3\Component\Odoo\DBAL\Query\QueryBuilder; use Ang3\Component\Odoo\DBAL\Query\QueryInterface; -use Ang3\Component\Odoo\DBAL\Repository\RecordNotFoundException; use Ang3\Component\Odoo\DBAL\Repository\RecordRepository; use Ang3\Component\Odoo\DBAL\Schema\Schema; +/** + * @author Joanis ROUANET + */ class RecordManager { private Schema $schema; @@ -36,182 +37,6 @@ public function __construct(private readonly Client $client, private array $repo $this->expressionBuilder = new ExpressionBuilder(); } - /** - * Create a new record. - * - * @return int the ID of the new record - */ - public function create(string $modelName, array $data): int - { - return $this - ->getRepository($modelName) - ->insert($data) - ; - } - - /** - * Update record(s). - * - * NB: It is not currently possible to perform “computed” updates (by criteria). - * To do it, you have to perform a search then an update with search result IDs. - * - * @param array|int $ids - */ - public function update(string $modelName, $ids, array $data = []): void - { - $this - ->getRepository($modelName) - ->update($ids, $data) - ; - } - - /** - * Delete record(s). - * - * NB: It is not currently possible to perform “computed” deletes (by criteria). - * To do it, you have to perform a search then a delete with search result IDs. - * - * @param array|int $ids - */ - public function delete(string $modelName, $ids): void - { - $this - ->getRepository($modelName) - ->delete($ids) - ; - } - - /** - * Search one ID of record by criteria. - */ - public function searchOne(string $modelName, ?DomainInterface $criteria): ?int - { - return $this - ->getRepository($modelName) - ->searchOne($criteria) - ; - } - - /** - * Search all ID of record(s). - * - * @return int[] - */ - public function searchAll(string $modelName, array $orders = [], int $limit = null, int $offset = null): array - { - return $this - ->getRepository($modelName) - ->searchAll($orders, $limit, $offset) - ; - } - - /** - * Search ID of record(s) by criteria. - * - * @return int[] - */ - public function search(string $modelName, ?DomainInterface $criteria = null, array $orders = [], int $limit = null, int $offset = null): array - { - return $this - ->getRepository($modelName) - ->search($criteria, $orders, $limit, $offset) - ; - } - - /** - * Find ONE record by ID. - * - * @throws RecordNotFoundException when the record was not found - */ - public function read(string $modelName, int $id, array $fields = []): array - { - return $this - ->getRepository($modelName) - ->read($id, $fields) - ; - } - - /** - * Find ONE record by ID. - */ - public function find(string $modelName, int $id, array $fields = []): ?array - { - return $this - ->getRepository($modelName) - ->find($id, $fields) - ; - } - - /** - * Find ONE record by criteria. - */ - public function findOneBy(string $modelName, ?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $offset = null): ?array - { - return $this - ->getRepository($modelName) - ->findOneBy($criteria, $fields, $orders, $offset) - ; - } - - /** - * Find all records. - * - * @return array[] - */ - public function findAll(string $modelName, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array - { - return $this - ->getRepository($modelName) - ->findAll($fields, $orders, $limit, $offset) - ; - } - - /** - * Find record(s) by criteria. - * - * @return array[] - */ - public function findBy(string $modelName, ?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array - { - return $this - ->getRepository($modelName) - ->findBy($criteria, $fields, $orders, $limit, $offset) - ; - } - - /** - * Check if a record exists. - */ - public function exists(string $modelName, int $id): bool - { - return $this - ->getRepository($modelName) - ->exists($id) - ; - } - - /** - * Count number of all records for the model. - */ - public function countAll(string $modelName): int - { - return $this - ->getRepository($modelName) - ->countAll() - ; - } - - /** - * Count number of records for a model and criteria. - */ - public function count(string $modelName, ?DomainInterface $criteria = null): int - { - return $this - ->getRepository($modelName) - ->count($criteria) - ; - } - public function getRepository(string $modelName): RecordRepository { if (!\array_key_exists($modelName, $this->repositories)) { @@ -263,10 +88,10 @@ public function executeQuery(QueryInterface $query): mixed $options = $query->getOptions(); if (!$options) { - return $this->client->request($query->getName(), $query->getMethod(), $query->getParameters()); + return $this->client->execute($query->getName(), $query->getMethod(), $query->getParameters()); } - return $this->client->request($query->getName(), $query->getMethod(), $query->getParameters(), $options); + return $this->client->execute($query->getName(), $query->getMethod(), $query->getParameters(), $options); } public function getClient(): Client diff --git a/src/DBAL/Repository/RecordRepository.php b/src/DBAL/Repository/RecordRepository.php index 60644ed..e40f57f 100644 --- a/src/DBAL/Repository/RecordRepository.php +++ b/src/DBAL/Repository/RecordRepository.php @@ -11,11 +11,16 @@ namespace Ang3\Component\Odoo\DBAL\Repository; +use Ang3\Component\Odoo\DBAL\Expression\Domain\CompositeDomain; use Ang3\Component\Odoo\DBAL\Expression\Domain\DomainInterface; use Ang3\Component\Odoo\DBAL\Expression\ExpressionBuilder; use Ang3\Component\Odoo\DBAL\Query\QueryBuilder; use Ang3\Component\Odoo\DBAL\RecordManager; +use Ang3\Component\Odoo\DBAL\Schema\Model; +/** + * @author Joanis ROUANET + */ class RecordRepository { public function __construct(private RecordManager $recordManager, private readonly string $modelName) @@ -23,6 +28,14 @@ public function __construct(private RecordManager $recordManager, private readon $recordManager->addRepository($this); } + /** + * Gets the model metadata from the schema. + */ + public function getMetadata(): Model + { + return $this->recordManager->getSchema()->getModel($this->modelName); + } + /** * Insert a new record. * @@ -88,12 +101,12 @@ public function delete(int|array $ids): void /** * Search one ID of record by criteria. */ - public function searchOne(?DomainInterface $criteria): ?int + public function searchOne(DomainInterface|array|null $criteria): ?int { return (int) $this ->createQueryBuilder() ->search() - ->where($criteria) + ->where($this->normalizeCriteria($criteria)) ->getQuery() ->getOneOrNullScalarResult() ; @@ -114,13 +127,12 @@ public function searchAll(array $orders = [], int $limit = null, int $offset = n * * @return int[] */ - public function search(?DomainInterface $criteria = null, array $orders = [], int $limit = null, int $offset = null): array + public function search(DomainInterface|array|null $criteria = null, array $orders = [], int $limit = null, int $offset = null): array { - /* @var int[] $result */ return $this ->createQueryBuilder() ->search() - ->where($criteria) + ->where($this->normalizeCriteria($criteria)) ->setOrders($orders) ->setFirstResult($offset) ->setMaxResults($limit) @@ -156,7 +168,7 @@ public function find(int $id, array $fields = []): ?array /** * Find ONE record by criteria. */ - public function findOneBy(?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $offset = null): ?array + public function findOneBy(DomainInterface|array|null $criteria = null, array $fields = [], array $orders = [], int $offset = null): ?array { $result = $this->findBy($criteria, $fields, $orders, 1, $offset); @@ -178,12 +190,12 @@ public function findAll(array $fields = [], array $orders = [], int $limit = nul * * @return array[] */ - public function findBy(?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array + public function findBy(DomainInterface|array|null $criteria = null, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array { return $this ->createQueryBuilder() ->select($fields) - ->where($criteria) + ->where($this->normalizeCriteria($criteria)) ->setOrders($orders) ->setFirstResult($offset) ->setMaxResults($limit) @@ -211,12 +223,12 @@ public function countAll(): int /** * Count number of records for a model and criteria. */ - public function count(?DomainInterface $criteria = null): int + public function count(DomainInterface|array|null $criteria = null): int { return $this ->createQueryBuilder() ->select() - ->where($criteria) + ->where($this->normalizeCriteria($criteria)) ->getQuery() ->count() ; @@ -251,4 +263,9 @@ public function expr(): ExpressionBuilder { return $this->recordManager->getExpressionBuilder(); } + + public function normalizeCriteria(DomainInterface|array|null $criteria = null): ?DomainInterface + { + return \is_array($criteria) ? CompositeDomain::criteria($criteria) : $criteria; + } } diff --git a/src/DBAL/Schema/Enum/DateTimeFormat.php b/src/DBAL/Schema/Enum/DateTimeFormat.php new file mode 100644 index 0000000..037b072 --- /dev/null +++ b/src/DBAL/Schema/Enum/DateTimeFormat.php @@ -0,0 +1,18 @@ +id = (int) $data['id']; $this->name = (string) $data['name']; - $this->type = (string) $data['ttype']; + $this->type = FieldType::from((string) $data['ttype']); $this->required = (bool) $data['required']; $this->readOnly = (bool) $data['readonly']; $this->displayName = $data['display_name'] ?? null; @@ -85,7 +64,7 @@ public function getName(): string return $this->name; } - public function getType(): string + public function getType(): FieldType { return $this->type; } @@ -132,22 +111,22 @@ public function isIdentifier(): bool public function isBinary(): bool { - return self::T_BINARY === $this->type; + return FieldType::Binary === $this->type; } public function isBoolean(): bool { - return self::T_BOOLEAN === $this->type; + return FieldType::Boolean === $this->type; } public function isInteger(): bool { - return self::T_INTEGER === $this->type; + return FieldType::Integer === $this->type; } public function isFloat(): bool { - return \in_array($this->type, [self::T_FLOAT, self::T_MONETARY], true); + return \in_array($this->type, [FieldType::Float, FieldType::Monetary], true); } public function isNumber(): bool @@ -157,22 +136,22 @@ public function isNumber(): bool public function isString(): bool { - return \in_array($this->type, [self::T_CHAR, self::T_TEXT, self::T_HTML], true); + return \in_array($this->type, [FieldType::Char, FieldType::Text, FieldType::Html], true); } public function isDate(): bool { - return \in_array($this->type, [self::T_DATE, self::T_DATETIME], true); + return \in_array($this->type, [FieldType::Date, FieldType::DateTime], true); } - public function getDateFormat(): string + public function getDateFormat(): DateTimeFormat { - return self::T_DATETIME === $this->type ? self::DATETIME_FORMAT : self::DATE_FORMAT; + return FieldType::DateTime === $this->type ? DateTimeFormat::Long : DateTimeFormat::Short; } public function isSelection(): bool { - return self::T_SELECTION === $this->type; + return FieldType::Selection === $this->type; } public function isSelectable(): bool @@ -182,23 +161,19 @@ public function isSelectable(): bool public function isAssociation(): bool { - return \in_array($this->type, [ - self::T_MANY_TO_ONE, - self::T_MANY_TO_MANY, - self::T_ONE_TO_MANY, - ], true); + return $this->isSingleAssociation() || $this->isMultipleAssociation(); } public function isSingleAssociation(): bool { - return self::T_MANY_TO_ONE === $this->type; + return FieldType::ManyToOne === $this->type; } public function isMultipleAssociation(): bool { return \in_array($this->type, [ - self::T_MANY_TO_MANY, - self::T_ONE_TO_MANY, + FieldType::ManyToMany, + FieldType::OneToMany, ], true); } } diff --git a/src/DBAL/Schema/Model.php b/src/DBAL/Schema/Model.php index 0895f3d..e118275 100644 --- a/src/DBAL/Schema/Model.php +++ b/src/DBAL/Schema/Model.php @@ -67,7 +67,7 @@ public function hasField(string $fieldName): bool { try { $this->getField($fieldName); - } catch (SchemaException $exception) { + } catch (SchemaException) { return false; } @@ -75,12 +75,18 @@ public function hasField(string $fieldName): bool } /** - * @throws SchemaException when the field was not found + * @throws \InvalidArgumentException when the field name is empty + * @throws SchemaException when the field was not found */ public function getField(string $fieldName): Field { $model = $this; $fields = explode('.', $fieldName); + + if (!$fields) { + throw new \InvalidArgumentException('Empty field name.'); + } + $lastKey = \count($fields) - 1; foreach ($fields as $key => $subFieldName) { diff --git a/src/Enum/OdooMethod.php b/src/Enum/OdooMethod.php new file mode 100644 index 0000000..ae5289a --- /dev/null +++ b/src/Enum/OdooMethod.php @@ -0,0 +1,19 @@ +getName(); + } + + public function getName(): string + { + return sprintf('%s.%s.%s+%s', $this->majorVersion, $this->minorVersion, $this->patchVersion, $this->buildVersion); + } + + public function getMajorVersion(): int + { + return $this->majorVersion; + } + + public function getMinorVersion(): int + { + return $this->minorVersion; + } + + public function getPatchVersion(): int + { + return $this->patchVersion; + } + + public function getBuildName(): string + { + return $this->buildName; + } + + public function getBuildIdentifier(): string + { + return $this->buildIdentifier; + } + + public function getBuildVersion(): string + { + return $this->buildVersion; + } + + public function getProtocolVersion(): int + { + return $this->protocolVersion; + } +} diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index a13aabe..9d59cc7 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -12,7 +12,6 @@ namespace Ang3\Component\Odoo\Transport; use Ang3\Component\Odoo\Connection; -use Ang3\Component\Odoo\Exception\RequestException; /** * @author Joanis ROUANET @@ -36,7 +35,7 @@ public function request(string $service, string $method, array $arguments = []): $payload = json_encode($this->normalizeRpcData($service, $method, $arguments)); if (JSON_ERROR_NONE !== json_last_error()) { - throw new RequestException(sprintf('Failed to encode data to JSON: %s', json_last_error_msg())); + throw new TransportException(sprintf('Failed to encode data to JSON: %s', json_last_error_msg())); } $context = stream_context_create([ @@ -52,13 +51,13 @@ public function request(string $service, string $method, array $arguments = []): $request = file_get_contents($url, false, $context); if (false === $request) { - throw new RequestException('Unable to connect to Odoo.'); + throw new TransportException('JSON RPC request failed - Unable to get stream contents.'); } $data = json_decode($request, true); if (JSON_ERROR_NONE !== json_last_error()) { - throw new RequestException(sprintf('Failed to decode JSON data: %s', json_last_error_msg())); + throw new TransportException(sprintf('Failed to decode JSON data: %s', json_last_error_msg())); } return (array) $data; diff --git a/src/Transport/TransportException.php b/src/Transport/TransportException.php new file mode 100644 index 0000000..9b583da --- /dev/null +++ b/src/Transport/TransportException.php @@ -0,0 +1,21 @@ + + */ +class TransportException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Transport/TransportInterface.php b/src/Transport/TransportInterface.php index 0f19aad..161e466 100644 --- a/src/Transport/TransportInterface.php +++ b/src/Transport/TransportInterface.php @@ -11,8 +11,6 @@ namespace Ang3\Component\Odoo\Transport; -use Ang3\Component\Odoo\Exception\RequestException; - /** * @author Joanis ROUANET */ @@ -23,7 +21,7 @@ interface TransportInterface /** * Make a request to Odoo database. * - * @throws RequestException when request failed + * @throws TransportException on transport errors */ public function request(string $service, string $method, array $arguments = []): array; } diff --git a/tests/DBAL/Expression/CompositeDomainTest.php b/tests/DBAL/Expression/CompositeDomainTest.php index 913a2b6..6306525 100644 --- a/tests/DBAL/Expression/CompositeDomainTest.php +++ b/tests/DBAL/Expression/CompositeDomainTest.php @@ -68,7 +68,7 @@ public function provideToArrayDataSet(): array ], [ // 1 CompositeDomain::AND, [$domainA], - $domainA->toArray(), + [$domainA->toArray()], ], [ // 2 CompositeDomain::AND, [$domainA, $domainB], @@ -83,7 +83,7 @@ public function provideToArrayDataSet(): array ], [ // 5 CompositeDomain::OR, [$domainA], - $domainA->toArray(), + [ $domainA->toArray() ], ], [ // 6 CompositeDomain::OR, [$domainA, $domainB], diff --git a/tests/Utils/Debugger.php b/tests/Utils/Debugger.php deleted file mode 100644 index 1a58036..0000000 --- a/tests/Utils/Debugger.php +++ /dev/null @@ -1,35 +0,0 @@ - null, self::VALUE => null, self::RESULT => null, @@ -75,7 +56,6 @@ public function __construct(TestCase $testCase, object $object, array $defaultCo parent::__construct($testCase); $this->reflector = new Reflector(); - $this->debugger = new Debugger(); $this->setObject($object); $this->defaultContext = array_merge($this->defaultContext, $defaultContext); } @@ -83,10 +63,8 @@ public function __construct(TestCase $testCase, object $object, array $defaultCo /** * Test the accessors of a property (setter and getter). * This test also checks if the return value of the getter is equal to the value registered with the setter. - * - * @param mixed $value */ - public function assertPropertyAccessorsAndMutators(string $propertyName, $value, array $context = []): self + public function assertPropertyAccessorsAndMutators(string $propertyName, mixed $value, array $context = []): self { $context = $this->getContext($context); $context[self::VALUE] = $value; @@ -112,10 +90,7 @@ public function assertPropertyAccessorsAndMutators(string $propertyName, $value, return $this; } - /** - * @param mixed $value - */ - public function assertHasser(string $propertyName, $value, array $context = []): ?\ReflectionMethod + public function assertHasser(string $propertyName, mixed $value, array $context = []): ?\ReflectionMethod { $context[self::VALUE] = $value; $context[self::IS_FLUENT] = false; @@ -140,10 +115,7 @@ public function assertGetter(string $propertyName, array $context = []): ?\Refle return $this->assertPropertyMethod($propertyName, self::ACCESSOR_GET, $context); } - /** - * @param mixed $value - */ - public function assertAdder(string $propertyName, $value, array $context = []): ?\ReflectionMethod + public function assertAdder(string $propertyName, mixed $value, array $context = []): ?\ReflectionMethod { $context[self::VALUE] = $value; $context[self::IS_FLUENT] ??= true; @@ -156,10 +128,7 @@ public function assertAdder(string $propertyName, $value, array $context = []): return $this->assertPropertyMethod($propertyName, self::MUTATOR_ADD, $context); } - /** - * @param mixed $value - */ - public function assertRemover(string $propertyName, $value, array $context = []): ?\ReflectionMethod + public function assertRemover(string $propertyName, mixed $value, array $context = []): ?\ReflectionMethod { $context[self::VALUE] = $value; $context[self::IS_FLUENT] ??= true; @@ -174,10 +143,8 @@ public function assertRemover(string $propertyName, $value, array $context = []) /** * Test and return the setter of a property. - * - * @param mixed $value */ - public function assertSetter(string $propertyName, $value = null, array $context = []): ?\ReflectionMethod + public function assertSetter(string $propertyName, mixed $value = null, array $context = []): ?\ReflectionMethod { $context[self::VALUE] = $value; $context[self::IS_FLUENT] ??= true; @@ -201,8 +168,6 @@ public function assertPropertyMethod(string $propertyName, string $prefix, array $property = $this->reflector->getProperty($this->class, $propertyName); } catch (\ReflectionException $e) { $this->testCase::fail($this->getContextErrorMessage('The property was not found', $context)); - - return null; } $context[self::NAME] = (string) ($context[self::NAME] ?? null); @@ -230,8 +195,6 @@ public function assertPropertyMethod(string $propertyName, string $prefix, array } catch (\ReflectionException $e) { $errorMessage = sprintf('None of methods "%s()" was found', implode('"(), "', $tested)); $this->testCase::fail($this->getContextErrorMessage($errorMessage, $context)); - - return null; } $this->testCase->addToAssertionCount(1); @@ -270,10 +233,8 @@ public function assertPropertyMethod(string $propertyName, string $prefix, array if (!is_iterable($propertyValue)) { $this->testCase::fail($this->getContextErrorMessage(sprintf( 'The property collection should be iterable, %s declared', - $this->debugger->debugType($propertyValue) + get_debug_type($propertyValue) ), $context)); - - return null; } $hasValue = false; @@ -291,7 +252,7 @@ public function assertPropertyMethod(string $propertyName, string $prefix, array sprintf( 'The property collection %s the value but the hasser returns %s', $hasValue ? 'contains' : 'does not contain', - $this->debugger->debugBool($result) + $result ? 'TRUE' : 'FALSE' ), $context )); @@ -381,18 +342,6 @@ public function setReflector(Reflector $reflector): self return $this; } - public function getDebugger(): Debugger - { - return $this->debugger; - } - - public function setDebugger(Debugger $debugger): self - { - $this->debugger = $debugger; - - return $this; - } - public function getDefaultContext(): array { return $this->defaultContext; diff --git a/tests/Utils/Reflector.php b/tests/Utils/Reflector.php index 9e26068..6b5e031 100644 --- a/tests/Utils/Reflector.php +++ b/tests/Utils/Reflector.php @@ -14,21 +14,17 @@ class Reflector { /** - * @return mixed - * * @throws \ReflectionException */ - public function getObjectValue(object $object, string $propertyName) + public function getObjectValue(object $object, string $propertyName): mixed { return $this->getProperty($object, $propertyName)->getValue($object); } /** - * @param mixed $value - * * @throws \ReflectionException */ - public function setObjectValue(object $object, string $propertyName, $value): \ReflectionProperty + public function setObjectValue(object $object, string $propertyName, mixed $value): \ReflectionProperty { $property = $this->getProperty($object, $propertyName); @@ -38,51 +34,36 @@ public function setObjectValue(object $object, string $propertyName, $value): \R } /** - * @param object|string $objectOrClass - * * @throws \ReflectionException */ - public function getMethod($objectOrClass, string $methodName, bool $setAccessible = true): ?\ReflectionMethod + public function getMethod(object|string $objectOrClass, string $methodName, bool $setAccessible = true): ?\ReflectionMethod { $class = $this->getClass($objectOrClass); - $method = $class->getMethod($methodName); - if ($setAccessible) { - $method->setAccessible(true); - } - - return $method; + return $class->getMethod($methodName); } /** - * @param object|string $objectOrClass - * * @throws \ReflectionException */ - public function getProperty($objectOrClass, string $propertyName, bool $setAccessible = true): ?\ReflectionProperty + public function getProperty(object|string $objectOrClass, string $propertyName, bool $setAccessible = true): ?\ReflectionProperty { $class = $this->getClass($objectOrClass); - $property = $class->getProperty($propertyName); - if ($setAccessible) { - $property->setAccessible(true); - } - - return $property; + return $class->getProperty($propertyName); } /** - * @param object|string $objectOrClass - * * @throws \ReflectionException */ - public function getClass($objectOrClass): \ReflectionClass + public function getClass(object|string $objectOrClass): \ReflectionClass { if (\is_string($objectOrClass)) { - /** @var class-string $class */ - $class = $objectOrClass; + if (!class_exists($objectOrClass)) { + throw new \RuntimeException(sprintf('The class "%s" was not found.', $objectOrClass)); + } - return new \ReflectionClass($class); + return new \ReflectionClass($objectOrClass); } return $objectOrClass instanceof \ReflectionClass ? $objectOrClass : new \ReflectionClass($objectOrClass); diff --git a/tests/Utils/TestDecorator.php b/tests/Utils/TestDecorator.php index 266beac..c448cd5 100644 --- a/tests/Utils/TestDecorator.php +++ b/tests/Utils/TestDecorator.php @@ -15,10 +15,7 @@ abstract class TestDecorator { - /** - * @var TestCase - */ - protected $testCase; + protected TestCase $testCase; public function __construct(TestCase $testCase) { From b7fbfeae23efd4eda807ae44cd768e5b62b2d98a Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Mon, 6 Nov 2023 14:35:46 +0100 Subject: [PATCH 45/80] Removed DBAL features --- README.md | 808 +----------------- docs/client/expression_builder.md | 245 ------ docs/custom_transport.md | 0 docs/index.md | 23 +- examples/expression_builder.php | 30 - phpstan.neon | 4 +- phpunit.xml.dist | 48 +- src/Client.php | 28 +- src/DBAL/Expression/Domain/Comparison.php | 121 --- .../Expression/Domain/CompositeDomain.php | 224 ----- src/DBAL/Expression/Domain/CustomDomain.php | 47 - .../Expression/Domain/DomainInterface.php | 20 - .../Exception/ConversionException.php | 19 - src/DBAL/Expression/ExpressionBuilder.php | 305 ------- .../Operation/CollectionOperation.php | 126 --- .../Operation/OperationInterface.php | 20 - src/DBAL/Query/AbstractQuery.php | 102 --- src/DBAL/Query/NativeQuery.php | 16 - src/DBAL/Query/NoResultException.php | 20 - src/DBAL/Query/NoUniqueResultException.php | 20 - src/DBAL/Query/OrmQuery.php | 193 ----- src/DBAL/Query/QueryBuilder.php | 485 ----------- src/DBAL/Query/QueryException.php | 16 - src/DBAL/Query/QueryInterface.php | 23 - src/DBAL/RecordManager.php | 116 --- .../Repository/RecordNotFoundException.php | 30 - src/DBAL/Repository/RecordRepository.php | 271 ------ src/DBAL/Schema/Choice.php | 37 - src/DBAL/Schema/Enum/DateTimeFormat.php | 18 - src/DBAL/Schema/Enum/FieldType.php | 33 - src/DBAL/Schema/Field.php | 179 ---- src/DBAL/Schema/Model.php | 138 --- src/DBAL/Schema/Schema.php | 129 --- src/DBAL/Schema/SchemaException.php | 25 - src/DBAL/Schema/Selection.php | 79 -- src/Exception/RemoteException.php | 4 +- .../TransportException.php | 4 +- src/Transport/AbstractRpcTransport.php | 43 - src/Transport/JsonRpcPhpStreamTransport.php | 25 +- src/Transport/TransportInterface.php | 6 +- tests/AbstractTest.php | 37 - tests/DBAL/Expression/AbstractDomainTest.php | 29 - tests/DBAL/Expression/ComparisonTest.php | 54 -- tests/DBAL/Expression/CompositeDomainTest.php | 152 ---- tests/Utils/ObjectTester.php | 361 -------- tests/Utils/Reflector.php | 71 -- tests/Utils/TestDecorator.php | 36 - 47 files changed, 111 insertions(+), 4709 deletions(-) delete mode 100644 docs/client/expression_builder.md create mode 100644 docs/custom_transport.md delete mode 100644 examples/expression_builder.php delete mode 100644 src/DBAL/Expression/Domain/Comparison.php delete mode 100644 src/DBAL/Expression/Domain/CompositeDomain.php delete mode 100644 src/DBAL/Expression/Domain/CustomDomain.php delete mode 100644 src/DBAL/Expression/Domain/DomainInterface.php delete mode 100644 src/DBAL/Expression/Exception/ConversionException.php delete mode 100644 src/DBAL/Expression/ExpressionBuilder.php delete mode 100644 src/DBAL/Expression/Operation/CollectionOperation.php delete mode 100644 src/DBAL/Expression/Operation/OperationInterface.php delete mode 100644 src/DBAL/Query/AbstractQuery.php delete mode 100644 src/DBAL/Query/NativeQuery.php delete mode 100644 src/DBAL/Query/NoResultException.php delete mode 100644 src/DBAL/Query/NoUniqueResultException.php delete mode 100644 src/DBAL/Query/OrmQuery.php delete mode 100644 src/DBAL/Query/QueryBuilder.php delete mode 100644 src/DBAL/Query/QueryException.php delete mode 100644 src/DBAL/Query/QueryInterface.php delete mode 100644 src/DBAL/RecordManager.php delete mode 100644 src/DBAL/Repository/RecordNotFoundException.php delete mode 100644 src/DBAL/Repository/RecordRepository.php delete mode 100644 src/DBAL/Schema/Choice.php delete mode 100644 src/DBAL/Schema/Enum/DateTimeFormat.php delete mode 100644 src/DBAL/Schema/Enum/FieldType.php delete mode 100644 src/DBAL/Schema/Field.php delete mode 100644 src/DBAL/Schema/Model.php delete mode 100644 src/DBAL/Schema/Schema.php delete mode 100644 src/DBAL/Schema/SchemaException.php delete mode 100644 src/DBAL/Schema/Selection.php rename src/{Transport => Exception}/TransportException.php (78%) delete mode 100644 src/Transport/AbstractRpcTransport.php delete mode 100644 tests/AbstractTest.php delete mode 100644 tests/DBAL/Expression/AbstractDomainTest.php delete mode 100644 tests/DBAL/Expression/ComparisonTest.php delete mode 100644 tests/DBAL/Expression/CompositeDomainTest.php delete mode 100644 tests/Utils/ObjectTester.php delete mode 100644 tests/Utils/Reflector.php delete mode 100644 tests/Utils/TestDecorator.php diff --git a/README.md b/README.md index 9e81585..aa05540 100644 --- a/README.md +++ b/README.md @@ -7,45 +7,37 @@ PHP Odoo API client [![Latest Unstable Version](https://poser.pugx.org/ang3/php-odoo-api-client/v/unstable)](https://packagist.org/packages/ang3/php-odoo-api-client) [![Total Downloads](https://poser.pugx.org/ang3/php-odoo-api-client/downloads)](https://packagist.org/packages/ang3/php-odoo-api-client) -Odoo API client using -[XML-RPC Odoo ORM External API](https://www.odoo.com/documentation/12.0/webservices/odoo.html). It allows -you call your odoo instance and manage records easily. +This package is a PHP library to connect and interact with an Odoo instance via JSON-RPC by default. +It follows [the official Odoo documentation](https://www.odoo.com/documentation/13.0/developer/misc/api/odoo.html). -**You are reading the documentation of version ```8.0```, if your version is older, please read -[this documentation (7.0.6)](https://github.com/Ang3/php-odoo-api-client/tree/v7.0.6).** -Please see the file [UPGRADE-8.0.md](https://github.com/Ang3/php-odoo-api-client/blob/7.0/UPGRADE-8.0.md) -to upgrade your version easily. +> From v8.x, this package is dedicated to the client only and PHP 8.1+. +> +> - For DBAL features, please report to the package [PHP Odoo DBAL](https://github.com/ang3/php-odoo-dbal). +> +> - For older versions of PHP, please use the version 7.x. -**Main features** +Odoo version support +-------------------- -- Authentication `<7.0` -- Basic JSON-RPC calls `>=8.0` -- Expression builder `<7.0` -- Database Abstraction Layer (DBAL) `>=7.0` - - Record manager - - Repositories - - Query builder - - Paginator `>=8.0` +| Odoo series | Compatibility | Comment | +|-------------|---------------|-----------------| +| v13.0+ | Unknown | Needs feedbacks | +| v13.0 | Yes | | +| v12.0 | Yes | | +| Older | Unknown | Needs feedbacks | -**Good to know** - -If you are in Symfony application you should be interested in the bundle -[ang3/odoo-bundle](https://github.com/Ang3/odoo-bundle) (client integration). +Getting started +=============== Requirements -============ - -- The PHP extension ```php-json``` must be enabled for JSON RPC calls. +------------ -| Odoo server | Compatibility | Comment | -|-------------|---------------|--------------------------------------------------------------------| -| newer | Unknown | Needs feddback | -| v13.0 | Yes | Some Odoo model names changed (e.g account.invoice > account.move) | -| v12.0 | Yes | First tested version | -| < v12 | Unknown | Needs feddback | +PHP version 8.1 or newer to develop using the client. Other requirements, such as PHP extensions, are enforced by +composer. See the `require` section of [composer.json file](../composer.json) +for details. Installation -============ +------------ Open a command console, enter your project directory and execute the following command to download the latest stable version of the client: @@ -59,9 +51,11 @@ in the [installation chapter](https://getcomposer.org/doc/00-intro.md) of the Composer documentation. Basic usage -=========== +----------- -First, you have to create a client instance: +### Create a client + +First, create a client instance statically with your config: ```php ', '', '', '', $logger = null); - -// Option 2 : by calling the static method ::create() with configuration as array $client = Client::create([ - 'url' => '', - 'database' => '', - 'username' => '', - 'password' => '', + 'url' => '', + 'database' => '', + 'username' => '', + 'password' => '', ], $logger = null); ``` @@ -86,750 +76,44 @@ Exceptions: - ```Ang3\Component\Odoo\Exception\MissingConfigParameterException``` when a required parameter is missing from the static method ```create()```. -Then, make your call: +### Make a request ```php -$result = $client->execute($name, $method, $parameters = [], $options = []); +$result = $client->request('service_name', 'method_name', 'argument_1', 'argument_2'/*, ...*/); ``` Exceptions: - ```Ang3\Component\Odoo\Exception\AuthenticationException``` when authentication failed. - ```Ang3\Component\Odoo\Exception\RequestException``` when request failed. +- ```Ang3\Component\Odoo\Exception\TransportException``` on transport errors. These previous exception can be thrown by all methods of the client. -DBAL (Database Abstraction Layer) -================================= - -First of all, Odoo is a database. Each "model" is a table and has its own fields. - -> DBAL features was added in version ```7.0``` - If your version is older, please use the built-in -ORM methods of the client like explained in the -> [dedicated documentation](https://github.com/Ang3/php-odoo-api-client/tree/v6.1.3): -be aware that these client ORM methods are deprecated since version ```7.0```. - -Record manager --------------- - -The client provides a record manager to manage records of your Odoo models. - -You can get the related manager of the client like below: - -```php -$recordManager = $client->getRecordManager(); -``` - -You can also create your own with a client instance: - -```php -use Ang3\Component\Odoo\DBAL\RecordManager; - -/** @var \Ang3\Component\Odoo\Client $myClient */ -$recordManager = new RecordManager($myClient); -``` - -### Built-in ORM methods - -Here is all built-in ORM methods provided by the record manager: - -```php -use Ang3\Component\Odoo\DBAL\Expression\Domain\DomainInterface; - -/** - * Create a new record. - * - * @return int the ID of the new record - */ -public function create(string $modelName, array $data): int; - -/** - * Update record(s). - * - * NB: It is not currently possible to perform “computed” updates (by criteria). - * To do it, you have to perform a search then an update with search result IDs. - * - * @param array|int $ids - */ -public function update(string $modelName, $ids, array $data = []): void; - -/** - * Delete record(s). - * - * NB: It is not currently possible to perform “computed” deletes (by criteria). - * To do it, you have to perform a search then a delete with search result IDs. - * - * @param array|int $ids - */ -public function delete(string $modelName, $ids): void; - -/** - * Search one ID of record by criteria. - */ -public function searchOne(string $modelName, ?DomainInterface $criteria): ?int; - -/** - * Search all ID of record(s). - * - * @return int[] - */ -public function searchAll(string $modelName, array $orders = [], int $limit = null, int $offset = null): array; - -/** - * Search ID of record(s) by criteria. - * - * @return int[] - */ -public function search(string $modelName, ?DomainInterface $criteria = null, array $orders = [], int $limit = null, int $offset = null): array; - -/** - * Find ONE record by ID. - * - * @throws RecordNotFoundException when the record was not found - */ -public function read(string $modelName, int $id, array $fields = []): array; - -/** - * Find ONE record by ID. - */ -public function find(string $modelName, int $id, array $fields = []): ?array; - -/** - * Find ONE record by criteria. - */ -public function findOneBy(string $modelName, ?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $offset = null): ?array; - -/** - * Find all records. - * - * @return array[] - */ -public function findAll(string $modelName, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array; - -/** - * Find record(s) by criteria. - * - * @return array[] - */ -public function findBy(string $modelName, ?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array; - -/** - * Check if a record exists. - */ -public function exists(string $modelName, int $id): bool; - -/** - * Count number of all records for the model. - */ -public function countAll(string $modelName): int; - -/** - * Count number of records for a model and criteria. - */ -public function count(string $modelName, ?DomainInterface $criteria = null): int; -``` - -For ```$criteria``` in select/search queries and ```$data``` for data writing context, please read the section -[Expression builder](#expression-builder). - -Schema ------- - -You can get the schema of your Odoo database by calling the getter method -```RecordManager::getSchema()```: - -```php -/** @var \Ang3\Component\Odoo\DBAL\Schema\Schema $schema */ -$schema = $recordManager->getSchema(); -``` - -The schema helps you to get all model names or get metadata of a model. - -### Get all model names - -```php -/** @var string[] $modelNames */ -$modelNames = $schema->getModelNames(); -``` - -### Get model metadata - -```php -/** @var \Ang3\Component\Odoo\DBAL\Schema\Model $model */ -$model = $schema->getModel('res.company'); -``` - -An exception of type ```Ang3\Component\Odoo\DBAL\Schema\SchemaException``` is thrown if the model -does not exist. - -Query builder -------------- - -It helps you to create queries easily by chaining helpers methods (like Doctrine for SQL databases). - -### Create a query builder - -```php -/** @var string|null $modelName */ -$queryBuilder = $recordManager->createQueryBuilder($modelName); -``` - -The variable ```$modelName``` represents the target model of your query (clause ```from```). - -### Build your query - -Here is a complete list of helper methods available in ```QueryBuilder```: - -```php -/** - * Defines the query of type "SELECT" with selected fields. - * No fields selected = all fields returned. - * - * @param array|string|null $fields - */ -public function select($fields = null): self; - -/** - * Defines the query of type "SEARCH". - */ -public function search(): self; - -/** - * Defines the query of type "INSERT". - */ -public function insert(): self; - -/** - * Defines the query of type "UPDATE" with ids of records to update. - * - * @param int[] $ids - */ -public function update(array $ids): self; - -/** - * Defines the query of type "DELETE" with ids of records to delete. - */ -public function delete(array $ids): self; - -/** - * Adds a field to select. - * - * @throws LogicException when the type of the query is not "SELECT". - */ -public function addSelect(string $fieldName): self; - -/** - * Gets selected fields. - */ -public function getSelect(): array; - -/** - * Sets the target model name. - */ -public function from(string $modelName): self; - -/** - * Gets the target model name of the query. - */ -public function getFrom(): ?string; - -/** - * Sets target IDs in case of query of type "UPDATE" or "DELETE". - * - * @throws LogicException when the type of the query is not "UPDATE" nor "DELETE". - */ -public function setIds(array $ids): self; - -/** - * Adds target ID in case of query of type "UPDATE" or "DELETE". - * - * @throws LogicException when the type of the query is not "UPDATE" nor "DELETE". - */ -public function addId(int $id): self; - -/** - * Sets field values in case of query of type "INSERT" or "UPDATE". - * - * @throws LogicException when the type of the query is not "INSERT" nor "UPDATE". - */ -public function setValues(array $values = []): self; - -/** - * Set a field value in case of query of type "INSERT" or "UPDATE". - * - * @param mixed $value - * - * @throws LogicException when the type of the query is not "INSERT" nor "UPDATE". - */ -public function set(string $fieldName, $value): self; - -/** - * Gets field values set in case of query of type "INSERT" or "UPDATE". - */ -public function getValues(): array; - -/** - * Sets criteria for queries of type "SELECT" and "SEARCH". - * - * @throws LogicException when the type of the query is not "SELECT" not "SEARCH". - */ -public function where(?DomainInterface $domain = null): self; - -/** - * Takes the WHERE clause and adds a node with logical operator AND. - * - * @throws LogicException when the type of the query is not "SELECT" nor "SEARCH". - */ -public function andWhere(DomainInterface $domain): self; - -/** - * Takes the WHERE clause and adds a node with logical operator OR. - * - * @throws LogicException when the type of the query is not "SELECT" nor "SEARCH". - */ -public function orWhere(DomainInterface $domain): self; - -/** - * Gets the WHERE clause. - */ -public function getWhere(): ?DomainInterface; - -/** - * Sets orders. - */ -public function setOrders(array $orders = []): self; - -/** - * Clears orders and adds one. - */ -public function orderBy(string $fieldName, bool $isAsc = true): self; - -/** - * Adds order. - * - * @throws LogicException when the query type is not valid. - */ -public function addOrderBy(string $fieldName, bool $isAsc = true): self; - -/** - * Gets ordered fields. - */ -public function getOrders(): array; - -/** - * Sets the max results of the query (limit). - */ -public function setMaxResults(?int $maxResults): self; - -/** - * Gets the max results of the query. - */ -public function getMaxResults(): ?int; - -/** - * Sets the first results of the query (offset). - */ -public function setFirstResult(?int $firstResult): self; - -/** - * Gets the first results of the query. - */ -public function getFirstResult(): ?int; -``` - -Then, build your query like below: +### ExecuteKw -```php -$query = $queryBuilder->getQuery(); -``` - -Your query is an instance of ```Ang3\Component\Odoo\DBAL\Query\OrmQuery```. - -### Execute your query - -You can get/count results or execute insert/update/delete by differents ways depending on the query type. +The client has a shortcut for the Odoo method `executeKw` of Odoo service `object`. +By calling the method `executeKw`, the client tries to authenticate then makes the request and returns result. ```php -/** - * Counts the number of records from parameters. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws QueryException on invalid query method. - */ -public function count(): int; - -/** - * Gets just ONE scalar result. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @return bool|int|float|string - * - * @throws NoUniqueResultException on no unique result - * @throws NoResultException on no result - * @throws QueryException on invalid query method. - */ -public function getSingleScalarResult(); - -/** - * Gets one or NULL scalar result. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @return bool|int|float|string|null - * - * @throws NoUniqueResultException on no unique result - * @throws QueryException on invalid query method. - */ -public function getOneOrNullScalarResult(); - -/** - * Gets a list of scalar result. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws QueryException on invalid query method. - * - * @return array - */ -public function getScalarResult(): array; - -/** - * Gets one row. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws NoUniqueResultException on no unique result - * @throws NoResultException on no result - * @throws QueryException on invalid query method. - */ -public function getSingleResult(): array; - -/** - * Gets one or NULL row. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws NoUniqueResultException on no unique result - * @throws QueryException on invalid query method. - */ -public function getOneOrNullResult(): ?array; - -/** - * Gets all result rows. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws QueryException on invalid query method. - */ -public function getResult(): array; - -/** - * Execute the query. - * Allowed methods: all. - * - * @return mixed - */ -public function execute(); +$result = $client->executeKw('name', 'method', $parameters = [], $options = []); ``` -Repositories -============ - -Sometimes, you would want to keep your queries in memory to reuse it in your code. To do it, you should use -a repository. A repository is a class that helps you to isolate queries for a dedicated model. - -For example, let's create the repository for your companies and define a query to get all french companies: +The UID is stored in the client to process authentication once. You can retrieve the UID by calling the client getter: ```php -namespace App\Odoo\Repository; - -use Ang3\Component\Odoo\DBAL\RecordManager;use Ang3\Component\Odoo\DBAL\Repository\RecordRepository; - -class CompanyRepository extends RecordRepository -{ - public function __construct(RecordManager $recordManager) - { - parent::__construct($recordManager, 'res.company'); - } - - public function findFrenchCompanies(): array - { - return $this - ->createQueryBuilder() - ->select('name') - ->where($this->expr()->eq('country_id.code', 'FR')) - ->getQuery() - ->getResult(); - } -} +$uid = $client->getUid(); // int|null ``` -Note that Odoo will always return the record ID in the result, even if you didn't select it explicitly. - -Each repository is registered inside the record manager on construct. -That's why you can retrieve your repository directly from the record manager: +### Get the Odoo version ```php -/** @var \App\Odoo\Repository\CompanyRepository $companyRepository */ -$companyRepository = $recordManager->getRepository('res.company'); +$version = $client->version(); // \Ang3\Component\Odoo\Metadata\Version +dump($version); ``` -If no repository exists for a model, the default repository ```Ang3\Component\Odoo\DBAL\Repository\RecordRepository``` -is used. Last but not least, all repositories are stored into the related record manager to avoid creating multiple -instances of same repository. - -Expression builder -================== - -There are two kinds of expressions : ```domains``` for criteria -and ```collection operations``` in data writing context. -Odoo has its own array format for those expressions. -The aim of the expression builder is to provide some -helper methods to simplify your programmer's life. - -Here is an example of how to get a builder from a client or record manager: - -```php -$expr = $clientOrRecordManager->expr(); -// or $expr = $clientOrRecordManager->getExpressionBuilder(); -``` - -You can still use the expression builder as standalone by creating a new instance: - -```php -use Ang3\Component\Odoo\DBAL\Expression\ExpressionBuilder; - -$expr = new ExpressionBuilder(); -``` - -Domains -------- - -For all **select/search/count** queries, -Odoo is waiting for an array of [domains](https://www.odoo.com/documentation/13.0/reference/orm.html#search-domains) -with a *polish notation* for logical operations (```AND```, ```OR``` and ```NOT```). - -It could be quickly ugly to do a complex domain, but don't worry the builder makes all -for you. :-) - -Each domain builder method creates an instance of ```Ang3\Component\Odoo\Expression\Domain\DomainInterface```. -The only one method of this interface is ```toArray()``` to get a normalized array of the expression. - -To illustrate how to work with it, here is an example using ```ExpressionBuilder``` helper methods: - -```php -// Get the expression builder -$expr = $recordManager->expr(); - -$result = $recordManager->findBy('model_name', $expr->andX( // Logical node "AND" - $expr->gte('id', 10), // id >= 10 - $expr->lte('id', 100), // id <= 10 -)); -``` - -Of course, you can nest logical nodes: - -```php -$result = $recordManager->findBy('model_name', $expr->andX( - $expr->orX( - $expr->eq('A', 1), - $expr->eq('B', 1) - ), - $expr->orX( - $expr->eq('C', 1), - $expr->eq('D', 1), - $expr->eq('E', 1) - ) -)); -``` - -Internally, the client formats automatically all domains by calling the special builder -method ```normalizeDomains()```. - -Here is a complete list of helper methods available in ```ExpressionBuilder``` for domain expressions: - -```php -/** - * Create a logical operation "AND". - */ -public function andX(DomainInterface ...$domains): CompositeDomain; - -/** - * Create a logical operation "OR". - */ -public function orX(DomainInterface ...$domains): CompositeDomain; - -/** - * Create a logical operation "NOT". - */ -public function notX(DomainInterface ...$domains): CompositeDomain; - -/** - * Check if the field is EQUAL TO the value. - * - * @param mixed $value - */ -public function eq(string $fieldName, $value): Comparison; - -/** - * Check if the field is NOT EQUAL TO the value. - * - * @param mixed $value - */ -public function neq(string $fieldName, $value): Comparison; - -/** - * Check if the field is UNSET OR EQUAL TO the value. - * - * @param mixed $value - */ -public function ueq(string $fieldName, $value): Comparison; - -/** - * Check if the field is LESS THAN the value. - * - * @param mixed $value - */ -public function lt(string $fieldName, $value): Comparison; - -/** - * Check if the field is LESS THAN OR EQUAL the value. - * - * @param mixed $value - */ -public function lte(string $fieldName, $value): Comparison; - -/** - * Check if the field is GREATER THAN the value. - * - * @param mixed $value - */ -public function gt(string $fieldName, $value): Comparison; - -/** - * Check if the field is GREATER THAN OR EQUAL the value. - * - * @param mixed $value - */ -public function gte(string $fieldName, $value): Comparison; - -/** - * Check if the variable is LIKE the value. - * - * An underscore _ in the pattern stands for (matches) any single character - * A percent sign % matches any string of zero or more characters. - * - * If $strict is set to FALSE, the value pattern is "%value%" (automatically wrapped into signs %). - * - * @param mixed $value - */ -public function like(string $fieldName, $value, bool $strict = false, bool $caseSensitive = true): Comparison; - -/** - * Check if the field is IS NOT LIKE the value. - * - * @param mixed $value - */ -public function notLike(string $fieldName, $value, bool $caseSensitive = true): Comparison; - -/** - * Check if the field is IN values list. - */ -public function in(string $fieldName, array $values = []): Comparison; - -/** - * Check if the field is NOT IN values list. - */ -public function notIn(string $fieldName, array $values = []): Comparison; -``` - -Collection operations ---------------------- - -In data writing context with queries of type **insert/update**, Odoo allows you to manage ***toMany** collection -fields with special commands. - -Please read the [ORM documentation](https://www.odoo.com/documentation/13.0/reference/orm.html#openerp-models-relationals-format) -to known what we are talking about. - -The expression builder provides helper methods to build a well-formed *operation command*: -each operation method returns an instance of ```Ang3\Component\Odoo\DBAL\Expression\Operation\CollectionOperation```. -Like domains, the only one method of this interface is ```toArray()``` to get a normalized array of the expression. - -To illustrate how to work with operations, here is an example using ```ExpressionBuilder``` helper methods: - -```php -// Get the expression builder -$expr = $recordManager->expr(); - -// Prepare data for a new record -$data = [ - 'foo' => 'bar', - 'bar_ids' => [ // Field of type "manytoMany" - $expr->addRecord(3), // Add the record of ID 3 to the set - $expr->createRecord([ // Create a new sub record and add it to the set - 'bar' => 'baz' - // ... - ]) - ] -]; - -$result = $recordManager->create('model_name', $data); -``` - -Internally, the client formats automatically the whole query parameters for all writing methods -(```create``` and ```update```) by calling the special builder -method ```normalizeData()```. - -Here is a complete list of helper methods available in ```ExpressionBuilder``` for operation expressions: - -```php -/** - * Adds a new record created from data. - */ -public function createRecord(array $data): CollectionOperation; - -/** - * Updates an existing record of id $id with data. - * /!\ Can not be used in record CREATE query. - */ -public function updateRecord(int $id, array $data): CollectionOperation; - -/** - * Adds an existing record of id $id to the collection. - */ -public function addRecord(int $id): CollectionOperation; - -/** - * Removes the record of id $id from the collection, but does not delete it. - * /!\ Can not be used in record CREATE query. - */ -public function removeRecord(int $id): CollectionOperation; - -/** - * Removes the record of id $id from the collection, then deletes it from the database. - * /!\ Can not be used in record CREATE query. - */ -public function deleteRecord(int $id): CollectionOperation; - -/** - * Replaces all existing records in the collection by the $ids list, - * Equivalent to using the command "clear" followed by a command "add" for each id in $ids. - */ -public function replaceRecords(array $ids = []): CollectionOperation; - -/** - * Removes all records from the collection, equivalent to using the command "remove" on every record explicitly. - * /!\ Can not be used in record CREATE query. - */ -public function clearRecords(): CollectionOperation; -``` - -Data support ------------- - -- Scalar values are unchanged -- Arrays recursive conversion -- Objects of type ```\DateTimeInterface``` are automatically formatted into string in UTC timezone -- Iterable/generator are fetched into an array -- Non-iterable values are automatically casted to string - (so any non-supported objects must define the method ```__toString()```) +### Database Abstraction Layer (DBAL) -Resources -========= +You need to manage your Odoo database models? +Please see the package [Odoo DBAL](https://github.com/ang3/php-odoo-dbal) to execute queries like Doctrine. -- [CHANGELOG.md](CHANGELOG.md) \ No newline at end of file +That's it! \ No newline at end of file diff --git a/docs/client/expression_builder.md b/docs/client/expression_builder.md deleted file mode 100644 index c191b9d..0000000 --- a/docs/client/expression_builder.md +++ /dev/null @@ -1,245 +0,0 @@ -Expression builder -================== - -There are two kinds of expressions : ```domains``` for criteria -and ```collection operations``` in data writing context. -Odoo has its own array format for those expressions. -The aim of the expression builder is to provide some -helper methods to simplify your programmer's life. - -Here is an example of how to get a builder from the client: - -```php -$expr = $client->getExpressionBuilder(); -``` - -You can still use the expression builder as standalone by creating a new instance: - -```php -use Ang3\Component\Odoo\DBAL\Expression\ExpressionBuilder; - -$expr = new ExpressionBuilder(); -``` - -Domains -------- - -For all **select/search/count** queries, -Odoo is waiting for an array of [domains](https://www.odoo.com/documentation/13.0/reference/orm.html#search-domains) -with a *polish notation* for logical operations (```AND```, ```OR``` and ```NOT```). - -It could be quickly ugly to do a complex domain, but don't worry the builder makes all -for you. :-) - -Each domain builder method creates an instance of ```Ang3\Component\Odoo\Expression\Domain\DomainInterface```. -The only one method of this interface is ```toArray()``` to get a normalized array of the expression. - -To illustrate how to work with it, here is an example using ```ExpressionBuilder``` helper methods: - -```php -// Get the expression builder -$expr = $client->expr(); - -$result = $client->findBy('model_name', $expr->andX( // Logical node "AND" - $expr->gte('id', 10), // id >= 10 - $expr->lte('id', 100), // id <= 10 -)); -``` - -Of course, you can nest logical nodes: - -```php -$result = $client->findBy('model_name', $expr->andX( - $expr->orX( - $expr->eq('A', 1), - $expr->eq('B', 1) - ), - $expr->orX( - $expr->eq('C', 1), - $expr->eq('D', 1), - $expr->eq('E', 1) - ) -)); -``` - -Internally, the client formats automatically all domains by calling the special builder -method ```normalizeDomains()```. - -Here is a complete list of helper methods available in ```ExpressionBuilder``` for domain expressions: - -```php -/** - * Create a logical operation "AND". - */ -public function andX(DomainInterface ...$domains): CompositeDomain; - -/** - * Create a logical operation "OR". - */ -public function orX(DomainInterface ...$domains): CompositeDomain; - -/** - * Create a logical operation "NOT". - */ -public function notX(DomainInterface ...$domains): CompositeDomain; - -/** - * Check if the field is EQUAL TO the value. - * - * @param mixed $value - */ -public function eq(string $fieldName, $value): Comparison; - -/** - * Check if the field is NOT EQUAL TO the value. - * - * @param mixed $value - */ -public function neq(string $fieldName, $value): Comparison; - -/** - * Check if the field is UNSET OR EQUAL TO the value. - * - * @param mixed $value - */ -public function ueq(string $fieldName, $value): Comparison; - -/** - * Check if the field is LESS THAN the value. - * - * @param mixed $value - */ -public function lt(string $fieldName, $value): Comparison; - -/** - * Check if the field is LESS THAN OR EQUAL the value. - * - * @param mixed $value - */ -public function lte(string $fieldName, $value): Comparison; - -/** - * Check if the field is GREATER THAN the value. - * - * @param mixed $value - */ -public function gt(string $fieldName, $value): Comparison; - -/** - * Check if the field is GREATER THAN OR EQUAL the value. - * - * @param mixed $value - */ -public function gte(string $fieldName, $value): Comparison; - -/** - * Check if the variable is LIKE the value. - * - * An underscore _ in the pattern stands for (matches) any single character - * A percent sign % matches any string of zero or more characters. - * - * If $strict is set to FALSE, the value pattern is "%value%" (automatically wrapped into signs %). - * - * @param mixed $value - */ -public function like(string $fieldName, $value, bool $strict = false, bool $caseSensitive = true): Comparison; - -/** - * Check if the field is IS NOT LIKE the value. - * - * @param mixed $value - */ -public function notLike(string $fieldName, $value, bool $caseSensitive = true): Comparison; - -/** - * Check if the field is IN values list. - */ -public function in(string $fieldName, array $values = []): Comparison; - -/** - * Check if the field is NOT IN values list. - */ -public function notIn(string $fieldName, array $values = []): Comparison; -``` - -Collection operations ---------------------- - -In data writing context with queries of type ```insert``` or ```update```, -Odoo allows you to manage ***toMany** collection fields with special commands. - -Please read the [ORM documentation](https://www.odoo.com/documentation/13.0/reference/orm.html#openerp-models-relationals-format) -to get more information about. - -The expression builder provides helper methods to build a well-formed *operation command*: -each operation method returns an instance of ```Ang3\Component\Odoo\Expression\Operation\CollectionOperation```. -Like domains, the only one method of this interface is ```toArray()``` to get a normalized array of the expression. - -To illustrate how to work with operations, here is an example using ```ExpressionBuilder``` helper methods: - -```php -// Get the expression builder -$expr = $client->expr(); - -// Prepare data for a new record -$data = [ - 'foo' => 'bar', - 'bar_ids' => [ // Field of type "manytoMany" - $expr->addRecord(3), // Add the record of ID 3 to the set - $expr->createRecord([ // Create a new sub record and add it to the set - 'bar' => 'baz' - // ... - ]) - ] -]; - -$result = $client->create('model_name', $data); -``` - -Internally, the client formats automatically the whole query parameters for all writing methods -(```create``` and ```update```) by calling the special builder -method ```normalizeData()```. - -Here is a complete list of helper methods available in ```ExpressionBuilder``` for operation expressions: - -```php -/** - * Adds a new record created from data. - */ -public function createRecord(array $data): CollectionOperation; - -/** - * Updates an existing record of id $id with data. - * /!\ Can not be used in record CREATE query. - */ -public function updateRecord(int $id, array $data): CollectionOperation; - -/** - * Adds an existing record of id $id to the collection. - */ -public function addRecord(int $id): CollectionOperation; - -/** - * Removes the record of id $id from the collection, but does not delete it. - * /!\ Can not be used in record CREATE query. - */ -public function removeRecord(int $id): CollectionOperation; - -/** - * Removes the record of id $id from the collection, then deletes it from the database. - * /!\ Can not be used in record CREATE query. - */ -public function deleteRecord(int $id): CollectionOperation; - -/** - * Replaces all existing records in the collection by the $ids list, - * Equivalent to using the command "clear" followed by a command "add" for each id in $ids. - */ -public function replaceRecords(array $ids = []): CollectionOperation; - -/** - * Removes all records from the collection, equivalent to using the command "remove" on every record explicitly. - * /!\ Can not be used in record CREATE query. - */ -public function clearRecords(): CollectionOperation; -``` \ No newline at end of file diff --git a/docs/custom_transport.md b/docs/custom_transport.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/index.md b/docs/index.md index 0cb0e0e..b337809 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,30 +1,19 @@ -PHP Odoo API client documentation -================================= +PHP Odoo API client +=================== -This package is a PHP library to connect and interact with an Odoo database. +This package is a PHP library to connect and interact with an Odoo instance via JSON-RPC by default. It follows [the official Odoo documentation](https://www.odoo.com/documentation/13.0/developer/misc/api/odoo.html). -Main features -============= - -- Built-in ORM methods -- Expression builder for domains and collection operations -- Query builder to build your queries easily -- Repositories to work on a specific model or create reusable queries - Getting started =============== -Software requirements ---------------------- +Requirements +------------ -PHP version 7.2 or newer to develop using the client. Other requirements, such as PHP extensions, are enforced by +PHP version 8.1 or newer to develop using the client. Other requirements, such as PHP extensions, are enforced by composer. See the `require` section of [composer.json file](../composer.json) for details. -The installation of the PHP extension `php-curl` is recommended -for HTTP requests. If missing, PHP native streams are used. - Odoo database support --------------------- diff --git a/examples/expression_builder.php b/examples/expression_builder.php deleted file mode 100644 index 69a77d9..0000000 --- a/examples/expression_builder.php +++ /dev/null @@ -1,30 +0,0 @@ -andX( - $expr->orX( - $expr->eq('A', 1), - $expr->eq('B', 1) - ), - $expr->orX( - $expr->eq('C', 1), - $expr->eq('D', 1), - $expr->eq('E', 1) - ) -); - -dump($domain); -dump($domain->toArray()); - -// Expected: [ '&', '|', (A), (B), '|', (C), '|', (D), (E) ] \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon index 30af974..cd4c474 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -6,6 +6,4 @@ parameters: paths: - %currentWorkingDirectory%/src ignoreErrors: - - '#Method .* should return array but returns array\.#' - - '#Cannot cast mixed to int\.#' - - '#Cannot cast mixed to string\.#' \ No newline at end of file + - '#Cannot cast mixed to int\.#' \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index fb5f654..94d8b09 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,31 +1,23 @@ - - - - - - - - - - tests - - - - - - ./src - ./vendor - - ./tests - ./vendor - - - + + + + ./src + ./vendor + + + ./tests + ./vendor + + + + + + + + + tests + + diff --git a/src/Client.php b/src/Client.php index f8c285c..87ede96 100644 --- a/src/Client.php +++ b/src/Client.php @@ -11,16 +11,14 @@ namespace Ang3\Component\Odoo; -use Ang3\Component\Odoo\DBAL\Expression\ExpressionBuilder; use Ang3\Component\Odoo\Enum\OdooMethod; use Ang3\Component\Odoo\Enum\OdooService; use Ang3\Component\Odoo\Exception\AuthenticationException; use Ang3\Component\Odoo\Exception\MissingConfigParameterException; -use Ang3\Component\Odoo\Exception\RemoteException; use Ang3\Component\Odoo\Exception\RequestException; +use Ang3\Component\Odoo\Exception\TransportException; use Ang3\Component\Odoo\Metadata\Version; use Ang3\Component\Odoo\Transport\JsonRpcPhpStreamTransport; -use Ang3\Component\Odoo\Transport\TransportException; use Ang3\Component\Odoo\Transport\TransportInterface; use Psr\Log\LoggerInterface; @@ -30,7 +28,6 @@ class Client { private TransportInterface $transport; - private ExpressionBuilder $expressionBuilder; private ?int $uid = null; public function __construct( @@ -38,7 +35,6 @@ public function __construct( ?TransportInterface $transport = null, private ?LoggerInterface $logger = null ) { - $this->expressionBuilder = new ExpressionBuilder(); $this->transport = $transport ?: new JsonRpcPhpStreamTransport($this->connection); } @@ -58,7 +54,7 @@ public static function create( return new self(Connection::create($config), $transport, $logger); } - public function execute(string $name, string $method, array $parameters = [], array $options = []): mixed + public function executeKw(string $name, string $method, array $parameters = [], array $options = []): mixed { return $this->request( OdooService::Object->value, @@ -119,20 +115,16 @@ public function request(string $service, string $method, ...$arguments): mixed $this->logger?->info('Odoo request #{request_id} - {service}::{method}({arguments}) (uid: #{uid})', $context); $runtime = microtime(true); - $payload = $this->transport->request($service, $method, $arguments); + $result = $this->transport->request($service, $method, $arguments); $runtime = microtime(true) - $runtime; $this->logger?->debug('Odoo request #{request_id} finished - Runtime: {runtime}s.', [ 'request_id' => $context['request_id'], 'runtime' => number_format($runtime, 3, '.', ' '), - 'payload' => $payload, + 'payload' => $result, ]); - if (\is_array($payload['error'] ?? null)) { - throw RemoteException::create($payload); - } - - return $payload['result']; + return $result; } public function getConnection(): Connection @@ -156,11 +148,6 @@ public function setTransport(TransportInterface $transport): self return $this; } - public function getExpressionBuilder(): ExpressionBuilder - { - return $this->expressionBuilder; - } - public function getLogger(): ?LoggerInterface { return $this->logger; @@ -172,4 +159,9 @@ public function setLogger(?LoggerInterface $logger): self return $this; } + + public function getUid(): ?int + { + return $this->uid; + } } diff --git a/src/DBAL/Expression/Domain/Comparison.php b/src/DBAL/Expression/Domain/Comparison.php deleted file mode 100644 index 085de8c..0000000 --- a/src/DBAL/Expression/Domain/Comparison.php +++ /dev/null @@ -1,121 +0,0 @@ - - */ -class Comparison implements DomainInterface -{ - /** - * Comparison operators. - */ - public const UNSET_OR_EQUAL_TO = '=?'; - public const EQUAL_TO = '='; - public const NOT_EQUAL_TO = '!='; - public const LESS_THAN = '<'; - public const LESS_THAN_OR_EQUAL = '<='; - public const GREATER_THAN = '>'; - public const GREATER_THAN_OR_EQUAL = '>='; - public const EQUAL_LIKE = '=like'; - public const INSENSITIVE_EQUAL_LIKE = '=ilike'; - public const LIKE = 'like'; - public const NOT_LIKE = 'not like'; - public const INSENSITIVE_LIKE = 'ilike'; - public const INSENSITIVE_NOT_LIKE = 'not ilike'; - public const IN = 'in'; - public const NOT_IN = 'not in'; - - /** - * @var string[] - */ - private static array $operators = [ - self::UNSET_OR_EQUAL_TO, - self::EQUAL_TO, - self::NOT_EQUAL_TO, - self::LESS_THAN, - self::LESS_THAN_OR_EQUAL, - self::GREATER_THAN, - self::GREATER_THAN_OR_EQUAL, - self::EQUAL_LIKE, - self::INSENSITIVE_EQUAL_LIKE, - self::LIKE, - self::NOT_LIKE, - self::INSENSITIVE_LIKE, - self::INSENSITIVE_NOT_LIKE, - self::IN, - self::NOT_IN, - ]; - - public function __construct(private string $fieldName, private string $operator, private mixed $value) - { - } - - public function __clone() - { - $this->value = \is_object($this->value) ? clone $this->value : $this->value; - } - - public function getIterator(): \ArrayIterator - { - return new \ArrayIterator($this->toArray()); - } - - public function toArray(): array - { - return [$this->fieldName, $this->operator, $this->value]; - } - - /** - * @return string[] - */ - public static function getOperators(): array - { - return self::$operators; - } - - public function getFieldName(): string - { - return $this->fieldName; - } - - public function setFieldName(string $fieldName): self - { - $this->fieldName = $fieldName; - - return $this; - } - - public function getOperator(): string - { - return $this->operator; - } - - public function setOperator(string $operator): self - { - $this->operator = $operator; - - return $this; - } - - public function getValue(): mixed - { - return $this->value; - } - - public function setValue(mixed $value): self - { - $this->value = $value; - - return $this; - } -} diff --git a/src/DBAL/Expression/Domain/CompositeDomain.php b/src/DBAL/Expression/Domain/CompositeDomain.php deleted file mode 100644 index 1072d5f..0000000 --- a/src/DBAL/Expression/Domain/CompositeDomain.php +++ /dev/null @@ -1,224 +0,0 @@ - - */ -class CompositeDomain implements DomainInterface -{ - /** - * Logical operators. - */ - public const AND = '&'; - public const OR = '|'; - public const NOT = '!'; - - /** - * @var string[] - */ - private static array $operators = [ - self::AND, - self::OR, - self::NOT, - ]; - - /** - * @param DomainInterface[] $domains - */ - public function __construct(private string $operator, private array $domains = []) - { - } - - public static function criteria(array $criteria = []): self - { - $domains = []; - - foreach ($criteria as $fieldName => $value) { - $domains[] = new Comparison($fieldName, Comparison::EQUAL_TO, $value); - } - - return new self(self::AND, $domains); - } - - public function __clone() - { - foreach ($this->domains as $key => $domain) { - $this->domains[$key] = clone $domain; - } - } - - /** - * {@inheritdoc} - * - * @return DomainInterface[]|\Generator - */ - public function getIterator(): \Generator - { - foreach ($this->getDomains() as $key => $domain) { - yield $key => $domain; - } - } - - /** - * @static - * - * @return string[] - */ - public static function getOperators(): array - { - return self::$operators; - } - - public function toArray(): array - { - $domain = $this->prepare(); - - if (!($domain instanceof self)) { - return $domain ? [$domain->toArray()] : []; - } - - $result = [$domain->getOperator()]; - - foreach ($domain->getDomains() as $domain) { - $domainArray = $domain->toArray(); - - if ($domain instanceof self) { - foreach ($domainArray as $value) { - $result[] = $value; - } - - continue; - } - - $result[] = $domainArray; - } - - return $result; - } - - /** - * @internal - * - * Create a copy according to arity policy of Odoo polish notation - */ - private function prepare(): ?DomainInterface - { - $domains = $this->domains; - $nbDomains = \count($domains); - - if (0 === $nbDomains) { - return null; - } - - if (1 === $nbDomains) { - return self::NOT === $this->operator ? $this : array_shift($domains); - } - - if (self::NOT === $this->operator) { - $andX = new self(self::AND, $domains); - - return new self($this->operator, array_filter([$andX->prepare()])); - } - - if (2 === $nbDomains) { - return $this; - } - - foreach ($domains as $key => $subDomain) { - if ($subDomain instanceof self) { - $domains[$key] = $subDomain->prepare(); - - if (!$domains[$key]) { - unset($domains[$key]); - } - } - } - - $firstDomain = array_shift($domains); - $subDomain = new self($this->operator, array_filter($domains)); - - return new self($this->operator, array_filter([ - $firstDomain, $subDomain->prepare(), - ])); - } - - public function getOperator(): string - { - return $this->operator; - } - - public function setOperator(string $operator): self - { - $this->operator = $operator; - - return $this; - } - - /** - * @return DomainInterface[] - */ - public function getDomains(): array - { - return array_values($this->domains); - } - - public function setDomains(array $domains = []): self - { - $this->domains = []; - - foreach ($domains as $domain) { - if (!$domain) { - continue; - } - - $this->add($domain); - } - - return $this; - } - - public function add(DomainInterface $domain): self - { - if (!$this->has($domain)) { - $this->domains[] = $domain; - } - - return $this; - } - - public function remove(DomainInterface $domain): self - { - foreach ($this->domains as $key => $value) { - if ($value === $domain) { - unset($this->domains[$key]); - } - } - - return $this; - } - - public function has(DomainInterface $domain): bool - { - return \in_array($domain, $this->domains, true); - } - - public function count(): int - { - return \count($this->domains); - } - - public function isEmpty(): bool - { - return 0 === \count($this->domains); - } -} diff --git a/src/DBAL/Expression/Domain/CustomDomain.php b/src/DBAL/Expression/Domain/CustomDomain.php deleted file mode 100644 index 8a28627..0000000 --- a/src/DBAL/Expression/Domain/CustomDomain.php +++ /dev/null @@ -1,47 +0,0 @@ - - */ -class CustomDomain implements DomainInterface -{ - private array $data; - - public function __construct(array $data = []) - { - $this->data = $data; - } - - public function getIterator(): \ArrayIterator - { - return new \ArrayIterator($this->toArray()); - } - - public function toArray(): array - { - return $this->data; - } - - public function getData(): array - { - return $this->data; - } - - public function setData(array $data): self - { - $this->data = $data; - - return $this; - } -} diff --git a/src/DBAL/Expression/Domain/DomainInterface.php b/src/DBAL/Expression/Domain/DomainInterface.php deleted file mode 100644 index 94fbde3..0000000 --- a/src/DBAL/Expression/Domain/DomainInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ -interface DomainInterface extends \IteratorAggregate -{ - public function toArray(): array; -} diff --git a/src/DBAL/Expression/Exception/ConversionException.php b/src/DBAL/Expression/Exception/ConversionException.php deleted file mode 100644 index 514ff45..0000000 --- a/src/DBAL/Expression/Exception/ConversionException.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ -class ConversionException extends \RuntimeException -{ -} diff --git a/src/DBAL/Expression/ExpressionBuilder.php b/src/DBAL/Expression/ExpressionBuilder.php deleted file mode 100644 index 6b78867..0000000 --- a/src/DBAL/Expression/ExpressionBuilder.php +++ /dev/null @@ -1,305 +0,0 @@ - - */ -class ExpressionBuilder -{ - /** - * Create a logical operation "AND". - */ - public function andX(DomainInterface ...$domains): CompositeDomain - { - return new CompositeDomain(CompositeDomain::AND, $domains ?: []); - } - - /** - * Create a logical operation "OR". - */ - public function orX(DomainInterface ...$domains): CompositeDomain - { - return new CompositeDomain(CompositeDomain::OR, $domains ?: []); - } - - /** - * Create a logical operation "NOT". - */ - public function notX(DomainInterface ...$domains): CompositeDomain - { - return new CompositeDomain(CompositeDomain::NOT, $domains ?: []); - } - - /** - * Check if the field is EQUAL TO the value. - */ - public function eq(string $fieldName, mixed $value): Comparison - { - return new Comparison($fieldName, Comparison::EQUAL_TO, $value); - } - - /** - * Check if the field is NOT EQUAL TO the value. - */ - public function neq(string $fieldName, mixed $value): Comparison - { - return new Comparison($fieldName, Comparison::NOT_EQUAL_TO, $value); - } - - /** - * Check if the field is UNSET OR EQUAL TO the value. - */ - public function ueq(string $fieldName, mixed $value): Comparison - { - return new Comparison($fieldName, Comparison::UNSET_OR_EQUAL_TO, $value); - } - - /** - * Check if the field is LESS THAN the value. - */ - public function lt(string $fieldName, mixed $value): Comparison - { - return new Comparison($fieldName, Comparison::LESS_THAN, $value); - } - - /** - * Check if the field is LESS THAN OR EQUAL the value. - */ - public function lte(string $fieldName, mixed $value): Comparison - { - return new Comparison($fieldName, Comparison::LESS_THAN_OR_EQUAL, $value); - } - - /** - * Check if the field is GREATER THAN the value. - */ - public function gt(string $fieldName, mixed $value): Comparison - { - return new Comparison($fieldName, Comparison::GREATER_THAN, $value); - } - - /** - * Check if the field is GREATER THAN OR EQUAL the value. - */ - public function gte(string $fieldName, mixed $value): Comparison - { - return new Comparison($fieldName, Comparison::GREATER_THAN_OR_EQUAL, $value); - } - - /** - * Check if the variable is LIKE the value. - * - * An underscore _ in the pattern stands for (matches) any single character - * A percent sign % matches any string of zero or more characters. - * - * If $strict is set to FALSE, the value pattern is "%value%" (automatically wrapped into signs %). - */ - public function like(string $fieldName, mixed $value, bool $strict = false, bool $caseSensitive = true): Comparison - { - if ($strict) { - $operator = $caseSensitive ? Comparison::EQUAL_LIKE : Comparison::INSENSITIVE_EQUAL_LIKE; - } else { - $operator = $caseSensitive ? Comparison::LIKE : Comparison::INSENSITIVE_LIKE; - } - - return new Comparison($fieldName, $operator, $value); - } - - /** - * Check if the field is IS NOT LIKE the value. - */ - public function notLike(string $fieldName, mixed $value, bool $caseSensitive = true): Comparison - { - $operator = $caseSensitive ? Comparison::NOT_LIKE : Comparison::INSENSITIVE_NOT_LIKE; - - return new Comparison($fieldName, $operator, $value); - } - - /** - * Check if the field is IN values list. - */ - public function in(string $fieldName, float|array|bool|int|string $values): Comparison - { - return new Comparison($fieldName, Comparison::IN, $this->getValues($values)); - } - - /** - * Check if the field is NOT IN values list. - */ - public function notIn(string $fieldName, float|array|bool|int|string $values): Comparison - { - return new Comparison($fieldName, Comparison::NOT_IN, $this->getValues($values)); - } - - /** - * @internal - */ - private function getValues(float|array|bool|int|string $values): array - { - return \is_array($values) ? $values : [$values]; - } - - /** - * Adds a new record created from data. - * - * @throws \InvalidArgumentException when $data is empty - */ - public function createRecord(array $data): CollectionOperation - { - return CollectionOperation::create($data); - } - - /** - * Updates an existing record of id $id with data. - * /!\ Can not be used in record create operation. - * - * @throws \InvalidArgumentException when $data is empty - */ - public function updateRecord(int $id, array $data): CollectionOperation - { - if (!$data) { - throw new \InvalidArgumentException('Data cannot be empty'); - } - - return CollectionOperation::update($id, $data); - } - - /** - * Adds an existing record of id $id to the collection. - */ - public function addRecord(int $id): CollectionOperation - { - return CollectionOperation::add($id); - } - - /** - * Removes the record of id $id from the collection, but does not delete it. - * /!\ Can not be used in record create operation. - */ - public function removeRecord(int $id): CollectionOperation - { - return CollectionOperation::remove($id); - } - - /** - * Removes the record of id $id from the collection, then deletes it from the database. - * /!\ Can not be used in record create operation. - */ - public function deleteRecord(int $id): CollectionOperation - { - return CollectionOperation::delete($id); - } - - /** - * Replaces all existing records in the collection by the $ids list, - * Equivalent to using the command "clear" followed by a command "add" for each id in $ids. - */ - public function replaceRecords(array $ids = []): CollectionOperation - { - return CollectionOperation::replace($ids); - } - - /** - * Removes all records from the collection, equivalent to using the command "remove" on every record explicitly. - * /!\ Can not be used in record create operation. - */ - public function clearRecords(): CollectionOperation - { - return CollectionOperation::clear(); - } - - /** - * @throws \InvalidArgumentException when $criteria value is not valid - * @throws ConversionException on data conversion failure - */ - public function normalizeDomains(iterable $criteria = null): array - { - if (!$criteria) { - return [[]]; - } - - if (\is_array($criteria)) { - $criteria = CompositeDomain::criteria($criteria); - } - - if (!$criteria instanceof DomainInterface) { - throw new \InvalidArgumentException(sprintf('Expected parameter #1 of type %s|array<%s|array>, %s given', DomainInterface::class, DomainInterface::class, \gettype($criteria))); - } - - $criteriaArray = $this->formatValue($criteria->toArray()); - - return $criteria instanceof CompositeDomain ? [$criteriaArray] : [[$criteriaArray]]; - } - - /** - * @throws ConversionException on data conversion failure - */ - public function normalizeData(array $data = []): array - { - return (array) $this->formatValue($data); - } - - /** - * @internal - * - * @throws ConversionException on data conversion failure - */ - private function formatValue(mixed $value): mixed - { - if (\is_scalar($value)) { - return $value; - } - - if (\is_array($value) || is_iterable($value)) { - $values = []; - - foreach ($value as $key => $aValue) { - $values[$key] = $this->formatValue($aValue); - } - - return $values; - } - - if (\is_object($value)) { - if ($value instanceof DomainInterface) { - return $this->formatValue($value->toArray()); - } - - if ($value instanceof OperationInterface) { - return $this->formatValue($value->toArray()); - } - - if ($value instanceof \DateTimeInterface) { - try { - $date = new \DateTime(sprintf('@%s', $value->getTimestamp())); - } catch (\Exception $e) { - throw new ConversionException(sprintf('Failed to convert date from timestamp "%d"', $value->getTimestamp()), 0, $e); - } - - return $date - ->setTimezone(new \DateTimeZone('UTC')) - ->format('Y-m-d H:i:s') - ; - } - } - - return (string) $value; - } -} diff --git a/src/DBAL/Expression/Operation/CollectionOperation.php b/src/DBAL/Expression/Operation/CollectionOperation.php deleted file mode 100644 index f402927..0000000 --- a/src/DBAL/Expression/Operation/CollectionOperation.php +++ /dev/null @@ -1,126 +0,0 @@ - - */ -class CollectionOperation implements OperationInterface -{ - /** - * Operations key. - */ - public const CREATE = 0; - public const UPDATE = 1; - public const DELETE = 2; - public const REMOVE = 3; - public const ADD = 4; - public const CLEAR = 5; - public const REPLACE = 6; - - public function __construct(private int $type, private int $id = 0, private int|array $data = 0) - { - } - - /** - * @throws \InvalidArgumentException when data is empty - */ - public static function create(array $data): self - { - if (!$data) { - throw new \InvalidArgumentException('Data cannot be empty'); - } - - return new self(self::CREATE, 0, $data); - } - - /** - * @throws \InvalidArgumentException when data is empty - */ - public static function update(int $id, array $data = []): self - { - if (!$data) { - throw new \InvalidArgumentException('Data cannot be empty'); - } - - return new self(self::UPDATE, $id, $data); - } - - public static function add(int $id): self - { - return new self(self::ADD, $id); - } - - public static function remove(int $id): self - { - return new self(self::REMOVE, $id); - } - - public static function delete(int $id): self - { - return new self(self::DELETE, $id); - } - - /** - * @param int[] $ids - */ - public static function replace(array $ids): self - { - return new self(self::REPLACE, 0, $ids); - } - - public static function clear(): self - { - return new self(self::CLEAR); - } - - public function getType(): int - { - return $this->type; - } - - public function setType(int $type): self - { - $this->type = $type; - - return $this; - } - - public function getId(): int - { - return $this->id; - } - - public function setId(int $id): self - { - $this->id = $id; - - return $this; - } - - public function getData(): int|array - { - return $this->data; - } - - public function setData(int|array $data): self - { - $this->data = $data; - - return $this; - } - - public function toArray(): array - { - return [$this->type, $this->id, $this->data]; - } -} diff --git a/src/DBAL/Expression/Operation/OperationInterface.php b/src/DBAL/Expression/Operation/OperationInterface.php deleted file mode 100644 index 99db7d9..0000000 --- a/src/DBAL/Expression/Operation/OperationInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ -interface OperationInterface -{ - public function toArray(): array; -} diff --git a/src/DBAL/Query/AbstractQuery.php b/src/DBAL/Query/AbstractQuery.php deleted file mode 100644 index 97b164f..0000000 --- a/src/DBAL/Query/AbstractQuery.php +++ /dev/null @@ -1,102 +0,0 @@ -name; - } - - public function setName(string $name): static - { - $this->name = $name; - - return $this; - } - - public function getMethod(): string - { - return $this->method; - } - - public function setMethod(string $method): static - { - $this->method = $method; - - return $this; - } - - public function getParameters(): array - { - return $this->parameters; - } - - public function setParameters(array $parameters = []): static - { - $this->parameters = $parameters; - - return $this; - } - - public function getOptions(): array - { - return $this->options; - } - - public function setOptions(array $options = []): static - { - $this->options = $options; - - return $this; - } - - /** - * Add an option on the query. - */ - public function addOption(string $name, mixed $value): static - { - $this->options[$name] = $value; - - return $this; - } - - /** - * Execute the query. - * Allowed methods: all. - */ - public function execute(): mixed - { - return $this->recordManager->executeQuery($this); - } - - /** - * Gets the related manager of the query. - */ - public function getRecordManager(): RecordManager - { - return $this->recordManager; - } -} diff --git a/src/DBAL/Query/NativeQuery.php b/src/DBAL/Query/NativeQuery.php deleted file mode 100644 index b164e72..0000000 --- a/src/DBAL/Query/NativeQuery.php +++ /dev/null @@ -1,16 +0,0 @@ -method = $method; - - return $this; - } - - /** - * Counts the number of records from parameters. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws QueryException on invalid query method - */ - public function count(): int - { - if (!\in_array($this->method, [self::SEARCH, self::SEARCH_READ], true)) { - throw new QueryException(sprintf('You can count results with method "%s" and "%s" only.', self::SEARCH, self::SEARCH_READ)); - } - - $query = new self($this->recordManager, $this->name, self::SEARCH_COUNT); - $query->setParameters($this->parameters); - - return (int) $query->execute(); - } - - /** - * Gets just ONE scalar result. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws NoUniqueResultException on no unique result - * @throws NoResultException on no result - * @throws QueryException on invalid query method - */ - public function getSingleScalarResult(): float|bool|int|string - { - $result = $this->getOneOrNullScalarResult(); - - if (!$result) { - throw new NoResultException(); - } - - return $result; - } - - /** - * Gets one or NULL scalar result. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws NoUniqueResultException on no unique result - * @throws QueryException on invalid query method - */ - public function getOneOrNullScalarResult(): float|bool|int|string|null - { - $result = $this->getScalarResult(); - - if (\count($result) > 1) { - throw new NoUniqueResultException(); - } - - return array_shift($result); - } - - /** - * Gets a list of scalar result. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @return array - * - * @throws QueryException on invalid query method - */ - public function getScalarResult(): array - { - $result = $this->getResult(); - - if (self::SEARCH === $this->method) { - return $result; - } - - $selectedFields = $this->options['fields'] ?? []; - if (\count($selectedFields) > 1) { - throw new QueryException('More than one field selected.'); - } - - $selectedFieldName = $selectedFields[0] ?? 'id'; - - foreach ($result as $key => $value) { - $result[$key] = $value[$selectedFieldName] ?? null; - } - - return $result; - } - - /** - * Gets one row. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws NoUniqueResultException on no unique result - * @throws NoResultException on no result - * @throws QueryException on invalid query method - */ - public function getSingleResult(): array - { - $result = $this->getOneOrNullResult(); - $result = \is_array($result) ? array_shift($result) : null; - - if (!$result) { - throw new NoResultException(); - } - - return $result; - } - - /** - * Gets one or NULL row. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws NoUniqueResultException on no unique result - * @throws QueryException on invalid query method - */ - public function getOneOrNullResult(): ?array - { - $result = $this->getResult(); - - if (\count($result) > 1) { - throw new NoUniqueResultException(); - } - - return array_shift($result); - } - - /** - * Gets all result rows. - * Allowed methods: SEARCH, SEARCH_READ. - * - * @throws QueryException on invalid query method - */ - public function getResult(): array - { - if (!\in_array($this->method, [self::SEARCH, self::SEARCH_READ], true)) { - throw new QueryException(sprintf('You can get results with methods "%s" and "%s" only.', self::SEARCH, self::SEARCH_READ)); - } - - return (array) $this->execute(); - } -} diff --git a/src/DBAL/Query/QueryBuilder.php b/src/DBAL/Query/QueryBuilder.php deleted file mode 100644 index 872a815..0000000 --- a/src/DBAL/Query/QueryBuilder.php +++ /dev/null @@ -1,485 +0,0 @@ -type = self::SELECT; - $this->select = []; - $this->values = []; - $this->ids = []; - - $fields = array_filter(\is_array($fields) ? $fields : [$fields]); - - foreach ($fields as $fieldName) { - $this->addSelect($fieldName); - } - - return $this; - } - - /** - * Defines the query of type "SEARCH". - */ - public function search(): self - { - $this->type = self::SEARCH; - $this->select = []; - $this->values = []; - $this->ids = []; - - return $this; - } - - /** - * Defines the query of type "INSERT". - */ - public function insert(): self - { - $this->type = self::INSERT; - $this->select = []; - $this->ids = []; - $this->where = null; - - return $this; - } - - /** - * Defines the query of type "UPDATE" with ids of records to update. - * - * @param int[] $ids - */ - public function update(array $ids): self - { - $this->type = self::UPDATE; - $this->select = []; - - return $this->setIds($ids); - } - - /** - * Defines the query of type "DELETE" with ids of records to delete. - */ - public function delete(array $ids): self - { - $this->type = self::DELETE; - $this->select = []; - $this->values = []; - - return $this->setIds($ids); - } - - /** - * Adds a field to select. - * - * @throws QueryException when the type of the query is not "SELECT" - */ - public function addSelect(string $fieldName): self - { - if (self::SELECT !== $this->type) { - throw new QueryException('You can select fields in query of type "SELECT" only.'); - } - - if (!\in_array($fieldName, $this->select, true)) { - $this->select[] = $fieldName; - } - - return $this; - } - - /** - * Gets selected fields. - */ - public function getSelect(): array - { - return $this->select; - } - - /** - * Sets the target model name. - */ - public function from(string $modelName): self - { - $this->from = $modelName; - - return $this; - } - - /** - * Gets the target model name of the query. - */ - public function getFrom(): ?string - { - return $this->from; - } - - /** - * Sets target IDs in case of query of type "UPDATE" or "DELETE". - * - * @throws QueryException when the type of the query is not "UPDATE" nor "DELETE" - */ - public function setIds(array $ids): self - { - $this->ids = []; - - foreach ($ids as $id) { - $this->addId($id); - } - - return $this; - } - - /** - * Adds target ID in case of query of type "UPDATE" or "DELETE". - * - * @throws QueryException when the type of the query is not "UPDATE" nor "DELETE" - */ - public function addId(int $id): self - { - if (!\in_array($this->type, [self::UPDATE, self::DELETE], true)) { - throw new QueryException('You can set indexes in query of type "UPDATE" or "DELETE" only.'); - } - - if (!\in_array($id, $this->ids, true)) { - $this->ids[] = $id; - } - - return $this; - } - - /** - * Sets field values in case of query of type "INSERT" or "UPDATE". - * - * @throws QueryException when the type of the query is not "INSERT" nor "UPDATE" - */ - public function setValues(array $values = []): self - { - $this->values = []; - - foreach ($values as $fieldName => $value) { - $this->set($fieldName, $value); - } - - return $this; - } - - /** - * Set a field value in case of query of type "INSERT" or "UPDATE". - * - * @throws QueryException when the type of the query is not "INSERT" nor "UPDATE" - */ - public function set(string $fieldName, mixed $value): self - { - if (!\in_array($this->type, [self::INSERT, self::UPDATE], true)) { - throw new QueryException('You can set values in query of type "INSERT" or "UPDATE" only.'); - } - - $this->values[$fieldName] = $value; - - return $this; - } - - /** - * Gets field values set in case of query of type "INSERT" or "UPDATE". - */ - public function getValues(): array - { - return $this->values; - } - - /** - * Sets criteria for queries of type "SELECT" and "SEARCH". - * - * @throws QueryException when the type of the query is not "SELECT" not "SEARCH" - */ - public function where(?DomainInterface $domain = null): self - { - $this->assertSupportsWhereClause(); - $this->where = $domain; - - return $this; - } - - /** - * Takes the WHERE clause and adds a node with logical operator AND. - * - * @throws QueryException when the type of the query is not "SELECT" nor "SEARCH" - */ - public function andWhere(DomainInterface $domain): self - { - $this->assertSupportsWhereClause(); - $this->where = $this->where ? $this->expr()->andX($this->where, $domain) : $domain; - - return $this; - } - - /** - * Takes the WHERE clause and adds a node with logical operator OR. - * - * @throws QueryException when the type of the query is not "SELECT" nor "SEARCH" - */ - public function orWhere(DomainInterface $domain): self - { - $this->assertSupportsWhereClause(); - $this->where = $this->where ? $this->expr()->orX($this->where, $domain) : $domain; - - return $this; - } - - /** - * Gets the WHERE clause. - */ - public function getWhere(): ?DomainInterface - { - return $this->where; - } - - /** - * @internal - * - * @throws QueryException when the type of the query is not "SELECT" nor "SEARCH" - */ - private function assertSupportsWhereClause(): void - { - if (!\in_array($this->type, [self::SELECT, self::SEARCH], true)) { - throw new QueryException('You can set criteria in query of type "SELECT" or "SEARCH" only.'); - } - } - - /** - * Sets orders. - */ - public function setOrders(array $orders = []): self - { - $this->orders = []; - - foreach ($orders as $fieldName => $isAsc) { - $this->addOrderBy($fieldName, $isAsc); - } - - return $this; - } - - /** - * Clears orders and adds one. - */ - public function orderBy(string $fieldName, bool $isAsc = true): self - { - $this->orders = []; - - return $this->addOrderBy($fieldName, $isAsc); - } - - /** - * Adds order. - * - * @throws QueryException when the query type is not valid - */ - public function addOrderBy(string $fieldName, bool $isAsc = true): self - { - if (!\in_array($this->type, [self::SELECT, self::SEARCH], true)) { - throw new QueryException('You can set orders in query of type "SELECT", "SEARCH" only.'); - } - - $this->orders[$fieldName] = $isAsc; - - return $this; - } - - /** - * Gets ordered fields. - */ - public function getOrders(): array - { - return $this->orders; - } - - /** - * Sets the max results of the query (limit). - */ - public function setMaxResults(?int $maxResults): self - { - $this->maxResults = $maxResults; - - return $this; - } - - /** - * Gets the max results of the query. - */ - public function getMaxResults(): ?int - { - return $this->maxResults; - } - - /** - * Sets the first results of the query (offset). - */ - public function setFirstResult(?int $firstResult): self - { - $this->firstResult = $firstResult; - - return $this; - } - - /** - * Gets the first results of the query. - */ - public function getFirstResult(): ?int - { - return $this->firstResult; - } - - /** - * Computes and returns the query. - * - * @throws QueryException on invalid query - * @throws ConversionException on data conversion failure - */ - public function getQuery(): OrmQuery - { - $method = match ($this->type) { - self::SELECT => OrmQuery::SEARCH_READ, - self::SEARCH => OrmQuery::SEARCH, - self::INSERT => OrmQuery::CREATE, - self::UPDATE => OrmQuery::WRITE, - self::DELETE => OrmQuery::UNLINK, - default => throw new \InvalidArgumentException(sprintf('The query type "%s" is not valid.', $this->type)), - }; - - $query = new OrmQuery($this->recordManager, $this->from, $method); - - if (\in_array($this->type, [self::SELECT, self::SEARCH], true)) { - $parameters = $this->expr()->normalizeDomains($this->where); - } elseif (self::DELETE === $this->type) { - if (!$this->ids) { - throw new QueryException('You must set indexes for queries of type "DELETE".'); - } - - $parameters = [$this->ids]; - } else { - if (!$this->values) { - throw new QueryException('You must set values for queries of type "INSERT" and "UPDATE".'); - } - - $parameters = $this->expr()->normalizeData($this->values); - - if (self::UPDATE === $this->type) { - if (!$this->ids) { - throw new QueryException('You must set indexes for queries of type "UPDATE".'); - } - - $parameters = [$this->ids, $parameters]; - } else { - $parameters = [$parameters]; - } - } - - $query->setParameters($parameters); - - if (\in_array($this->type, [self::SELECT, self::SEARCH], true)) { - $options = []; - - if (self::SELECT === $this->type && $this->select) { - $options['fields'] = $this->select; - } - - $orders = $this->orders; - - if ($orders) { - foreach ($orders as $fieldName => $isAsc) { - $orders[$fieldName] = sprintf('%s %s', $fieldName, $isAsc ? 'asc' : 'desc'); - } - - $options['order'] = implode(', ', $orders); - } - - if ($this->firstResult) { - $options['offset'] = $this->firstResult; - } - - if ($this->maxResults) { - $options['limit'] = $this->maxResults; - } - - $query->setOptions($options); - } - - return $query; - } - - /** - * Gets the type of the query. - */ - public function getType(): string - { - return $this->type; - } - - /** - * Gets the related manager of the query. - */ - public function getRecordManager(): RecordManager - { - return $this->recordManager; - } - - /** - * Shortcut to the expression builder of the related client. - */ - public function expr(): ExpressionBuilder - { - return $this->recordManager->getExpressionBuilder(); - } -} diff --git a/src/DBAL/Query/QueryException.php b/src/DBAL/Query/QueryException.php deleted file mode 100644 index e1c3ad9..0000000 --- a/src/DBAL/Query/QueryException.php +++ /dev/null @@ -1,16 +0,0 @@ - - */ -class RecordManager -{ - private Schema $schema; - private ExpressionBuilder $expressionBuilder; - - /** - * @param RecordRepository[] $repositories - */ - public function __construct(private readonly Client $client, private array $repositories = []) - { - $this->schema = new Schema($client); - $this->expressionBuilder = new ExpressionBuilder(); - } - - public function getRepository(string $modelName): RecordRepository - { - if (!\array_key_exists($modelName, $this->repositories)) { - $repository = new RecordRepository($this, $modelName); - $this->addRepository($repository); - - return $repository; - } - - return $this->repositories[$modelName]; - } - - public function setRepositories(array $repositories = []): self - { - $this->repositories = []; - - foreach ($repositories as $repository) { - $this->addRepository($repository); - } - - return $this; - } - - public function addRepository(RecordRepository $repository): self - { - $this->repositories[$repository->getModelName()] = $repository; - $repository->setRecordManager($this); - - return $this; - } - - public function createQueryBuilder(string $modelName): QueryBuilder - { - return new QueryBuilder($this, $modelName); - } - - public function createOrmQuery(string $name, string $method): OrmQuery - { - return new OrmQuery($this, $name, $method); - } - - public function createNativeQuery(string $name, string $method): NativeQuery - { - return new NativeQuery($this, $name, $method); - } - - public function executeQuery(QueryInterface $query): mixed - { - $options = $query->getOptions(); - - if (!$options) { - return $this->client->execute($query->getName(), $query->getMethod(), $query->getParameters()); - } - - return $this->client->execute($query->getName(), $query->getMethod(), $query->getParameters(), $options); - } - - public function getClient(): Client - { - return $this->client; - } - - public function getSchema(): Schema - { - return $this->schema; - } - - public function getRepositories(): array - { - return $this->repositories; - } - - public function getExpressionBuilder(): ExpressionBuilder - { - return $this->expressionBuilder; - } -} diff --git a/src/DBAL/Repository/RecordNotFoundException.php b/src/DBAL/Repository/RecordNotFoundException.php deleted file mode 100644 index be21790..0000000 --- a/src/DBAL/Repository/RecordNotFoundException.php +++ /dev/null @@ -1,30 +0,0 @@ -modelName, $this->id)); - } - - public function getModelName(): string - { - return $this->modelName; - } - - public function getId(): int - { - return $this->id; - } -} diff --git a/src/DBAL/Repository/RecordRepository.php b/src/DBAL/Repository/RecordRepository.php deleted file mode 100644 index e40f57f..0000000 --- a/src/DBAL/Repository/RecordRepository.php +++ /dev/null @@ -1,271 +0,0 @@ - - */ -class RecordRepository -{ - public function __construct(private RecordManager $recordManager, private readonly string $modelName) - { - $recordManager->addRepository($this); - } - - /** - * Gets the model metadata from the schema. - */ - public function getMetadata(): Model - { - return $this->recordManager->getSchema()->getModel($this->modelName); - } - - /** - * Insert a new record. - * - * @return int the ID of the new record - * - * @throws \InvalidArgumentException when $data is empty - */ - public function insert(array $data): int - { - if (!$data) { - throw new \InvalidArgumentException('Data cannot be empty'); - } - - $result = $this - ->createQueryBuilder() - ->insert() - ->setValues($data) - ->getQuery() - ->execute() - ; - - return \is_scalar($result) ? (int) $result : 0; - } - - /** - * Update record(s). - * - * NB: It is not currently possible to perform “computed” updates - * (where the value being set depends on an existing value of a record). - */ - public function update(int|array $ids, array $data = []): void - { - if (!$data) { - return; - } - - $this - ->createQueryBuilder() - ->update((array) $ids) - ->setValues($data) - ->getQuery() - ->execute() - ; - } - - /** - * Delete record(s). - */ - public function delete(int|array $ids): void - { - if (!$ids) { - return; - } - - $this - ->createQueryBuilder() - ->delete((array) $ids) - ->getQuery() - ->execute() - ; - } - - /** - * Search one ID of record by criteria. - */ - public function searchOne(DomainInterface|array|null $criteria): ?int - { - return (int) $this - ->createQueryBuilder() - ->search() - ->where($this->normalizeCriteria($criteria)) - ->getQuery() - ->getOneOrNullScalarResult() - ; - } - - /** - * Search all ID of record(s). - * - * @return int[] - */ - public function searchAll(array $orders = [], int $limit = null, int $offset = null): array - { - return $this->search(null, $orders, $limit, $offset); - } - - /** - * Search ID of record(s) by criteria. - * - * @return int[] - */ - public function search(DomainInterface|array|null $criteria = null, array $orders = [], int $limit = null, int $offset = null): array - { - return $this - ->createQueryBuilder() - ->search() - ->where($this->normalizeCriteria($criteria)) - ->setOrders($orders) - ->setFirstResult($offset) - ->setMaxResults($limit) - ->getQuery() - ->getScalarResult() - ; - } - - /** - * Find ONE record by ID. - * - * @throws RecordNotFoundException when the record was not found - */ - public function read(int $id, array $fields = []): array - { - $record = $this->find($id, $fields); - - if (!$record) { - throw new RecordNotFoundException($this->modelName, $id); - } - - return $record; - } - - /** - * Find ONE record by ID. - */ - public function find(int $id, array $fields = []): ?array - { - return $this->findOneBy($this->expr()->eq('id', $id), $fields); - } - - /** - * Find ONE record by criteria. - */ - public function findOneBy(DomainInterface|array|null $criteria = null, array $fields = [], array $orders = [], int $offset = null): ?array - { - $result = $this->findBy($criteria, $fields, $orders, 1, $offset); - - return array_pop($result); - } - - /** - * Find all records. - * - * @return array[] - */ - public function findAll(array $fields = [], array $orders = [], int $limit = null, int $offset = null): array - { - return $this->findBy(null, $fields, $orders, $limit, $offset); - } - - /** - * Find record(s) by criteria. - * - * @return array[] - */ - public function findBy(DomainInterface|array|null $criteria = null, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array - { - return $this - ->createQueryBuilder() - ->select($fields) - ->where($this->normalizeCriteria($criteria)) - ->setOrders($orders) - ->setFirstResult($offset) - ->setMaxResults($limit) - ->getQuery() - ->getResult() - ; - } - - /** - * Check if a record exists. - */ - public function exists(int $id): bool - { - return 1 === $this->count($this->expr()->eq('id', $id)); - } - - /** - * Count number of all records for the model. - */ - public function countAll(): int - { - return $this->count(); - } - - /** - * Count number of records for a model and criteria. - */ - public function count(DomainInterface|array|null $criteria = null): int - { - return $this - ->createQueryBuilder() - ->select() - ->where($this->normalizeCriteria($criteria)) - ->getQuery() - ->count() - ; - } - - public function createQueryBuilder(): QueryBuilder - { - return $this->recordManager - ->createQueryBuilder($this->modelName) - ->select() - ; - } - - public function setRecordManager(RecordManager $recordManager): self - { - $this->recordManager = $recordManager; - - return $this; - } - - public function getRecordManager(): RecordManager - { - return $this->recordManager; - } - - public function getModelName(): string - { - return $this->modelName; - } - - public function expr(): ExpressionBuilder - { - return $this->recordManager->getExpressionBuilder(); - } - - public function normalizeCriteria(DomainInterface|array|null $criteria = null): ?DomainInterface - { - return \is_array($criteria) ? CompositeDomain::criteria($criteria) : $criteria; - } -} diff --git a/src/DBAL/Schema/Choice.php b/src/DBAL/Schema/Choice.php deleted file mode 100644 index d194021..0000000 --- a/src/DBAL/Schema/Choice.php +++ /dev/null @@ -1,37 +0,0 @@ -id; - } - - public function getName(): string - { - return $this->name; - } - - public function getValue(): string - { - return $this->value; - } -} diff --git a/src/DBAL/Schema/Enum/DateTimeFormat.php b/src/DBAL/Schema/Enum/DateTimeFormat.php deleted file mode 100644 index 037b072..0000000 --- a/src/DBAL/Schema/Enum/DateTimeFormat.php +++ /dev/null @@ -1,18 +0,0 @@ -id = (int) $data['id']; - $this->name = (string) $data['name']; - $this->type = FieldType::from((string) $data['ttype']); - $this->required = (bool) $data['required']; - $this->readOnly = (bool) $data['readonly']; - $this->displayName = $data['display_name'] ?? null; - $this->size = $data['size'] ?? null; - $this->selection = $data['selection'] ?? null; - $this->targetModelName = $data['relation'] ?? null; - $this->targetFieldName = $data['relation_field'] ?? null; - } - - public function getModel(): Model - { - return $this->model; - } - - public function setModel(Model $model): self - { - $this->model = $model; - - return $this; - } - - public function getId(): ?int - { - return $this->id; - } - - public function getName(): string - { - return $this->name; - } - - public function getType(): FieldType - { - return $this->type; - } - - public function isRequired(): bool - { - return $this->required; - } - - public function isReadOnly(): bool - { - return $this->readOnly; - } - - public function getDisplayName(): string - { - return $this->displayName ?: $this->name; - } - - public function getSize(): ?int - { - return $this->size; - } - - public function getSelection(): ?Selection - { - return $this->selection; - } - - public function getTargetModelName(): ?string - { - return $this->targetModelName; - } - - public function getTargetFieldName(): ?string - { - return $this->targetFieldName; - } - - public function isIdentifier(): bool - { - return 'id' === $this->name; - } - - public function isBinary(): bool - { - return FieldType::Binary === $this->type; - } - - public function isBoolean(): bool - { - return FieldType::Boolean === $this->type; - } - - public function isInteger(): bool - { - return FieldType::Integer === $this->type; - } - - public function isFloat(): bool - { - return \in_array($this->type, [FieldType::Float, FieldType::Monetary], true); - } - - public function isNumber(): bool - { - return $this->isInteger() || $this->isFloat(); - } - - public function isString(): bool - { - return \in_array($this->type, [FieldType::Char, FieldType::Text, FieldType::Html], true); - } - - public function isDate(): bool - { - return \in_array($this->type, [FieldType::Date, FieldType::DateTime], true); - } - - public function getDateFormat(): DateTimeFormat - { - return FieldType::DateTime === $this->type ? DateTimeFormat::Long : DateTimeFormat::Short; - } - - public function isSelection(): bool - { - return FieldType::Selection === $this->type; - } - - public function isSelectable(): bool - { - return null !== $this->selection; - } - - public function isAssociation(): bool - { - return $this->isSingleAssociation() || $this->isMultipleAssociation(); - } - - public function isSingleAssociation(): bool - { - return FieldType::ManyToOne === $this->type; - } - - public function isMultipleAssociation(): bool - { - return \in_array($this->type, [ - FieldType::ManyToMany, - FieldType::OneToMany, - ], true); - } -} diff --git a/src/DBAL/Schema/Model.php b/src/DBAL/Schema/Model.php deleted file mode 100644 index e118275..0000000 --- a/src/DBAL/Schema/Model.php +++ /dev/null @@ -1,138 +0,0 @@ -id = (int) $data['id']; - $this->name = (string) $data['model']; - $this->displayName = (string) $data['name']; - $this->transient = (bool) $data['transient']; - - foreach ($fields as $field) { - $this->addField($field); - } - } - - public function getSchema(): Schema - { - return $this->schema; - } - - public function getId(): int - { - return $this->id; - } - - public function getName(): string - { - return $this->name; - } - - public function getDisplayName(): string - { - return $this->displayName ?: $this->name; - } - - public function isTransient(): bool - { - return $this->transient; - } - - public function hasField(string $fieldName): bool - { - try { - $this->getField($fieldName); - } catch (SchemaException) { - return false; - } - - return true; - } - - /** - * @throws \InvalidArgumentException when the field name is empty - * @throws SchemaException when the field was not found - */ - public function getField(string $fieldName): Field - { - $model = $this; - $fields = explode('.', $fieldName); - - if (!$fields) { - throw new \InvalidArgumentException('Empty field name.'); - } - - $lastKey = \count($fields) - 1; - - foreach ($fields as $key => $subFieldName) { - $field = $model->getField($subFieldName); - - if ($lastKey === $key) { - break; - } - - $targetModel = $field->getTargetModelName(); - - if (!$targetModel) { - throw SchemaException::fieldNotFound($fieldName, $this); - } - - $model = $this->schema->getModel($targetModel); - } - - return $field; - } - - public function getFields(): array - { - return $this->fields; - } - - /** - * @return array - */ - public function getFieldNames(): array - { - $fieldNames = []; - - foreach ($this->fields as $field) { - $fieldNames[] = $field->getName(); - } - - return $fieldNames; - } - - /** - * @internal - */ - private function addField(Field $field): void - { - $field->setModel($this); - $this->fields[] = $field; - } -} diff --git a/src/DBAL/Schema/Schema.php b/src/DBAL/Schema/Schema.php deleted file mode 100644 index 5f4b0e1..0000000 --- a/src/DBAL/Schema/Schema.php +++ /dev/null @@ -1,129 +0,0 @@ -hasModel($modelName)) { - throw SchemaException::modelNotFound($modelName); - } - - if (!isset($this->loadedModels[$modelName])) { - $expr = $this->client->getExpressionBuilder(); - $modelData = (array) $this->client->request(self::IR_MODEL, OrmQuery::SEARCH_READ, $expr->normalizeDomains($expr->eq('model', $modelName))); - $modelData = $modelData[0] ?? null; - - if (!\is_array($modelData)) { - throw SchemaException::modelNotFound($modelName); - } - - $this->loadedModels[$modelName] = $this->createModel($modelData); - } - - return $this->loadedModels[$modelName]; - } - - public function hasModel(string $modelName): bool - { - return \in_array($modelName, $this->getModelNames(), true); - } - - /** - * Gets all model names. - * - * @return string[] - */ - public function getModelNames(): array - { - if (!$this->modelNames) { - $this->modelNames = array_column((array) $this->client->request(self::IR_MODEL, OrmQuery::SEARCH_READ, [[]], [ - 'fields' => ['model'], - ]), 'model'); - } - - return $this->modelNames; - } - - /** - * @internal - */ - private function createModel(array $modelData): Model - { - $expr = $this->client->getExpressionBuilder(); - $fields = (array) $this->client->request( - self::IR_MODEL_FIELDS, - OrmQuery::SEARCH_READ, - $expr->normalizeDomains($expr->eq('model_id', $modelData['id'])) - ); - - foreach ($fields as $key => $fieldData) { - $choices = []; - $selectionsIds = array_filter($fieldData['selection_ids'] ?? []); - - if (!empty($selectionsIds)) { - $choices = (array) $this->client->request( - self::IR_MODEL_FIELD_SELECTION, - OrmQuery::SEARCH_READ, - $expr->normalizeDomains($expr->eq('field_id', $fieldData['id'])) - ); - - foreach ($choices as $index => $choice) { - if (\is_array($choice)) { - $choices[$index] = new Choice((string) $choice['name'], $choice['value'], (int) $choice['id']); - } - } - } elseif (!empty($fieldData['selection'])) { - if (preg_match_all('#^\[\s*(\(\'(\w+)\'\,\s*\'(\w+)\'\)\s*\,?\s*)*\s*\]$#', trim($fieldData['selection']), $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - if (isset($match[2], $match[3])) { - $choices[] = new Choice($match[3], $match[2]); - } - } - } - } - - if ($choices) { - $fieldData['selection'] = $choices; - } - - $fields[$key] = new Field($fieldData); - } - - return new Model($this, $modelData, $fields); - } -} diff --git a/src/DBAL/Schema/SchemaException.php b/src/DBAL/Schema/SchemaException.php deleted file mode 100644 index 08b62db..0000000 --- a/src/DBAL/Schema/SchemaException.php +++ /dev/null @@ -1,25 +0,0 @@ -getName())); - } - - public static function modelNotFound(string $modelName): self - { - return new self(sprintf('The model "%s" was not found on the database', $modelName)); - } -} diff --git a/src/DBAL/Schema/Selection.php b/src/DBAL/Schema/Selection.php deleted file mode 100644 index 648fff8..0000000 --- a/src/DBAL/Schema/Selection.php +++ /dev/null @@ -1,79 +0,0 @@ -addChoice($choice); - } - } - - public function getIds(): array - { - $ids = []; - - foreach ($this->choices as $choice) { - $ids[] = $choice->getId(); - } - - return $ids; - } - - public function getNames(): array - { - $names = []; - - foreach ($this->choices as $choice) { - $names[] = $choice->getName(); - } - - return $names; - } - - public function getValues(): array - { - $values = []; - - foreach ($this->choices as $choice) { - $values[] = $choice->getValue(); - } - - return $values; - } - - /** - * @return Choice[] - */ - public function getChoices(): array - { - return $this->choices; - } - - /** - * @internal - */ - private function addChoice(Choice $choice): void - { - $this->choices[] = $choice; - } -} diff --git a/src/Exception/RemoteException.php b/src/Exception/RemoteException.php index 84c09e0..c60b61a 100644 --- a/src/Exception/RemoteException.php +++ b/src/Exception/RemoteException.php @@ -20,8 +20,8 @@ class RemoteException extends RequestException public static function create(array $payload): self { - $errorCode = $payload['error']['code']; - $errorMessage = $payload['error']['message']; + $errorCode = $payload['error']['code'] ?? 0; + $errorMessage = $payload['error']['message'] ?? 'Unknown error.'; $remoteTrace = trim($payload['error']['data']['debug']); if (preg_match('#'.preg_quote('Traceback (most recent call last):').'#', $remoteTrace)) { diff --git a/src/Transport/TransportException.php b/src/Exception/TransportException.php similarity index 78% rename from src/Transport/TransportException.php rename to src/Exception/TransportException.php index 9b583da..9e8bba3 100644 --- a/src/Transport/TransportException.php +++ b/src/Exception/TransportException.php @@ -9,9 +9,7 @@ * with this source code in the file LICENSE. */ -namespace Ang3\Component\Odoo\Transport; - -use Ang3\Component\Odoo\Exception\ExceptionInterface; +namespace Ang3\Component\Odoo\Exception; /** * @author Joanis ROUANET diff --git a/src/Transport/AbstractRpcTransport.php b/src/Transport/AbstractRpcTransport.php deleted file mode 100644 index ba48c50..0000000 --- a/src/Transport/AbstractRpcTransport.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -abstract class AbstractRpcTransport implements TransportInterface -{ - public function normalizeRpcData(string $service, string $method, array $arguments = []): array - { - return [ - 'jsonrpc' => '2.0', - 'method' => 'call', - 'params' => [ - 'service' => $service, - 'method' => $method, - 'args' => $arguments, - ], - 'id' => uniqid('odoo_jsonrpc'), - ]; - } - - protected function handleResult(array $payload): mixed - { - if (\is_array($payload['error'] ?? null)) { - throw RemoteException::create($payload); - } - - return $payload['result']; - } -} diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index 9d59cc7..66b8eda 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -12,12 +12,14 @@ namespace Ang3\Component\Odoo\Transport; use Ang3\Component\Odoo\Connection; +use Ang3\Component\Odoo\Exception\RemoteException; +use Ang3\Component\Odoo\Exception\TransportException; /** * @author Joanis ROUANET * @author Jules Sayer */ -class JsonRpcPhpStreamTransport extends AbstractRpcTransport +class JsonRpcPhpStreamTransport implements TransportInterface { /** * JSON-RPC endpoint. @@ -30,9 +32,18 @@ public function __construct( ) { } - public function request(string $service, string $method, array $arguments = []): array + public function request(string $service, string $method, array $arguments = []): mixed { - $payload = json_encode($this->normalizeRpcData($service, $method, $arguments)); + $payload = json_encode([ + 'jsonrpc' => '2.0', + 'method' => 'call', + 'params' => [ + 'service' => $service, + 'method' => $method, + 'args' => $arguments, + ], + 'id' => uniqid('odoo_jsonrpc'), + ]); if (JSON_ERROR_NONE !== json_last_error()) { throw new TransportException(sprintf('Failed to encode data to JSON: %s', json_last_error_msg())); @@ -54,12 +65,16 @@ public function request(string $service, string $method, array $arguments = []): throw new TransportException('JSON RPC request failed - Unable to get stream contents.'); } - $data = json_decode($request, true); + $data = (array) json_decode($request, true); if (JSON_ERROR_NONE !== json_last_error()) { throw new TransportException(sprintf('Failed to decode JSON data: %s', json_last_error_msg())); } - return (array) $data; + if (\is_array($data['error'] ?? null)) { + throw RemoteException::create($data); + } + + return $data['result'] ?? null; } } diff --git a/src/Transport/TransportInterface.php b/src/Transport/TransportInterface.php index 161e466..b5df559 100644 --- a/src/Transport/TransportInterface.php +++ b/src/Transport/TransportInterface.php @@ -11,6 +11,9 @@ namespace Ang3\Component\Odoo\Transport; +use Ang3\Component\Odoo\Exception\RequestException; +use Ang3\Component\Odoo\Exception\TransportException; + /** * @author Joanis ROUANET */ @@ -21,7 +24,8 @@ interface TransportInterface /** * Make a request to Odoo database. * + * @throws RequestException on bad request * @throws TransportException on transport errors */ - public function request(string $service, string $method, array $arguments = []): array; + public function request(string $service, string $method, array $arguments = []): mixed; } diff --git a/tests/AbstractTest.php b/tests/AbstractTest.php deleted file mode 100644 index 0679f5d..0000000 --- a/tests/AbstractTest.php +++ /dev/null @@ -1,37 +0,0 @@ -reflector = new Reflector(); - } - - /** - * @throws \ReflectionException - */ - protected function createObjectTester(object $object): ObjectTester - { - return new ObjectTester($this, $object); - } -} diff --git a/tests/DBAL/Expression/AbstractDomainTest.php b/tests/DBAL/Expression/AbstractDomainTest.php deleted file mode 100644 index 18f3ee1..0000000 --- a/tests/DBAL/Expression/AbstractDomainTest.php +++ /dev/null @@ -1,29 +0,0 @@ -createObjectTester($comparison) - ->assertPropertyAccessorsAndMutators('fieldName', 'bar') - ->assertPropertyAccessorsAndMutators('operator', Comparison::NOT_EQUAL_TO) - ->assertPropertyAccessorsAndMutators('value', 'mixed') - ; - } - - /** - * @covers ::toArray - */ - public function testToArray(): void - { - $comparison = new Comparison('foo', Comparison::EQUAL_TO, 'bar'); - - static::assertSame(['foo', '=', 'bar'], $comparison->toArray()); - } -} diff --git a/tests/DBAL/Expression/CompositeDomainTest.php b/tests/DBAL/Expression/CompositeDomainTest.php deleted file mode 100644 index 6306525..0000000 --- a/tests/DBAL/Expression/CompositeDomainTest.php +++ /dev/null @@ -1,152 +0,0 @@ -createMock(DomainInterface::class); - - $this - ->createObjectTester($domain) - ->assertPropertyAccessorsAndMutators('operator', CompositeDomain::OR) - ->assertPropertyAccessorsAndMutators('domains', $fakeDomain, [ - 'is_collection' => true, - 'adder' => ['name' => 'add'], - 'remover' => ['name' => 'remove'], - 'hasser' => ['name' => 'has'], - ]) - ; - } - - /** - * Data provider for the test for method ::toArray(). - */ - public function provideToArrayDataSet(): array - { - $domainA = $this->createFakeDomain('A'); - $domainB = $this->createFakeDomain('B'); - $domainC = $this->createFakeDomain('C'); - $domainD = $this->createFakeDomain('D'); - $domainE = $this->createFakeDomain('E'); - $domainF = $this->createFakeDomain('F'); - $domainG = $this->createFakeDomain('G'); - $domainH = $this->createFakeDomain('H'); - - $data = [ - // [ , , ], - [ // 0 - CompositeDomain::AND, [], [], - ], - [ // 1 - CompositeDomain::AND, [$domainA], - [$domainA->toArray()], - ], - [ // 2 - CompositeDomain::AND, [$domainA, $domainB], - ['&', $domainA->toArray(), $domainB->toArray()], - ], - [ // 3 - CompositeDomain::AND, [$domainA, $domainB, $domainC], - ['&', $domainA->toArray(), '&', $domainB->toArray(), $domainC->toArray()], - ], - [ // 4 - CompositeDomain::OR, [], [], - ], - [ // 5 - CompositeDomain::OR, [$domainA], - [ $domainA->toArray() ], - ], - [ // 6 - CompositeDomain::OR, [$domainA, $domainB], - ['|', $domainA->toArray(), $domainB->toArray()], - ], - [ // 7 - CompositeDomain::OR, [$domainA, $domainB, $domainC], - ['|', $domainA->toArray(), '|', $domainB->toArray(), $domainC->toArray()], - ], - [ // 8 - CompositeDomain::NOT, [], [], - ], - [ // 9 - CompositeDomain::NOT, [$domainA], - ['!', $domainA->toArray()], - ], - [ // 10 - CompositeDomain::NOT, [$domainA, $domainB], - ['!', '&', $domainA->toArray(), $domainB->toArray()], - ], - [ // 11 - CompositeDomain::NOT, [$domainA, $domainB, $domainC], - ['!', '&', $domainA->toArray(), '&', $domainB->toArray(), $domainC->toArray()], - ], - ]; - - /** - * @see https://www.odoo.com/fr_FR/forum/aide-1/question/domain-notation-using-multiple-and-nested-and-2170 - */ - $orXA = new CompositeDomain(CompositeDomain::OR, [$domainA, $domainB]); - $orXB = new CompositeDomain(CompositeDomain::OR, [$domainC, $domainD, $domainE]); - $expectedResult = ['&', '|', ['A'], ['B'], '|', ['C'], '|', ['D'], ['E']]; - $data[] = [CompositeDomain::AND, [$orXA, $orXB], $expectedResult]; - - // #13 Final test - $orXA = new CompositeDomain(CompositeDomain::OR, [$domainA, $domainB]); - $orXB = new CompositeDomain(CompositeDomain::OR, [$domainC, $domainD, $domainE]); - $orXC = new CompositeDomain(CompositeDomain::OR, [$domainF, $domainG, $domainH]); - $expectedResult = ['&', '|', ['A'], ['B'], '&', '|', ['C'], '|', ['D'], ['E'], '|', ['F'], '|', ['G'], ['H']]; - $data[] = [CompositeDomain::AND, [$orXA, $orXB, $orXC], $expectedResult]; - - return $data; - } - - /** - * @covers ::toArray - * - * @dataProvider provideToArrayDataSet - */ - public function testToArray(string $operator, array $domains = [], mixed $expectedResult = null, string $message = ''): void - { - $domain = new CompositeDomain($operator, $domains); - static::assertSame($expectedResult, $domain->toArray(), $message); - } - - protected function createFakeDomain(mixed $expression): DomainInterface - { - $fakeDomain = $this->createMock(DomainInterface::class); - $fakeDomain - ->method('toArray') - ->willReturn(\is_array($expression) ? $expression : (array) $expression) - ; - - return $fakeDomain; - } -} diff --git a/tests/Utils/ObjectTester.php b/tests/Utils/ObjectTester.php deleted file mode 100644 index c24ea11..0000000 --- a/tests/Utils/ObjectTester.php +++ /dev/null @@ -1,361 +0,0 @@ - null, - self::VALUE => null, - self::RESULT => null, - self::MESSAGE => null, - self::IS_FLUENT => false, - self::IS_COLLECTION => false, - ]; - - /** - * @throws \ReflectionException - */ - public function __construct(TestCase $testCase, object $object, array $defaultContext = []) - { - parent::__construct($testCase); - - $this->reflector = new Reflector(); - $this->setObject($object); - $this->defaultContext = array_merge($this->defaultContext, $defaultContext); - } - - /** - * Test the accessors of a property (setter and getter). - * This test also checks if the return value of the getter is equal to the value registered with the setter. - */ - public function assertPropertyAccessorsAndMutators(string $propertyName, mixed $value, array $context = []): self - { - $context = $this->getContext($context); - $context[self::VALUE] = $value; - $context[self::MESSAGE] = sprintf('Asserting accessors and mutators for property %s::$%s', $this->class->getShortName(), $propertyName); - - if ($context[self::IS_COLLECTION]) { - $adderContext = $this->getContext($context['adder'] ?? [], $context); - $adder = $this->assertAdder($propertyName, $value, $adderContext); - - $hasserContext = $this->getContext($context['hasser'] ?? [], $context); - $hasser = $this->assertHasser($propertyName, $value, $hasserContext); - - $removerContext = $this->getContext($context['remover'] ?? [], $context); - $remover = $this->assertRemover($propertyName, $value, $removerContext); - } - - $setterContext = $this->getContext($context['setter'] ?? [], $context); - $setter = $this->assertSetter($propertyName, $value, $setterContext); - - $getterContext = $this->getContext($context['getter'] ?? [], $context); - $getter = $this->assertGetter($propertyName, $getterContext); - - return $this; - } - - public function assertHasser(string $propertyName, mixed $value, array $context = []): ?\ReflectionMethod - { - $context[self::VALUE] = $value; - $context[self::IS_FLUENT] = false; - $context[self::MESSAGE] = sprintf( - 'Asserting hasser for property %s::$%s', - $this->class->getShortName(), - $propertyName - ); - - return $this->assertPropertyMethod($propertyName, self::ACCESSOR_HAS, $context); - } - - public function assertGetter(string $propertyName, array $context = []): ?\ReflectionMethod - { - $context[self::IS_FLUENT] ??= false; - $context[self::MESSAGE] = sprintf( - 'Asserting getter for property %s::$%s', - $this->class->getShortName(), - $propertyName - ); - - return $this->assertPropertyMethod($propertyName, self::ACCESSOR_GET, $context); - } - - public function assertAdder(string $propertyName, mixed $value, array $context = []): ?\ReflectionMethod - { - $context[self::VALUE] = $value; - $context[self::IS_FLUENT] ??= true; - $context[self::MESSAGE] = sprintf( - 'Asserting adder for property %s::$%s', - $this->class->getShortName(), - $propertyName - ); - - return $this->assertPropertyMethod($propertyName, self::MUTATOR_ADD, $context); - } - - public function assertRemover(string $propertyName, mixed $value, array $context = []): ?\ReflectionMethod - { - $context[self::VALUE] = $value; - $context[self::IS_FLUENT] ??= true; - $context[self::MESSAGE] = sprintf( - 'Asserting remover for property %s::$%s', - $this->class->getShortName(), - $propertyName - ); - - return $this->assertPropertyMethod($propertyName, self::MUTATOR_REMOVE, $context); - } - - /** - * Test and return the setter of a property. - */ - public function assertSetter(string $propertyName, mixed $value = null, array $context = []): ?\ReflectionMethod - { - $context[self::VALUE] = $value; - $context[self::IS_FLUENT] ??= true; - $context[self::MESSAGE] = sprintf( - 'Asserting setter for property %s::$%s', - $this->class->getShortName(), - $propertyName - ); - - return $this->assertPropertyMethod($propertyName, self::MUTATOR_SET, $context); - } - - /** - * Test and return the specific method of a property. - */ - public function assertPropertyMethod(string $propertyName, string $prefix, array $context = []): ?\ReflectionMethod - { - $context = $this->getContext($context); - - try { - $property = $this->reflector->getProperty($this->class, $propertyName); - } catch (\ReflectionException $e) { - $this->testCase::fail($this->getContextErrorMessage('The property was not found', $context)); - } - - $context[self::NAME] = (string) ($context[self::NAME] ?? null); - $methodName = $context[self::NAME] ?: $this->getPropertyMethodName($property->getName(), $prefix); - $class = $property->getDeclaringClass(); - $tested = []; - - if (!$context[self::NAME] && $context[self::IS_COLLECTION]) { - $methodNames = $this->getSingularNames($property->getName(), $prefix); - - foreach ($methodNames as $value) { - if ($class->hasMethod($value)) { - $methodName = $value; - break; - } - - $tested[] = $value; - } - } else { - $tested[] = $methodName; - } - - try { - $method = $class->getMethod($methodName); - } catch (\ReflectionException $e) { - $errorMessage = sprintf('None of methods "%s()" was found', implode('"(), "', $tested)); - $this->testCase::fail($this->getContextErrorMessage($errorMessage, $context)); - } - - $this->testCase->addToAssertionCount(1); - - $args = self::MUTATOR_SET === $prefix && $context[self::IS_COLLECTION] ? [$context[self::VALUE]] : $context[self::VALUE]; - $args = self::ACCESSOR_GET === $prefix ? [] : [$args]; - $result = $method->invokeArgs($this->object, $args); - - if ((bool) ($context[self::IS_FLUENT] ?? false)) { - $this->testCase::assertEquals($this->object, $result, $this->getContextErrorMessage( - 'The method is fluent and should return the object instance', - $context - )); - - return $method; - } - - $propertyValue = $property->getValue($this->object); - - switch ($prefix) { - case self::ACCESSOR_GET: - $this->testCase::assertEquals($result, $propertyValue); - break; - - case self::MUTATOR_SET: - $expectedValue = $context[self::IS_COLLECTION] ? [$context[self::VALUE]] : $context[self::VALUE]; - $this->testCase::assertEquals($expectedValue, $propertyValue, $this->getContextErrorMessage( - 'The property value is not equal to the value set', - $context - )); - break; - - case self::MUTATOR_ADD: - case self::MUTATOR_REMOVE: - case self::ACCESSOR_HAS: - if (!is_iterable($propertyValue)) { - $this->testCase::fail($this->getContextErrorMessage(sprintf( - 'The property collection should be iterable, %s declared', - get_debug_type($propertyValue) - ), $context)); - } - - $hasValue = false; - - foreach ($propertyValue as $value) { - if ($value === $context[self::VALUE]) { - $hasValue = true; - break; - } - } - - switch ($prefix) { - case self::ACCESSOR_HAS: - $this->testCase::assertEquals($result, $hasValue, $this->getContextErrorMessage( - sprintf( - 'The property collection %s the value but the hasser returns %s', - $hasValue ? 'contains' : 'does not contain', - $result ? 'TRUE' : 'FALSE' - ), - $context - )); - break; - case self::MUTATOR_ADD: - $this->testCase::assertTrue($hasValue, $this->getContextErrorMessage( - 'The adder was called but the property collection does not contain the added value', - $context - )); - - return $method; - - case self::MUTATOR_REMOVE: - $this->testCase::assertFalse($hasValue, $this->getContextErrorMessage( - 'The property collection still contains the removed value', - $context - )); - - return $method; - } - break; - } - - return $method; - } - - public function getContextErrorMessage(string $message, array $context = []): string - { - $context = $this->getContext($context); - - return sprintf('%s%s', $context[self::MESSAGE] ? sprintf('[%s] ', $context[self::MESSAGE]) : '', $message); - } - - /** - * @return string[] - */ - public function getSingularNames(string $name, string $prefix = null): array - { - $names = (array) Inflector::singularize($name); - - if ($prefix) { - foreach ($names as $key => $value) { - $names[$key] = $this->getPropertyMethodName($value, $prefix); - } - } - - return $names; - } - - public function getPropertyMethodName(string $propertyName, string $prefix = ''): string - { - $propertyName = preg_replace('#([^A-Za-z]+)#', '', $propertyName); - - return sprintf('%s%s', $prefix, $prefix ? ucfirst($propertyName) : $propertyName); - } - - public function getObject(): object - { - return $this->object; - } - - /** - * @throws \ReflectionException - */ - public function setObject(object $object): self - { - $this->object = $object; - $this->class = $this->reflector->getClass($object); - - return $this; - } - - public function getClass(): \ReflectionClass - { - return $this->class; - } - - public function getReflector(): Reflector - { - return $this->reflector; - } - - public function setReflector(Reflector $reflector): self - { - $this->reflector = $reflector; - - return $this; - } - - public function getDefaultContext(): array - { - return $this->defaultContext; - } - - public function setDefaultContext(array $defaultContext): self - { - $this->defaultContext = $defaultContext; - - return $this; - } - - public function getContext(array $context = [], array $defaultContext = []): array - { - return array_merge($defaultContext ?: $this->defaultContext, $context); - } -} diff --git a/tests/Utils/Reflector.php b/tests/Utils/Reflector.php deleted file mode 100644 index 6b5e031..0000000 --- a/tests/Utils/Reflector.php +++ /dev/null @@ -1,71 +0,0 @@ -getProperty($object, $propertyName)->getValue($object); - } - - /** - * @throws \ReflectionException - */ - public function setObjectValue(object $object, string $propertyName, mixed $value): \ReflectionProperty - { - $property = $this->getProperty($object, $propertyName); - - $property->setValue($object, $value); - - return $property; - } - - /** - * @throws \ReflectionException - */ - public function getMethod(object|string $objectOrClass, string $methodName, bool $setAccessible = true): ?\ReflectionMethod - { - $class = $this->getClass($objectOrClass); - - return $class->getMethod($methodName); - } - - /** - * @throws \ReflectionException - */ - public function getProperty(object|string $objectOrClass, string $propertyName, bool $setAccessible = true): ?\ReflectionProperty - { - $class = $this->getClass($objectOrClass); - - return $class->getProperty($propertyName); - } - - /** - * @throws \ReflectionException - */ - public function getClass(object|string $objectOrClass): \ReflectionClass - { - if (\is_string($objectOrClass)) { - if (!class_exists($objectOrClass)) { - throw new \RuntimeException(sprintf('The class "%s" was not found.', $objectOrClass)); - } - - return new \ReflectionClass($objectOrClass); - } - - return $objectOrClass instanceof \ReflectionClass ? $objectOrClass : new \ReflectionClass($objectOrClass); - } -} diff --git a/tests/Utils/TestDecorator.php b/tests/Utils/TestDecorator.php deleted file mode 100644 index c448cd5..0000000 --- a/tests/Utils/TestDecorator.php +++ /dev/null @@ -1,36 +0,0 @@ -testCase = $testCase; - } - - public function getTestCase(): TestCase - { - return $this->testCase; - } - - public function setTestCase(TestCase $testCase): self - { - $this->testCase = $testCase; - - return $this; - } -} From 72b92822b8b74b0acca2dc596058b91c2fb3a87a Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Mon, 6 Nov 2023 14:35:49 +0100 Subject: [PATCH 46/80] Create ClientTest.php --- tests/ClientTest.php | 142 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 tests/ClientTest.php diff --git a/tests/ClientTest.php b/tests/ClientTest.php new file mode 100644 index 0000000..a5d0aab --- /dev/null +++ b/tests/ClientTest.php @@ -0,0 +1,142 @@ +connection = $this->createMock(Connection::class); + $this->transport = $this->createMock(TransportInterface::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->client = new Client($this->connection, $this->transport, $this->logger); + } + + public function testConstruct(): void + { + $client = new Client($this->connection); + + // Asserting default transport + self::assertInstanceOf(JsonRpcPhpStreamTransport::class, $client->getTransport()); + + // Asserting optional logger + self::assertNull($client->getLogger()); + } + + public function provideRequestData(): array + { + return [ + [ OdooService::Common, OdooMethod::Login ], + [ OdooService::Common, OdooMethod::Version ], + [ OdooService::Object, OdooMethod::ExecuteKw ], + ]; + } + + /** + * @dataProvider provideRequestData + */ + public function testRequest(OdooService $service, OdooMethod $method): void + { + $this->transport + ->expects($this->once()) + ->method('request') + ->with($service->value, $method->value, [1, 2, 3]) + ->willReturn('foo') + ; + + $result = $this->client->request($service->value, $method->value, 1, 2, 3); + self::assertEquals('foo', $result); + } + + /** + * @dataProvider provideRequestData + */ + public function testRequestRemoteError(OdooService $service, OdooMethod $method): void + { + self::expectException(RemoteException::class); + $this->transport + ->expects($this->once()) + ->method('request') + ->with($service->value, $method->value, [1, 2, 3]) + ->willThrowException(RemoteException::create([ + 'error' => [ + 'code' => 123, + 'message' => 'Test error', + 'data' => [ + 'debug' => 'foo' + ] + ], + ])) + ; + + $this->client->request($service->value, $method->value, 1, 2, 3); + } + + /** + * @depends testRequest + */ + public function testExecuteKw(): void + { + // ... + } + + /** + * @depends testRequest + */ + public function testVersion(): void + { + $this->transport + ->expects($this->once()) + ->method('request') + ->with(OdooService::Common->value, OdooMethod::Version->value) + ->willReturn([ + 'server_version_info' => [13, 3, 7, 'a', 'b', 'c'], + 'protocol_version' => 1, + ]) + ; + + $version = $this->client->version(); + self::assertInstanceOf(Version::class, $version); + } + + /** + * @depends testRequest + */ + public function testAuthenticate(): void + { + list($database, $username, $password) = ['foo', 'bar', 'qux']; + $this->connection->expects($this->once())->method('getDatabase')->willReturn($database); + $this->connection->expects($this->once())->method('getUsername')->willReturn($username); + $this->connection->expects($this->once())->method('getPassword')->willReturn($password); + $expectedUid = 1337; + + $this->transport + ->expects($this->once()) + ->method('request') + ->with(OdooService::Common->value, OdooMethod::Login->value, [$database, $username, $password]) + ->willReturn($expectedUid) + ; + + $uid = $this->client->authenticate(); + self::assertEquals($expectedUid, $uid); + self::assertEquals($expectedUid, $this->client->getUid()); + } +} \ No newline at end of file From 9f1046195c5ed23a92b318567f6142c478f88901 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Mon, 6 Nov 2023 14:36:59 +0100 Subject: [PATCH 47/80] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa05540..24a6ebd 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ These previous exception can be thrown by all methods of the client. ### ExecuteKw -The client has a shortcut for the Odoo method `executeKw` of Odoo service `object`. +The client has a shortcut for the Odoo method `execute_kw` of Odoo service `object`. By calling the method `executeKw`, the client tries to authenticate then makes the request and returns result. ```php From 693adfcb4ad37bbdd2ba3500f64138894f1fd219 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Mon, 6 Nov 2023 14:42:35 +0100 Subject: [PATCH 48/80] Update README.md --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 24a6ebd..282a345 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ $client = Client::create([ 'database' => '', 'username' => '', 'password' => '', -], $logger = null); +], $transport = null, $logger = null); ``` Exceptions: @@ -111,6 +111,16 @@ $version = $client->version(); // \Ang3\Component\Odoo\Metadata\Version dump($version); ``` +### Custom transport + +By default, the library provides a JSON-RPC transport and the client create it by default +if no transport is explicitly provided on constructor. + +You can provide your own transport when you create your client as second argument. By this way, the JSON-RPC transport +will not be created and the client will use yours instead. + +Your custom transport must implement the interface `Ang3\Component\Odoo\Transport\TransportInterface`. + ### Database Abstraction Layer (DBAL) You need to manage your Odoo database models? From 0a5732ec27fac3bdde703bd6d500e13e976d1e62 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Mon, 6 Nov 2023 14:43:13 +0100 Subject: [PATCH 49/80] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 282a345..08d41c3 100644 --- a/README.md +++ b/README.md @@ -113,8 +113,8 @@ dump($version); ### Custom transport -By default, the library provides a JSON-RPC transport and the client create it by default -if no transport is explicitly provided on constructor. +By default, the library provides a JSON-RPC transport and the client creates it by default +if no transport is explicitly provided. You can provide your own transport when you create your client as second argument. By this way, the JSON-RPC transport will not be created and the client will use yours instead. From af5a80c10d9ecc21de4a1edf774bcf9398ce1216 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Mon, 6 Nov 2023 15:30:49 +0100 Subject: [PATCH 50/80] Update php_lint.yml --- .github/workflows/php_lint.yml | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/.github/workflows/php_lint.yml b/.github/workflows/php_lint.yml index 8e614e9..3875d99 100644 --- a/.github/workflows/php_lint.yml +++ b/.github/workflows/php_lint.yml @@ -1,14 +1,32 @@ name: PHP Linting -on: [push, pull_request] +on: + push: + branches-ignore: + - 'main' + pull_request: + branches-ignore: + - 'main' jobs: php-cs-fixer: name: PHP-CS-Fixer runs-on: ubuntu-latest steps: - - uses: actions/checkout@master - - name: PHP-CS-Fixer - uses: docker://oskarstark/php-cs-fixer-ga + - name: Setup PHP + uses: shivammathur/setup-php@v2 with: - args: src --diff --dry-run --config=.php-cs-fixer.php-highest.php \ No newline at end of file + php-version: '8.1' + - name: Install PHP-CS-Fixer + run: | + wget https://cs.symfony.com/download/php-cs-fixer-v3.phar -O php-cs-fixer + sudo chmod a+x php-cs-fixer + sudo mv php-cs-fixer /usr/local/bin/php-cs-fixer + - name: Checkout source code + uses: actions/checkout@master + - name: Run PHP-CS-Fixer - Fix + run: php-cs-fixer fix src -vvv --diff --config=.php-cs-fixer.php-highest.php + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Fix PHP code via php-cs-fixer (auto) [no ci] From 002bd1c0fa553cbbec29faffcc7a282b9b38caa1 Mon Sep 17 00:00:00 2001 From: Ang3 Date: Mon, 6 Nov 2023 14:31:13 +0000 Subject: [PATCH 51/80] Fix PHP code via php-cs-fixer (auto) [no ci] --- src/Client.php | 8 +++----- src/Exception/ExceptionInterface.php | 4 +--- src/Exception/MissingConfigParameterException.php | 4 +--- src/Exception/RequestException.php | 4 +--- src/Exception/TransportException.php | 4 +--- src/Metadata/Version.php | 3 +-- src/Transport/JsonRpcPhpStreamTransport.php | 3 +-- 7 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/Client.php b/src/Client.php index 87ede96..ff2ca7d 100644 --- a/src/Client.php +++ b/src/Client.php @@ -32,7 +32,7 @@ class Client public function __construct( private readonly Connection $connection, - ?TransportInterface $transport = null, + TransportInterface $transport = null, private ?LoggerInterface $logger = null ) { $this->transport = $transport ?: new JsonRpcPhpStreamTransport($this->connection); @@ -48,8 +48,8 @@ public function __construct( */ public static function create( array $config, - ?TransportInterface $transport = null, - ?LoggerInterface $logger = null + TransportInterface $transport = null, + LoggerInterface $logger = null ): self { return new self(Connection::create($config), $transport, $logger); } @@ -97,8 +97,6 @@ public function authenticate(): int } /** - * @param mixed ...$arguments - * * @throws RequestException on request errors * @throws TransportException on transport errors */ diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index ee50698..a3304e6 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -interface ExceptionInterface extends \Throwable -{ -} +interface ExceptionInterface extends \Throwable {} diff --git a/src/Exception/MissingConfigParameterException.php b/src/Exception/MissingConfigParameterException.php index 1ef3f23..fdb39a0 100644 --- a/src/Exception/MissingConfigParameterException.php +++ b/src/Exception/MissingConfigParameterException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface -{ -} +class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface {} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 204c7dc..50cf65a 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface -{ -} +class RequestException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 9e8bba3..057e514 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface -{ -} +class TransportException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 67d4ebe..728879b 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -21,8 +21,7 @@ public function __construct( private readonly string $buildIdentifier, private readonly string $buildVersion, private readonly int $protocolVersion - ) { - } + ) {} /** * Creates the instance from Odoo response payload. diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index 66b8eda..ae48119 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -29,8 +29,7 @@ class JsonRpcPhpStreamTransport implements TransportInterface public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) { - } + ) {} public function request(string $service, string $method, array $arguments = []): mixed { From ef6a026946cb99c5dc5aa63a3c1b504d23d463b3 Mon Sep 17 00:00:00 2001 From: NEOSOLVA INFORMATIQUE Date: Mon, 6 Nov 2023 16:08:19 +0100 Subject: [PATCH 52/80] Client tests --- composer.json | 2 +- composer.lock | 390 ++++++++++++++++++++++++++++++++++--------- tests/ClientTest.php | 66 +++++++- 3 files changed, 377 insertions(+), 81 deletions(-) diff --git a/composer.json b/composer.json index 909166b..4bd53e5 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "ang3/php-odoo-api-client", "type": "component", "description": "Odoo API client", - "keywords": ["odoo", "api", "client", "domain", "operation", "builder", "trransport", "json"], + "keywords": ["odoo", "api", "client", "transport", "json"], "license": "MIT", "authors": [ { diff --git a/composer.lock b/composer.lock index 409bdf7..7836196 100644 --- a/composer.lock +++ b/composer.lock @@ -18,7 +18,13 @@ "type": "zip", "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=8.0.0" @@ -34,7 +40,7 @@ "Psr\\Log\\": "src" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -70,7 +76,13 @@ "type": "zip", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": "^8.1" @@ -91,7 +103,7 @@ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -140,7 +152,13 @@ "type": "zip", "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-dom": "*", @@ -160,7 +178,7 @@ "Masterminds\\": "src" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -207,7 +225,13 @@ "type": "zip", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": "^7.1 || ^8.0" @@ -230,7 +254,7 @@ "DeepCopy\\": "src/DeepCopy/" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -266,7 +290,13 @@ "type": "zip", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-tokenizer": "*", @@ -290,7 +320,7 @@ "PhpParser\\": "lib/PhpParser" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -322,7 +352,13 @@ "type": "zip", "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-dom": "*", @@ -342,7 +378,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -382,7 +418,13 @@ "type": "zip", "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": "^7.2 || ^8.0" @@ -393,7 +435,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -433,7 +475,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-dom": "*", @@ -468,7 +516,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -511,7 +559,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -530,7 +584,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -571,7 +625,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -594,7 +654,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -634,7 +694,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -653,7 +719,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -693,7 +759,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -712,7 +784,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -752,7 +824,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f3d767f7f9e191eab4189abe41ab37797e30b1be", "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "doctrine/instantiator": "^1.3.1 || ^2", @@ -804,7 +882,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -855,7 +933,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -874,7 +958,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -911,7 +995,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -930,7 +1020,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -967,7 +1057,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -986,7 +1082,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1022,7 +1118,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3", @@ -1043,7 +1145,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1096,7 +1198,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "nikic/php-parser": "^4.7", @@ -1116,7 +1224,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1153,7 +1261,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -1173,7 +1287,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1219,7 +1333,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -1241,7 +1361,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1282,7 +1402,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3", @@ -1303,7 +1429,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1359,7 +1485,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", "reference": "bde739e7565280bda77be70044ac1047bc007e34", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3", @@ -1384,7 +1516,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1423,7 +1555,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "nikic/php-parser": "^4.6", @@ -1443,7 +1581,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1480,7 +1618,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3", @@ -1501,7 +1645,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1537,7 +1681,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -1556,7 +1706,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1592,7 +1742,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -1611,7 +1767,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1655,7 +1811,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -1674,7 +1836,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1710,7 +1872,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -1729,7 +1897,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1766,7 +1934,13 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.3" @@ -1782,7 +1956,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], @@ -1819,7 +1993,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ca4a988488f61ac18f8f845445eabdd36f89aa8d", "reference": "ca4a988488f61ac18f8f845445eabdd36f89aa8d", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=8.1", @@ -1840,7 +2020,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -1887,7 +2067,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57", "reference": "883d961421ab1709877c10ac99451632a3d6fa57", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=8.1" @@ -1901,7 +2087,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -1952,7 +2138,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=8.1" @@ -1972,7 +2164,7 @@ "function.php" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -2019,7 +2211,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/3fdd2a3d5fdc363b2e8dbf817f9726a4d013cbd1", "reference": "3fdd2a3d5fdc363b2e8dbf817f9726a4d013cbd1", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "masterminds/html5": "^2.6", @@ -2039,7 +2237,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -2086,7 +2284,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/c6f1df6a76c2c12bd14a0a5bf7c556dd935efe1d", "reference": "c6f1df6a76c2c12bd14a0a5bf7c556dd935efe1d", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.1.3" @@ -2120,7 +2324,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -2167,7 +2371,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.1" @@ -2196,7 +2406,7 @@ "Symfony\\Polyfill\\Ctype\\": "" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -2249,7 +2459,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", "reference": "42292d99c55abe617799667f454222c54c60e229", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.1" @@ -2278,7 +2494,7 @@ "Symfony\\Polyfill\\Mbstring\\": "" } }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -2332,7 +2548,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/test-pack/zipball/7b708c588cb3e1c8f0281889f273b920cbddff9b", "reference": "7b708c588cb3e1c8f0281889f273b920cbddff9b", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "phpunit/phpunit": "^9.5", @@ -2347,7 +2569,7 @@ "symfony/phpunit-bridge": "*" }, "type": "symfony-pack", - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -2384,7 +2606,13 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/var-dumper/zipball/999ede244507c32b8e43aebaa10e9fce20de7c97", "reference": "999ede244507c32b8e43aebaa10e9fce20de7c97", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=8.1", @@ -2417,7 +2645,7 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "MIT" ], @@ -2468,7 +2696,13 @@ "type": "zip", "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-dom": "*", @@ -2482,7 +2716,7 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", + "notification-url": "https://repo.packagist.com/neosolva/downloads/", "license": [ "BSD-3-Clause" ], diff --git a/tests/ClientTest.php b/tests/ClientTest.php index a5d0aab..54f31a0 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -35,12 +35,43 @@ public function testConstruct(): void $client = new Client($this->connection); // Asserting default transport - self::assertInstanceOf(JsonRpcPhpStreamTransport::class, $client->getTransport()); + $clientTransport = $client->getTransport(); + self::assertInstanceOf(JsonRpcPhpStreamTransport::class, $clientTransport); + + // Asserting optional logger + $clientLogger = $client->getLogger(); + self::assertNull($clientLogger); + } + + public function testConstructWithCustomTransport(): void + { + $customTransport = $this->createMock(TransportInterface::class); + $client = new Client($this->connection, $customTransport); + + // Asserting custom transport + $clientTransport = $client->getTransport(); + self::assertInstanceOf(TransportInterface::class, $clientTransport); + self::assertEquals($customTransport, $clientTransport); // Asserting optional logger self::assertNull($client->getLogger()); } + public function testConstructWithLogger(): void + { + $logger = $this->createMock(LoggerInterface::class); + $client = new Client($this->connection, null, $logger); + + // Asserting default transport + $clientTransport = $client->getTransport(); + self::assertInstanceOf(JsonRpcPhpStreamTransport::class, $clientTransport); + + // Asserting logger + $clientLogger = $client->getLogger(); + self::assertInstanceOf(LoggerInterface::class, $clientLogger); + self::assertEquals($logger, $clientLogger); + } + public function provideRequestData(): array { return [ @@ -95,7 +126,38 @@ public function testRequestRemoteError(OdooService $service, OdooMethod $method) */ public function testExecuteKw(): void { - // ... + list($database, $username, $password) = ['foo', 'bar', 'qux']; + $this->connection->expects($this->exactly(2))->method('getDatabase')->willReturn($database); + $this->connection->expects($this->once())->method('getUsername')->willReturn($username); + $this->connection->expects($this->exactly(2))->method('getPassword')->willReturn($password); + $expectedUid = 1337; + $expectedResult = 'foo'; + + $authenticationArguments = [OdooService::Common->value, OdooMethod::Login->value, [$database, $username, $password]]; + $requestArguments = [OdooService::Object->value, OdooMethod::ExecuteKw->value, [ + $database, + $expectedUid, + $password, + $name = 'object_name', + $method = 'object_method', + $parameters = [1, 2, 3], + $options = [4, 5, 6] + ]]; + + $this->transport + ->expects($this->exactly(2)) + ->method('request') + ->withConsecutive($authenticationArguments, $requestArguments) + ->willReturn($this->returnCallback(function ($service) use ($authenticationArguments, $expectedUid) { + return match($service) { + OdooService::Common->value => $expectedUid, + default => 'foo' + }; + })) + ; + + $result = $this->client->executeKw($name, $method, $parameters, $options); + self::assertEquals($expectedResult, $result); } /** From e5ada2e936def05fda99b84cc111457a39bd8506 Mon Sep 17 00:00:00 2001 From: NEOSOLVA INFORMATIQUE Date: Mon, 6 Nov 2023 16:48:59 +0100 Subject: [PATCH 53/80] JSONRPC transport tests --- src/Client.php | 2 +- src/Exception/ExceptionInterface.php | 4 +- .../MissingConfigParameterException.php | 4 +- src/Exception/RequestException.php | 4 +- src/Exception/TransportException.php | 4 +- src/Metadata/Version.php | 3 +- src/Transport/JsonRpcPhpStreamTransport.php | 19 +++++-- .../jsonrpc_endpoints/json_error/jsonrpc | 3 + .../jsonrpc_endpoints/remote_error/jsonrpc | 9 +++ .../jsonrpc_endpoints/success/jsonrpc | 5 ++ .../JsonRpcPhpStreamTransportTest.php | 56 +++++++++++++++++++ 11 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 tests/Resources/jsonrpc_endpoints/json_error/jsonrpc create mode 100644 tests/Resources/jsonrpc_endpoints/remote_error/jsonrpc create mode 100644 tests/Resources/jsonrpc_endpoints/success/jsonrpc create mode 100644 tests/Transport/JsonRpcPhpStreamTransportTest.php diff --git a/src/Client.php b/src/Client.php index ff2ca7d..d967c92 100644 --- a/src/Client.php +++ b/src/Client.php @@ -100,7 +100,7 @@ public function authenticate(): int * @throws RequestException on request errors * @throws TransportException on transport errors */ - public function request(string $service, string $method, ...$arguments): mixed + public function request(string $service, string $method, mixed ...$arguments): mixed { $context = [ 'service' => $service, diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index a3304e6..ee50698 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -interface ExceptionInterface extends \Throwable {} +interface ExceptionInterface extends \Throwable +{ +} diff --git a/src/Exception/MissingConfigParameterException.php b/src/Exception/MissingConfigParameterException.php index fdb39a0..1ef3f23 100644 --- a/src/Exception/MissingConfigParameterException.php +++ b/src/Exception/MissingConfigParameterException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface {} +class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 50cf65a..204c7dc 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface {} +class RequestException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 057e514..9e8bba3 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface {} +class TransportException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 728879b..67d4ebe 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -21,7 +21,8 @@ public function __construct( private readonly string $buildIdentifier, private readonly string $buildVersion, private readonly int $protocolVersion - ) {} + ) { + } /** * Creates the instance from Odoo response payload. diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index ae48119..fff23d0 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -24,12 +24,13 @@ class JsonRpcPhpStreamTransport implements TransportInterface /** * JSON-RPC endpoint. */ - public const ENDPOINT_JSON_RPC = 'jsonrpc'; + public const DEFAULT_ENDPOINT = '/jsonrpc'; public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) {} + ) { + } public function request(string $service, string $method, array $arguments = []): mixed { @@ -57,8 +58,8 @@ public function request(string $service, string $method, array $arguments = []): ], ]); - $url = sprintf('%s/%s', $this->connection->getUrl(), self::ENDPOINT_JSON_RPC); - $request = file_get_contents($url, false, $context); + $endpointUrl = $this->connection->getUrl().self::DEFAULT_ENDPOINT; + $request = file_get_contents($endpointUrl, false, $context); if (false === $request) { throw new TransportException('JSON RPC request failed - Unable to get stream contents.'); @@ -76,4 +77,14 @@ public function request(string $service, string $method, array $arguments = []): return $data['result'] ?? null; } + + public function getConnection(): Connection + { + return $this->connection; + } + + public function getTimeOut(): int + { + return $this->timeOut; + } } diff --git a/tests/Resources/jsonrpc_endpoints/json_error/jsonrpc b/tests/Resources/jsonrpc_endpoints/json_error/jsonrpc new file mode 100644 index 0000000..4b34730 --- /dev/null +++ b/tests/Resources/jsonrpc_endpoints/json_error/jsonrpc @@ -0,0 +1,3 @@ +{ + "result": "{"error":"true"}" +} \ No newline at end of file diff --git a/tests/Resources/jsonrpc_endpoints/remote_error/jsonrpc b/tests/Resources/jsonrpc_endpoints/remote_error/jsonrpc new file mode 100644 index 0000000..a39652f --- /dev/null +++ b/tests/Resources/jsonrpc_endpoints/remote_error/jsonrpc @@ -0,0 +1,9 @@ +{ + "error": { + "code": 123, + "message": "Odoo error", + "data": { + "debug": "foo" + } + } +} diff --git a/tests/Resources/jsonrpc_endpoints/success/jsonrpc b/tests/Resources/jsonrpc_endpoints/success/jsonrpc new file mode 100644 index 0000000..ae002c1 --- /dev/null +++ b/tests/Resources/jsonrpc_endpoints/success/jsonrpc @@ -0,0 +1,5 @@ +{ + "result": { + "success":"true" + } +} \ No newline at end of file diff --git a/tests/Transport/JsonRpcPhpStreamTransportTest.php b/tests/Transport/JsonRpcPhpStreamTransportTest.php new file mode 100644 index 0000000..a9ebb24 --- /dev/null +++ b/tests/Transport/JsonRpcPhpStreamTransportTest.php @@ -0,0 +1,56 @@ +connection = $this->createMock(Connection::class); + $this->transport = new JsonRpcPhpStreamTransport($this->connection, TransportInterface::DEFAULT_TIMEOUT, ''); + } + + public function testRequest(): void + { + list($service, $method, $arguments) = ['foo', 'bar', [1, 2, 3]]; + $this->connection->expects($this->once())->method('getUrl')->willReturn(self::TEST_URL.self::SUCCESS_ENDPOINT); + + $result = $this->transport->request($service, $method, $arguments); + self::assertEquals(['success' => true], $result); + } + + public function testRequestDecodingError(): void + { + list($service, $method, $arguments) = ['foo', 'bar', [1, 2, 3]]; + $this->connection->expects($this->once())->method('getUrl')->willReturn(self::TEST_URL.self::JSON_ERROR_ENDPOINT); + + $this->expectException(TransportException::class); + $this->transport->request($service, $method, $arguments); + } + + public function testRequestRemoteError(): void + { + list($service, $method, $arguments) = ['foo', 'bar', [1, 2, 3]]; + $this->connection->expects($this->once())->method('getUrl')->willReturn(self::TEST_URL.self::REMOTE_ERROR_ENDPOINT); + + $this->expectException(RemoteException::class); + $this->transport->request($service, $method, $arguments); + } +} \ No newline at end of file From 2dd0b7ddf59c4cd9afdf6545c1cb2de78792cd79 Mon Sep 17 00:00:00 2001 From: Ang3 Date: Mon, 6 Nov 2023 15:49:19 +0000 Subject: [PATCH 54/80] Fix PHP code via php-cs-fixer (auto) [no ci] --- src/Exception/ExceptionInterface.php | 4 +--- src/Exception/MissingConfigParameterException.php | 4 +--- src/Exception/RequestException.php | 4 +--- src/Exception/TransportException.php | 4 +--- src/Metadata/Version.php | 3 +-- src/Transport/JsonRpcPhpStreamTransport.php | 3 +-- 6 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index ee50698..a3304e6 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -interface ExceptionInterface extends \Throwable -{ -} +interface ExceptionInterface extends \Throwable {} diff --git a/src/Exception/MissingConfigParameterException.php b/src/Exception/MissingConfigParameterException.php index 1ef3f23..fdb39a0 100644 --- a/src/Exception/MissingConfigParameterException.php +++ b/src/Exception/MissingConfigParameterException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface -{ -} +class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface {} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 204c7dc..50cf65a 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface -{ -} +class RequestException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 9e8bba3..057e514 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface -{ -} +class TransportException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 67d4ebe..728879b 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -21,8 +21,7 @@ public function __construct( private readonly string $buildIdentifier, private readonly string $buildVersion, private readonly int $protocolVersion - ) { - } + ) {} /** * Creates the instance from Odoo response payload. diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index fff23d0..71be8ef 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -29,8 +29,7 @@ class JsonRpcPhpStreamTransport implements TransportInterface public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) { - } + ) {} public function request(string $service, string $method, array $arguments = []): mixed { From 34a9448dbc99144e4ac20450624302245cd533e2 Mon Sep 17 00:00:00 2001 From: NEOSOLVA INFORMATIQUE Date: Mon, 6 Nov 2023 16:49:54 +0100 Subject: [PATCH 55/80] Update README.md --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 08d41c3..6ae7f6b 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,13 @@ Odoo version support Getting started =============== -Requirements +Installation ------------ PHP version 8.1 or newer to develop using the client. Other requirements, such as PHP extensions, are enforced by composer. See the `require` section of [composer.json file](../composer.json) for details. -Installation ------------- - Open a command console, enter your project directory and execute the following command to download the latest stable version of the client: From 07f2cfdebb8024803f954a26cf604c72a974064f Mon Sep 17 00:00:00 2001 From: NEOSOLVA INFORMATIQUE Date: Mon, 6 Nov 2023 16:54:53 +0100 Subject: [PATCH 56/80] Update README.md --- README.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6ae7f6b..6f58508 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,6 @@ Odoo version support | v12.0 | Yes | | | Older | Unknown | Needs feedbacks | -Getting started -=============== - Installation ------------ @@ -118,9 +115,24 @@ will not be created and the client will use yours instead. Your custom transport must implement the interface `Ang3\Component\Odoo\Transport\TransportInterface`. -### Database Abstraction Layer (DBAL) +More features +------------- -You need to manage your Odoo database models? +- Database Abstraction Layer (DBAL): You need to manage your Odoo database models? Please see the package [Odoo DBAL](https://github.com/ang3/php-odoo-dbal) to execute queries like Doctrine. -That's it! \ No newline at end of file +Tests +----- + +To run tests: + +```console +$ git clone git@github.com:Ang3/php-odoo-api-client.git +$ composer install +$ vendor/bin/simple-phpunit +``` + +License +------- + +This software is published under the [MIT License](./LICENCE). From d5e01f77dcb425dd101b4263ca20c21ff0d32cac Mon Sep 17 00:00:00 2001 From: NEOSOLVA INFORMATIQUE Date: Mon, 6 Nov 2023 16:55:55 +0100 Subject: [PATCH 57/80] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6f58508..c43ec6a 100644 --- a/README.md +++ b/README.md @@ -118,8 +118,8 @@ Your custom transport must implement the interface `Ang3\Component\Odoo\Transpor More features ------------- -- Database Abstraction Layer (DBAL): You need to manage your Odoo database models? -Please see the package [Odoo DBAL](https://github.com/ang3/php-odoo-dbal) to execute queries like Doctrine. +- [Odoo Database Abstraction Layer (DBAL)](https://github.com/ang3/php-odoo-dbal): Execute queries like Doctrine to +your Odoo database. Tests ----- From 7fd211fa82bb1c0d7e59e876c73c3332a270e8de Mon Sep 17 00:00:00 2001 From: NEOSOLVA INFORMATIQUE Date: Mon, 6 Nov 2023 16:56:44 +0100 Subject: [PATCH 58/80] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index c43ec6a..3ad97fe 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,6 @@ It follows [the official Odoo documentation](https://www.odoo.com/documentation/ > > - For older versions of PHP, please use the version 7.x. -Odoo version support --------------------- - | Odoo series | Compatibility | Comment | |-------------|---------------|-----------------| | v13.0+ | Unknown | Needs feedbacks | From 52c2f839b901d64945b644d8471628f2756c552e Mon Sep 17 00:00:00 2001 From: NEOSOLVA INFORMATIQUE Date: Mon, 6 Nov 2023 16:59:39 +0100 Subject: [PATCH 59/80] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3ad97fe..f701104 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,8 @@ Your custom transport must implement the interface `Ang3\Component\Odoo\Transpor More features ------------- +Here is the list of packages using the client to provide more features: + - [Odoo Database Abstraction Layer (DBAL)](https://github.com/ang3/php-odoo-dbal): Execute queries like Doctrine to your Odoo database. From f80545f344a90a7fa38ee99f9d96ef4bb98a942c Mon Sep 17 00:00:00 2001 From: NEOSOLVA INFORMATIQUE Date: Mon, 6 Nov 2023 17:21:59 +0100 Subject: [PATCH 60/80] Fix code in /tests --- src/Exception/ExceptionInterface.php | 4 +- .../MissingConfigParameterException.php | 4 +- src/Exception/RequestException.php | 4 +- src/Exception/TransportException.php | 4 +- src/Metadata/Version.php | 3 +- src/Transport/JsonRpcPhpStreamTransport.php | 3 +- tests/ClientTest.php | 337 ++++++++++-------- .../JsonRpcPhpStreamTransportTest.php | 34 +- 8 files changed, 225 insertions(+), 168 deletions(-) diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index a3304e6..ee50698 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -interface ExceptionInterface extends \Throwable {} +interface ExceptionInterface extends \Throwable +{ +} diff --git a/src/Exception/MissingConfigParameterException.php b/src/Exception/MissingConfigParameterException.php index fdb39a0..1ef3f23 100644 --- a/src/Exception/MissingConfigParameterException.php +++ b/src/Exception/MissingConfigParameterException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface {} +class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 50cf65a..204c7dc 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface {} +class RequestException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 057e514..9e8bba3 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface {} +class TransportException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 728879b..67d4ebe 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -21,7 +21,8 @@ public function __construct( private readonly string $buildIdentifier, private readonly string $buildVersion, private readonly int $protocolVersion - ) {} + ) { + } /** * Creates the instance from Odoo response payload. diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index 71be8ef..fff23d0 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -29,7 +29,8 @@ class JsonRpcPhpStreamTransport implements TransportInterface public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) {} + ) { + } public function request(string $service, string $method, array $arguments = []): mixed { diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 54f31a0..ab8c766 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -1,5 +1,14 @@ connection = $this->createMock(Connection::class); - $this->transport = $this->createMock(TransportInterface::class); - $this->logger = $this->createMock(LoggerInterface::class); - $this->client = new Client($this->connection, $this->transport, $this->logger); - } - - public function testConstruct(): void - { - $client = new Client($this->connection); - - // Asserting default transport + private Client $client; + private MockObject $connection; + private MockObject $transport; + private MockObject $logger; + + protected function setUp(): void + { + parent::setUp(); + $this->connection = $this->createMock(Connection::class); + $this->transport = $this->createMock(TransportInterface::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->client = new Client($this->connection, $this->transport, $this->logger); + } + + /** + * @covers ::__construct + */ + public function testConstruct(): void + { + $client = new Client($this->connection); + + // Asserting default transport $clientTransport = $client->getTransport(); - self::assertInstanceOf(JsonRpcPhpStreamTransport::class, $clientTransport); + static::assertInstanceOf(JsonRpcPhpStreamTransport::class, $clientTransport); - // Asserting optional logger + // Asserting optional logger $clientLogger = $client->getLogger(); - self::assertNull($clientLogger); - } - - public function testConstructWithCustomTransport(): void - { + static::assertNull($clientLogger); + } + + /** + * @covers ::__construct + */ + public function testConstructWithCustomTransport(): void + { $customTransport = $this->createMock(TransportInterface::class); - $client = new Client($this->connection, $customTransport); + $client = new Client($this->connection, $customTransport); - // Asserting custom transport + // Asserting custom transport $clientTransport = $client->getTransport(); - self::assertInstanceOf(TransportInterface::class, $clientTransport); - self::assertEquals($customTransport, $clientTransport); - - // Asserting optional logger - self::assertNull($client->getLogger()); - } - - public function testConstructWithLogger(): void - { + static::assertInstanceOf(TransportInterface::class, $clientTransport); + static::assertSame($customTransport, $clientTransport); + + // Asserting optional logger + static::assertNull($client->getLogger()); + } + + /** + * @covers ::__construct + */ + public function testConstructWithLogger(): void + { $logger = $this->createMock(LoggerInterface::class); - $client = new Client($this->connection, null, $logger); + $client = new Client($this->connection, null, $logger); // Asserting default transport $clientTransport = $client->getTransport(); - self::assertInstanceOf(JsonRpcPhpStreamTransport::class, $clientTransport); + static::assertInstanceOf(JsonRpcPhpStreamTransport::class, $clientTransport); - // Asserting logger + // Asserting logger $clientLogger = $client->getLogger(); - self::assertInstanceOf(LoggerInterface::class, $clientLogger); - self::assertEquals($logger, $clientLogger); - } - - public function provideRequestData(): array - { - return [ - [ OdooService::Common, OdooMethod::Login ], - [ OdooService::Common, OdooMethod::Version ], - [ OdooService::Object, OdooMethod::ExecuteKw ], - ]; - } - - /** - * @dataProvider provideRequestData - */ - public function testRequest(OdooService $service, OdooMethod $method): void - { - $this->transport - ->expects($this->once()) - ->method('request') - ->with($service->value, $method->value, [1, 2, 3]) - ->willReturn('foo') - ; - - $result = $this->client->request($service->value, $method->value, 1, 2, 3); - self::assertEquals('foo', $result); - } - - /** - * @dataProvider provideRequestData - */ - public function testRequestRemoteError(OdooService $service, OdooMethod $method): void - { - self::expectException(RemoteException::class); - $this->transport - ->expects($this->once()) - ->method('request') - ->with($service->value, $method->value, [1, 2, 3]) - ->willThrowException(RemoteException::create([ - 'error' => [ - 'code' => 123, - 'message' => 'Test error', - 'data' => [ - 'debug' => 'foo' - ] - ], - ])) - ; - - $this->client->request($service->value, $method->value, 1, 2, 3); - } - - /** - * @depends testRequest - */ - public function testExecuteKw(): void - { - list($database, $username, $password) = ['foo', 'bar', 'qux']; - $this->connection->expects($this->exactly(2))->method('getDatabase')->willReturn($database); - $this->connection->expects($this->once())->method('getUsername')->willReturn($username); - $this->connection->expects($this->exactly(2))->method('getPassword')->willReturn($password); + static::assertInstanceOf(LoggerInterface::class, $clientLogger); + static::assertSame($logger, $clientLogger); + } + + public static function provideRequestData(): array + { + return [ + [OdooService::Common, OdooMethod::Login], + [OdooService::Common, OdooMethod::Version], + [OdooService::Object, OdooMethod::ExecuteKw], + ]; + } + + /** + * @covers ::request + * + * @dataProvider provideRequestData + */ + public function testRequest(OdooService $service, OdooMethod $method): void + { + $this->transport + ->expects(static::once()) + ->method('request') + ->with($service->value, $method->value, [1, 2, 3]) + ->willReturn('foo') + ; + + $result = $this->client->request($service->value, $method->value, 1, 2, 3); + static::assertSame('foo', $result); + } + + /** + * @covers ::request + * + * @dataProvider provideRequestData + */ + public function testRequestRemoteError(OdooService $service, OdooMethod $method): void + { + self::expectException(RemoteException::class); + $this->transport + ->expects(static::once()) + ->method('request') + ->with($service->value, $method->value, [1, 2, 3]) + ->willThrowException(RemoteException::create([ + 'error' => [ + 'code' => 123, + 'message' => 'Test error', + 'data' => [ + 'debug' => 'foo', + ], + ], + ])) + ; + + $this->client->request($service->value, $method->value, 1, 2, 3); + } + + /** + * @covers ::executeKw + * + * @depends testRequest + */ + public function testExecuteKw(): void + { + [$database, $username, $password] = ['foo', 'bar', 'qux']; + $this->connection->expects(static::exactly(2))->method('getDatabase')->willReturn($database); + $this->connection->expects(static::once())->method('getUsername')->willReturn($username); + $this->connection->expects(static::exactly(2))->method('getPassword')->willReturn($password); $expectedUid = 1337; $expectedResult = 'foo'; @@ -141,15 +170,15 @@ public function testExecuteKw(): void $name = 'object_name', $method = 'object_method', $parameters = [1, 2, 3], - $options = [4, 5, 6] + $options = [4, 5, 6], ]]; $this->transport - ->expects($this->exactly(2)) + ->expects(static::exactly(2)) ->method('request') ->withConsecutive($authenticationArguments, $requestArguments) - ->willReturn($this->returnCallback(function ($service) use ($authenticationArguments, $expectedUid) { - return match($service) { + ->willReturn(static::returnCallback(function ($service) use ($expectedUid) { + return match ($service) { OdooService::Common->value => $expectedUid, default => 'foo' }; @@ -157,48 +186,52 @@ public function testExecuteKw(): void ; $result = $this->client->executeKw($name, $method, $parameters, $options); - self::assertEquals($expectedResult, $result); - } - - /** - * @depends testRequest - */ - public function testVersion(): void - { - $this->transport - ->expects($this->once()) - ->method('request') - ->with(OdooService::Common->value, OdooMethod::Version->value) - ->willReturn([ - 'server_version_info' => [13, 3, 7, 'a', 'b', 'c'], - 'protocol_version' => 1, - ]) - ; - - $version = $this->client->version(); - self::assertInstanceOf(Version::class, $version); - } - - /** - * @depends testRequest - */ - public function testAuthenticate(): void - { - list($database, $username, $password) = ['foo', 'bar', 'qux']; - $this->connection->expects($this->once())->method('getDatabase')->willReturn($database); - $this->connection->expects($this->once())->method('getUsername')->willReturn($username); - $this->connection->expects($this->once())->method('getPassword')->willReturn($password); - $expectedUid = 1337; - - $this->transport - ->expects($this->once()) - ->method('request') - ->with(OdooService::Common->value, OdooMethod::Login->value, [$database, $username, $password]) - ->willReturn($expectedUid) - ; - - $uid = $this->client->authenticate(); - self::assertEquals($expectedUid, $uid); - self::assertEquals($expectedUid, $this->client->getUid()); - } -} \ No newline at end of file + static::assertSame($expectedResult, $result); + } + + /** + * @covers ::version + * + * @depends testRequest + */ + public function testVersion(): void + { + $this->transport + ->expects(static::once()) + ->method('request') + ->with(OdooService::Common->value, OdooMethod::Version->value) + ->willReturn([ + 'server_version_info' => [13, 3, 7, 'a', 'b', 'c'], + 'protocol_version' => 1, + ]) + ; + + $version = $this->client->version(); + static::assertInstanceOf(Version::class, $version); + } + + /** + * @covers ::authenticate + * + * @depends testRequest + */ + public function testAuthenticate(): void + { + [$database, $username, $password] = ['foo', 'bar', 'qux']; + $this->connection->expects(static::once())->method('getDatabase')->willReturn($database); + $this->connection->expects(static::once())->method('getUsername')->willReturn($username); + $this->connection->expects(static::once())->method('getPassword')->willReturn($password); + $expectedUid = 1337; + + $this->transport + ->expects(static::once()) + ->method('request') + ->with(OdooService::Common->value, OdooMethod::Login->value, [$database, $username, $password]) + ->willReturn($expectedUid) + ; + + $uid = $this->client->authenticate(); + static::assertSame($expectedUid, $uid); + static::assertSame($expectedUid, $this->client->getUid()); + } +} diff --git a/tests/Transport/JsonRpcPhpStreamTransportTest.php b/tests/Transport/JsonRpcPhpStreamTransportTest.php index a9ebb24..8b43687 100644 --- a/tests/Transport/JsonRpcPhpStreamTransportTest.php +++ b/tests/Transport/JsonRpcPhpStreamTransportTest.php @@ -1,5 +1,14 @@ connection->expects($this->once())->method('getUrl')->willReturn(self::TEST_URL.self::SUCCESS_ENDPOINT); + [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; + $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::SUCCESS_ENDPOINT); $result = $this->transport->request($service, $method, $arguments); - self::assertEquals(['success' => true], $result); + static::assertSame(['success' => true], $result); } public function testRequestDecodingError(): void { - list($service, $method, $arguments) = ['foo', 'bar', [1, 2, 3]]; - $this->connection->expects($this->once())->method('getUrl')->willReturn(self::TEST_URL.self::JSON_ERROR_ENDPOINT); + [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; + $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::JSON_ERROR_ENDPOINT); $this->expectException(TransportException::class); $this->transport->request($service, $method, $arguments); @@ -47,10 +61,10 @@ public function testRequestDecodingError(): void public function testRequestRemoteError(): void { - list($service, $method, $arguments) = ['foo', 'bar', [1, 2, 3]]; - $this->connection->expects($this->once())->method('getUrl')->willReturn(self::TEST_URL.self::REMOTE_ERROR_ENDPOINT); + [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; + $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::REMOTE_ERROR_ENDPOINT); $this->expectException(RemoteException::class); $this->transport->request($service, $method, $arguments); } -} \ No newline at end of file +} From df8c8a1bf3dab8d5c152997951cc6c5bee26bf95 Mon Sep 17 00:00:00 2001 From: Ang3 Date: Mon, 6 Nov 2023 16:22:14 +0000 Subject: [PATCH 61/80] Fix PHP code via php-cs-fixer (auto) [no ci] --- src/Exception/ExceptionInterface.php | 4 +--- src/Exception/MissingConfigParameterException.php | 4 +--- src/Exception/RequestException.php | 4 +--- src/Exception/TransportException.php | 4 +--- src/Metadata/Version.php | 3 +-- src/Transport/JsonRpcPhpStreamTransport.php | 3 +-- 6 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index ee50698..a3304e6 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -interface ExceptionInterface extends \Throwable -{ -} +interface ExceptionInterface extends \Throwable {} diff --git a/src/Exception/MissingConfigParameterException.php b/src/Exception/MissingConfigParameterException.php index 1ef3f23..fdb39a0 100644 --- a/src/Exception/MissingConfigParameterException.php +++ b/src/Exception/MissingConfigParameterException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface -{ -} +class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface {} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 204c7dc..50cf65a 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface -{ -} +class RequestException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 9e8bba3..057e514 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface -{ -} +class TransportException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 67d4ebe..728879b 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -21,8 +21,7 @@ public function __construct( private readonly string $buildIdentifier, private readonly string $buildVersion, private readonly int $protocolVersion - ) { - } + ) {} /** * Creates the instance from Odoo response payload. diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index fff23d0..71be8ef 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -29,8 +29,7 @@ class JsonRpcPhpStreamTransport implements TransportInterface public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) { - } + ) {} public function request(string $service, string $method, array $arguments = []): mixed { From 2d059c61e97456ae61ad63c5aaea83606a709b38 Mon Sep 17 00:00:00 2001 From: NEOSOLVA INFORMATIQUE Date: Mon, 6 Nov 2023 17:24:03 +0100 Subject: [PATCH 62/80] Update JsonRpcPhpStreamTransportTest.php --- tests/Transport/JsonRpcPhpStreamTransportTest.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/Transport/JsonRpcPhpStreamTransportTest.php b/tests/Transport/JsonRpcPhpStreamTransportTest.php index 8b43687..61fba7a 100644 --- a/tests/Transport/JsonRpcPhpStreamTransportTest.php +++ b/tests/Transport/JsonRpcPhpStreamTransportTest.php @@ -20,9 +20,9 @@ use PHPUnit\Framework\TestCase; /** - * @internal + * @coversDefaultClass \Ang3\Component\Odoo\Transport\JsonRpcPhpStreamTransport * - * @coversNothing + * @internal */ final class JsonRpcPhpStreamTransportTest extends TestCase { @@ -41,15 +41,21 @@ protected function setUp(): void $this->transport = new JsonRpcPhpStreamTransport($this->connection, TransportInterface::DEFAULT_TIMEOUT, ''); } + /** + * @covers ::request + */ public function testRequest(): void { [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::SUCCESS_ENDPOINT); $result = $this->transport->request($service, $method, $arguments); - static::assertSame(['success' => true], $result); + static::assertSame(['success' => 'true'], $result); } + /** + * @covers ::request + */ public function testRequestDecodingError(): void { [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; @@ -59,6 +65,9 @@ public function testRequestDecodingError(): void $this->transport->request($service, $method, $arguments); } + /** + * @covers ::request + */ public function testRequestRemoteError(): void { [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; From eaadf34156cfddfa50c91be57c8525cd14979c6b Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Mon, 6 Nov 2023 22:45:32 +0100 Subject: [PATCH 63/80] Renamed Odoo RPC enums --- src/Client.php | 14 +++++------ .../{OdooMethod.php => OdooRpcMethod.php} | 2 +- .../{OdooService.php => OdooRpcService.php} | 2 +- src/Exception/ExceptionInterface.php | 4 +++- .../MissingConfigParameterException.php | 4 +++- src/Exception/RequestException.php | 4 +++- src/Exception/TransportException.php | 4 +++- src/Metadata/Version.php | 3 ++- src/Transport/JsonRpcPhpStreamTransport.php | 3 ++- tests/ClientTest.php | 24 +++++++++---------- 10 files changed, 37 insertions(+), 27 deletions(-) rename src/Enum/{OdooMethod.php => OdooRpcMethod.php} (92%) rename src/Enum/{OdooService.php => OdooRpcService.php} (91%) diff --git a/src/Client.php b/src/Client.php index d967c92..3ecce17 100644 --- a/src/Client.php +++ b/src/Client.php @@ -11,8 +11,8 @@ namespace Ang3\Component\Odoo; -use Ang3\Component\Odoo\Enum\OdooMethod; -use Ang3\Component\Odoo\Enum\OdooService; +use Ang3\Component\Odoo\Enum\OdooRpcMethod; +use Ang3\Component\Odoo\Enum\OdooRpcService; use Ang3\Component\Odoo\Exception\AuthenticationException; use Ang3\Component\Odoo\Exception\MissingConfigParameterException; use Ang3\Component\Odoo\Exception\RequestException; @@ -57,8 +57,8 @@ public static function create( public function executeKw(string $name, string $method, array $parameters = [], array $options = []): mixed { return $this->request( - OdooService::Object->value, - OdooMethod::ExecuteKw->value, + OdooRpcService::Object->value, + OdooRpcMethod::ExecuteKw->value, $this->connection->getDatabase(), $this->authenticate(), $this->connection->getPassword(), @@ -71,7 +71,7 @@ public function executeKw(string $name, string $method, array $parameters = [], public function version(): Version { - return Version::create((array) $this->request(OdooService::Common->value, OdooMethod::Version->value)); + return Version::create((array) $this->request(OdooRpcService::Common->value, OdooRpcMethod::Version->value)); } /** @@ -81,8 +81,8 @@ public function authenticate(): int { if (null === $this->uid) { $this->uid = (int) $this->request( - OdooService::Common->value, - OdooMethod::Login->value, + OdooRpcService::Common->value, + OdooRpcMethod::Login->value, $this->connection->getDatabase(), $this->connection->getUsername(), $this->connection->getPassword() diff --git a/src/Enum/OdooMethod.php b/src/Enum/OdooRpcMethod.php similarity index 92% rename from src/Enum/OdooMethod.php rename to src/Enum/OdooRpcMethod.php index ae5289a..174135a 100644 --- a/src/Enum/OdooMethod.php +++ b/src/Enum/OdooRpcMethod.php @@ -11,7 +11,7 @@ namespace Ang3\Component\Odoo\Enum; -enum OdooMethod: string +enum OdooRpcMethod: string { case Login = 'login'; case ExecuteKw = 'execute_kw'; diff --git a/src/Enum/OdooService.php b/src/Enum/OdooRpcService.php similarity index 91% rename from src/Enum/OdooService.php rename to src/Enum/OdooRpcService.php index ead91a9..b8069b9 100644 --- a/src/Enum/OdooService.php +++ b/src/Enum/OdooRpcService.php @@ -11,7 +11,7 @@ namespace Ang3\Component\Odoo\Enum; -enum OdooService: string +enum OdooRpcService: string { case Common = 'common'; case Object = 'object'; diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index a3304e6..ee50698 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -interface ExceptionInterface extends \Throwable {} +interface ExceptionInterface extends \Throwable +{ +} diff --git a/src/Exception/MissingConfigParameterException.php b/src/Exception/MissingConfigParameterException.php index fdb39a0..1ef3f23 100644 --- a/src/Exception/MissingConfigParameterException.php +++ b/src/Exception/MissingConfigParameterException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface {} +class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 50cf65a..204c7dc 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface {} +class RequestException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 057e514..9e8bba3 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface {} +class TransportException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 728879b..67d4ebe 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -21,7 +21,8 @@ public function __construct( private readonly string $buildIdentifier, private readonly string $buildVersion, private readonly int $protocolVersion - ) {} + ) { + } /** * Creates the instance from Odoo response payload. diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index 71be8ef..fff23d0 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -29,7 +29,8 @@ class JsonRpcPhpStreamTransport implements TransportInterface public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) {} + ) { + } public function request(string $service, string $method, array $arguments = []): mixed { diff --git a/tests/ClientTest.php b/tests/ClientTest.php index ab8c766..8abdd6d 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -13,8 +13,8 @@ use Ang3\Component\Odoo\Client; use Ang3\Component\Odoo\Connection; -use Ang3\Component\Odoo\Enum\OdooMethod; -use Ang3\Component\Odoo\Enum\OdooService; +use Ang3\Component\Odoo\Enum\OdooRpcMethod; +use Ang3\Component\Odoo\Enum\OdooRpcService; use Ang3\Component\Odoo\Exception\RemoteException; use Ang3\Component\Odoo\Metadata\Version; use Ang3\Component\Odoo\Transport\JsonRpcPhpStreamTransport; @@ -98,9 +98,9 @@ public function testConstructWithLogger(): void public static function provideRequestData(): array { return [ - [OdooService::Common, OdooMethod::Login], - [OdooService::Common, OdooMethod::Version], - [OdooService::Object, OdooMethod::ExecuteKw], + [OdooRpcService::Common, OdooRpcMethod::Login], + [OdooRpcService::Common, OdooRpcMethod::Version], + [OdooRpcService::Object, OdooRpcMethod::ExecuteKw], ]; } @@ -109,7 +109,7 @@ public static function provideRequestData(): array * * @dataProvider provideRequestData */ - public function testRequest(OdooService $service, OdooMethod $method): void + public function testRequest(OdooRpcService $service, OdooRpcMethod $method): void { $this->transport ->expects(static::once()) @@ -127,7 +127,7 @@ public function testRequest(OdooService $service, OdooMethod $method): void * * @dataProvider provideRequestData */ - public function testRequestRemoteError(OdooService $service, OdooMethod $method): void + public function testRequestRemoteError(OdooRpcService $service, OdooRpcMethod $method): void { self::expectException(RemoteException::class); $this->transport @@ -162,8 +162,8 @@ public function testExecuteKw(): void $expectedUid = 1337; $expectedResult = 'foo'; - $authenticationArguments = [OdooService::Common->value, OdooMethod::Login->value, [$database, $username, $password]]; - $requestArguments = [OdooService::Object->value, OdooMethod::ExecuteKw->value, [ + $authenticationArguments = [OdooRpcService::Common->value, OdooRpcMethod::Login->value, [$database, $username, $password]]; + $requestArguments = [OdooRpcService::Object->value, OdooRpcMethod::ExecuteKw->value, [ $database, $expectedUid, $password, @@ -179,7 +179,7 @@ public function testExecuteKw(): void ->withConsecutive($authenticationArguments, $requestArguments) ->willReturn(static::returnCallback(function ($service) use ($expectedUid) { return match ($service) { - OdooService::Common->value => $expectedUid, + OdooRpcService::Common->value => $expectedUid, default => 'foo' }; })) @@ -199,7 +199,7 @@ public function testVersion(): void $this->transport ->expects(static::once()) ->method('request') - ->with(OdooService::Common->value, OdooMethod::Version->value) + ->with(OdooRpcService::Common->value, OdooRpcMethod::Version->value) ->willReturn([ 'server_version_info' => [13, 3, 7, 'a', 'b', 'c'], 'protocol_version' => 1, @@ -226,7 +226,7 @@ public function testAuthenticate(): void $this->transport ->expects(static::once()) ->method('request') - ->with(OdooService::Common->value, OdooMethod::Login->value, [$database, $username, $password]) + ->with(OdooRpcService::Common->value, OdooRpcMethod::Login->value, [$database, $username, $password]) ->willReturn($expectedUid) ; From c336ed1add6be823ed9a053b4193dfbf70902a8d Mon Sep 17 00:00:00 2001 From: Ang3 Date: Mon, 6 Nov 2023 21:45:48 +0000 Subject: [PATCH 64/80] Fix PHP code via php-cs-fixer (auto) [no ci] --- src/Exception/ExceptionInterface.php | 4 +--- src/Exception/MissingConfigParameterException.php | 4 +--- src/Exception/RequestException.php | 4 +--- src/Exception/TransportException.php | 4 +--- src/Metadata/Version.php | 3 +-- src/Transport/JsonRpcPhpStreamTransport.php | 3 +-- 6 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index ee50698..a3304e6 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -interface ExceptionInterface extends \Throwable -{ -} +interface ExceptionInterface extends \Throwable {} diff --git a/src/Exception/MissingConfigParameterException.php b/src/Exception/MissingConfigParameterException.php index 1ef3f23..fdb39a0 100644 --- a/src/Exception/MissingConfigParameterException.php +++ b/src/Exception/MissingConfigParameterException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface -{ -} +class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface {} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 204c7dc..50cf65a 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface -{ -} +class RequestException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 9e8bba3..057e514 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface -{ -} +class TransportException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 67d4ebe..728879b 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -21,8 +21,7 @@ public function __construct( private readonly string $buildIdentifier, private readonly string $buildVersion, private readonly int $protocolVersion - ) { - } + ) {} /** * Creates the instance from Odoo response payload. diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index fff23d0..71be8ef 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -29,8 +29,7 @@ class JsonRpcPhpStreamTransport implements TransportInterface public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) { - } + ) {} public function request(string $service, string $method, array $arguments = []): mixed { From 7f0900a4ac0d837fe07d3312e050c9c500b64f31 Mon Sep 17 00:00:00 2001 From: NEOSOLVA INFORMATIQUE Date: Tue, 7 Nov 2023 09:27:04 +0100 Subject: [PATCH 65/80] Update docs --- README.md | 3 +- docs/custom_transport.md | 0 docs/index.md | 60 ---------------------------------------- 3 files changed, 2 insertions(+), 61 deletions(-) delete mode 100644 docs/custom_transport.md delete mode 100644 docs/index.md diff --git a/README.md b/README.md index f701104..d18a0db 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ PHP Odoo API client This package is a PHP library to connect and interact with an Odoo instance via JSON-RPC by default. It follows [the official Odoo documentation](https://www.odoo.com/documentation/13.0/developer/misc/api/odoo.html). -> From v8.x, this package is dedicated to the client only and PHP 8.1+. +> From v8.x, this package is dedicated to the client only and PHP 8.1+. +> The PHP extension `xmlrpc` is not required anymore. > > - For DBAL features, please report to the package [PHP Odoo DBAL](https://github.com/ang3/php-odoo-dbal). > diff --git a/docs/custom_transport.md b/docs/custom_transport.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index b337809..0000000 --- a/docs/index.md +++ /dev/null @@ -1,60 +0,0 @@ -PHP Odoo API client -=================== - -This package is a PHP library to connect and interact with an Odoo instance via JSON-RPC by default. -It follows [the official Odoo documentation](https://www.odoo.com/documentation/13.0/developer/misc/api/odoo.html). - -Getting started -=============== - -Requirements ------------- - -PHP version 8.1 or newer to develop using the client. Other requirements, such as PHP extensions, are enforced by -composer. See the `require` section of [composer.json file](../composer.json) -for details. - -Odoo database support ---------------------- - -| Odoo series | Compatibility | Comment | -|-------------|---------------|--------------------------------------------------------------------| -| Newer | Unknown | Needs feedbacks | -| v13.0 | Yes | Some Odoo model names changed (e.g account.invoice > account.move) | -| v12.0 | Yes | First tested version | -| Older | Unknown | Needs feedbacks | - -Installation ------------- - -Open a command console, enter your project directory and execute the -following command to download the latest stable version of the client: - -```console -$ composer require ang3/php-odoo-api-client -``` - -This command requires you to have Composer installed globally, as explained -in the [installation chapter](https://getcomposer.org/doc/00-intro.md) -of the Composer documentation. - -Basic usage ------------ - -First, import the client with a `use` statement and create a client instance statically -with your config: - -```php - '', - 'database' => '', - 'username' => '', - 'password' => '', -], $logger = null); -``` \ No newline at end of file From 98f1c4a4f64467ec91272cd0c25d71669368f93f Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Tue, 21 Nov 2023 10:09:57 +0100 Subject: [PATCH 66/80] Implemented connection DSN --- README.md | 26 +- composer.json | 3 +- composer.lock | 565 +++++++----------- phpunit.xml.dist | 37 +- src/Client.php | 17 +- src/Connection.php | 101 +++- src/Exception/ConnectionException.php | 23 + src/Exception/ExceptionInterface.php | 4 +- .../MissingConfigParameterException.php | 17 - src/Exception/RequestException.php | 4 +- src/Exception/TransportException.php | 4 +- src/Metadata/Version.php | 3 +- src/Transport/JsonRpcPhpStreamTransport.php | 5 +- tests/ConnectionTest.php | 130 ++++ tests/FakerTrait.php | 29 + .../JsonRpcPhpStreamTransportTest.php | 6 +- 16 files changed, 551 insertions(+), 423 deletions(-) create mode 100644 src/Exception/ConnectionException.php delete mode 100644 src/Exception/MissingConfigParameterException.php create mode 100644 tests/ConnectionTest.php create mode 100644 tests/FakerTrait.php diff --git a/README.md b/README.md index d18a0db..987505a 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,30 @@ Basic usage ### Create a client +You can create a client via DSN or array configuration. + +#### By DSN + First, create a client instance statically with your config: ```php -:@/`; +$client = Client::create($dsn, $transport = null, $logger = null); +``` + +If your password contains special characters, encode it with the native function `urlencode()`: + +```php +$myEncodedPassword = urlencode('high_password'); +``` -require_once 'vendor/autoload.php'; +#### Array configuration +To create a client from an array configuration: + +```php use Ang3\Component\Odoo\Client; $client = Client::create([ @@ -64,9 +81,10 @@ $client = Client::create([ ], $transport = null, $logger = null); ``` +#### Error handling + Exceptions: -- ```Ang3\Component\Odoo\Exception\MissingConfigParameterException``` when a required parameter is missing -from the static method ```create()```. +- ```Ang3\Component\Odoo\Exception\ConnectionException``` on connection errors. ### Make a request diff --git a/composer.json b/composer.json index 4bd53e5..010b150 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ }, "require-dev": { "symfony/test-pack": "^1.0", - "symfony/var-dumper": "^6.3" + "symfony/var-dumper": "^6.3", + "fakerphp/faker": "^1.23" } } diff --git a/composer.lock b/composer.lock index 7836196..90721c3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "545eda0d995615c70edd2c291bf3f04c", + "content-hash": "292357a4a55eb972941159d021f33f33", "packages": [ { "name": "psr/log", @@ -18,13 +18,7 @@ "type": "zip", "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=8.0.0" @@ -40,7 +34,7 @@ "Psr\\Log\\": "src" } }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -76,13 +70,7 @@ "type": "zip", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": "^8.1" @@ -103,7 +91,7 @@ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -140,6 +128,74 @@ ], "time": "2022-12-30T00:23:10+00:00" }, + { + "name": "fakerphp/faker", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e3daa170d00fde61ea7719ef47bb09bb8f1d9b01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e3daa170d00fde61ea7719ef47bb09bb8f1d9b01", + "reference": "e3daa170d00fde61ea7719ef47bb09bb8f1d9b01", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "v1.21-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.23.0" + }, + "time": "2023-06-12T08:44:38+00:00" + }, { "name": "masterminds/html5", "version": "2.8.1", @@ -152,13 +208,7 @@ "type": "zip", "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "ext-dom": "*", @@ -178,7 +228,7 @@ "Masterminds\\": "src" } }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -225,13 +275,7 @@ "type": "zip", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": "^7.1 || ^8.0" @@ -254,7 +298,7 @@ "DeepCopy\\": "src/DeepCopy/" } }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -290,13 +334,7 @@ "type": "zip", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "ext-tokenizer": "*", @@ -320,7 +358,7 @@ "PhpParser\\": "lib/PhpParser" } }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -352,13 +390,7 @@ "type": "zip", "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "ext-dom": "*", @@ -378,7 +410,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -418,13 +450,7 @@ "type": "zip", "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": "^7.2 || ^8.0" @@ -435,7 +461,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -475,13 +501,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "ext-dom": "*", @@ -516,7 +536,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -559,13 +579,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -584,7 +598,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -625,13 +639,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -654,7 +662,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -694,13 +702,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -719,7 +721,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -759,13 +761,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -784,7 +780,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -824,13 +820,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f3d767f7f9e191eab4189abe41ab37797e30b1be", "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "doctrine/instantiator": "^1.3.1 || ^2", @@ -882,7 +872,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -921,6 +911,59 @@ ], "time": "2023-09-19T05:39:22+00:00" }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, { "name": "sebastian/cli-parser", "version": "1.0.1", @@ -933,13 +976,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -958,7 +995,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -995,13 +1032,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -1020,7 +1051,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1057,13 +1088,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -1082,7 +1107,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1118,13 +1143,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3", @@ -1145,7 +1164,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1198,13 +1217,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "nikic/php-parser": "^4.7", @@ -1224,7 +1237,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1261,13 +1274,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -1287,7 +1294,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1333,13 +1340,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -1361,7 +1362,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1402,13 +1403,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3", @@ -1429,7 +1424,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1485,13 +1480,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", "reference": "bde739e7565280bda77be70044ac1047bc007e34", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3", @@ -1516,7 +1505,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1555,13 +1544,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "nikic/php-parser": "^4.6", @@ -1581,7 +1564,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1618,13 +1601,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3", @@ -1645,7 +1622,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1681,13 +1658,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -1706,7 +1677,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1742,13 +1713,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -1767,7 +1732,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1811,13 +1776,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -1836,7 +1795,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1872,13 +1831,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -1897,7 +1850,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1934,13 +1887,7 @@ "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.3" @@ -1956,7 +1903,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1983,23 +1930,17 @@ }, { "name": "symfony/browser-kit", - "version": "v6.3.2", + "version": "v6.3.8", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "ca4a988488f61ac18f8f845445eabdd36f89aa8d" + "reference": "e270297dbee59168274c2b535ab1bccd593e6ffe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ca4a988488f61ac18f8f845445eabdd36f89aa8d", - "reference": "ca4a988488f61ac18f8f845445eabdd36f89aa8d", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/e270297dbee59168274c2b535ab1bccd593e6ffe", + "reference": "e270297dbee59168274c2b535ab1bccd593e6ffe", + "shasum": "" }, "require": { "php": ">=8.1", @@ -2020,7 +1961,7 @@ "/Tests/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2037,7 +1978,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v6.3.2" + "source": "https://github.com/symfony/browser-kit/tree/v6.3.8" }, "funding": [ { @@ -2053,7 +1994,7 @@ "type": "tidelift" } ], - "time": "2023-07-06T06:56:43+00:00" + "time": "2023-10-31T08:07:48+00:00" }, { "name": "symfony/css-selector", @@ -2067,13 +2008,7 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57", "reference": "883d961421ab1709877c10ac99451632a3d6fa57", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=8.1" @@ -2087,7 +2022,7 @@ "/Tests/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2128,7 +2063,7 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -2138,13 +2073,7 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=8.1" @@ -2164,7 +2093,7 @@ "function.php" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2181,7 +2110,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" }, "funding": [ { @@ -2211,13 +2140,7 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/3fdd2a3d5fdc363b2e8dbf817f9726a4d013cbd1", "reference": "3fdd2a3d5fdc363b2e8dbf817f9726a4d013cbd1", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "masterminds/html5": "^2.6", @@ -2237,7 +2160,7 @@ "/Tests/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2274,23 +2197,17 @@ }, { "name": "symfony/phpunit-bridge", - "version": "v6.3.6", + "version": "v6.3.8", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "c6f1df6a76c2c12bd14a0a5bf7c556dd935efe1d" + "reference": "45610900872a35b77db7698651f36129906041ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/c6f1df6a76c2c12bd14a0a5bf7c556dd935efe1d", - "reference": "c6f1df6a76c2c12bd14a0a5bf7c556dd935efe1d", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/45610900872a35b77db7698651f36129906041ea", + "reference": "45610900872a35b77db7698651f36129906041ea", + "shasum": "" }, "require": { "php": ">=7.1.3" @@ -2324,7 +2241,7 @@ "/Tests/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2341,7 +2258,7 @@ "description": "Provides utilities for PHPUnit, especially user deprecation notices management", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v6.3.6" + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.3.8" }, "funding": [ { @@ -2357,7 +2274,7 @@ "type": "tidelift" } ], - "time": "2023-10-12T15:02:41+00:00" + "time": "2023-10-31T08:07:48+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2371,13 +2288,7 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.1" @@ -2406,7 +2317,7 @@ "Symfony\\Polyfill\\Ctype\\": "" } }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2459,13 +2370,7 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", "reference": "42292d99c55abe617799667f454222c54c60e229", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.1" @@ -2494,7 +2399,7 @@ "Symfony\\Polyfill\\Mbstring\\": "" } }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2548,13 +2453,7 @@ "type": "zip", "url": "https://api.github.com/repos/symfony/test-pack/zipball/7b708c588cb3e1c8f0281889f273b920cbddff9b", "reference": "7b708c588cb3e1c8f0281889f273b920cbddff9b", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "phpunit/phpunit": "^9.5", @@ -2569,7 +2468,7 @@ "symfony/phpunit-bridge": "*" }, "type": "symfony-pack", - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2596,23 +2495,17 @@ }, { "name": "symfony/var-dumper", - "version": "v6.3.6", + "version": "v6.3.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "999ede244507c32b8e43aebaa10e9fce20de7c97" + "reference": "81acabba9046550e89634876ca64bfcd3c06aa0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/999ede244507c32b8e43aebaa10e9fce20de7c97", - "reference": "999ede244507c32b8e43aebaa10e9fce20de7c97", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/81acabba9046550e89634876ca64bfcd3c06aa0a", + "reference": "81acabba9046550e89634876ca64bfcd3c06aa0a", + "shasum": "" }, "require": { "php": ">=8.1", @@ -2645,7 +2538,7 @@ "/Tests/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2666,7 +2559,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.3.6" + "source": "https://github.com/symfony/var-dumper/tree/v6.3.8" }, "funding": [ { @@ -2682,27 +2575,21 @@ "type": "tidelift" } ], - "time": "2023-10-12T18:45:56+00:00" + "time": "2023-11-08T10:42:36+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "", - "mirrors": [ - { - "url": "https://repo.packagist.com/neosolva/dists/%package%/%version%/r%reference%.%type%", - "preferred": true - } - ] + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "shasum": "" }, "require": { "ext-dom": "*", @@ -2716,7 +2603,7 @@ "src/" ] }, - "notification-url": "https://repo.packagist.com/neosolva/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -2730,7 +2617,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" }, "funding": [ { @@ -2738,7 +2625,7 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2023-11-20T00:12:19+00:00" } ], "aliases": [], diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 94d8b09..421dd9e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,23 +1,32 @@ + - - - - ./src - ./vendor - - - ./tests - ./vendor - - + - - + + + - + tests + + + + ./src + ./vendor + + ./tests + ./vendor + + + diff --git a/src/Client.php b/src/Client.php index 3ecce17..7952ab8 100644 --- a/src/Client.php +++ b/src/Client.php @@ -14,7 +14,7 @@ use Ang3\Component\Odoo\Enum\OdooRpcMethod; use Ang3\Component\Odoo\Enum\OdooRpcService; use Ang3\Component\Odoo\Exception\AuthenticationException; -use Ang3\Component\Odoo\Exception\MissingConfigParameterException; +use Ang3\Component\Odoo\Exception\ConnectionException; use Ang3\Component\Odoo\Exception\RequestException; use Ang3\Component\Odoo\Exception\TransportException; use Ang3\Component\Odoo\Metadata\Version; @@ -39,19 +39,16 @@ public function __construct( } /** - * Create a new client instance from array configuration. - * The configuration array must have keys "url", "database", "username" and "password". + * Create a new client instance from a DSN. + * DSN format: odoo://:@/. * * @static * - * @throws MissingConfigParameterException when a required parameter is missing + * @throws ConnectionException on invalid DSN */ - public static function create( - array $config, - TransportInterface $transport = null, - LoggerInterface $logger = null - ): self { - return new self(Connection::create($config), $transport, $logger); + public static function create(string $dsn, TransportInterface $transport = null, LoggerInterface $logger = null): self + { + return new self(Connection::parseDsn($dsn), $transport, $logger); } public function executeKw(string $name, string $method, array $parameters = [], array $options = []): mixed diff --git a/src/Connection.php b/src/Connection.php index b57f660..4a8cb76 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -11,24 +11,21 @@ namespace Ang3\Component\Odoo; -use Ang3\Component\Odoo\Exception\MissingConfigParameterException; +use Ang3\Component\Odoo\Exception\ConnectionException; -/** - * @author Joanis ROUANET - */ class Connection { - private string $url; - private string $database; - private string $username; - private string $password; + public function __construct( + private readonly string $host, + private readonly string $username, + private readonly string $password, + private readonly string $database + ) { + } - public function __construct(string $url, string $database, string $username, string $password) + public function __toString(): string { - $this->url = $url; - $this->database = $database; - $this->username = $username; - $this->password = $password; + return sprintf('odoo://%s:%s@%s/%s', $this->username, urlencode($this->password), $this->host, $this->database); } public static function create(array $config): self @@ -37,28 +34,80 @@ public static function create(array $config): self $value = $config[$paramName] ?? null; if (null === $value) { - throw new MissingConfigParameterException(sprintf('Missing config parameter name "%s".', $paramName)); + throw new ConnectionException(sprintf('Missing configuration parameter "%s".', $paramName)); } return $value; }; return new self( - $getParam($config, 'url'), - $getParam($config, 'database'), + $getParam($config, 'host'), $getParam($config, 'username'), - $getParam($config, 'password') + $getParam($config, 'password'), + $getParam($config, 'database') ); } - public function getUrl(): string + /** + * @throws ConnectionException on invalid DSN + */ + public static function parseDsn(string $dsn): self { - return $this->url; + /** @var array|false $parsedUrl */ + $parsedUrl = parse_url($dsn); + + if (!\is_array($parsedUrl) && $parsedUrl) { + throw ConnectionException::invalidDsn($dsn); + } + + [$scheme, $host, $user, $password, $path] = [ + $parsedUrl['scheme'] ?? null, + $parsedUrl['host'] ?? null, + $parsedUrl['user'] ?? null, + $parsedUrl['pass'] ?? null, + $parsedUrl['path'] ?? null, + ]; + + if (!$scheme) { + throw ConnectionException::invalidDsn($dsn, 'Missing scheme.'); + } + + if ('odoo' !== $scheme) { + throw ConnectionException::invalidDsn($dsn, sprintf('The scheme "%s" is not supported (expecting "odoo").', $scheme)); + } + + if (!$host) { + throw ConnectionException::invalidDsn($dsn, 'Missing host.'); + } + + if (!$user) { + throw ConnectionException::invalidDsn($dsn, 'Missing username.'); + } + + if (!$password) { + throw ConnectionException::invalidDsn($dsn, 'Missing user password.'); + } + + if (!$path) { + throw ConnectionException::invalidDsn($dsn, 'Missing path.'); + } + + $database = str_starts_with($path, '/') ? substr($path, 1) : $path; + + return new self($host, $user, urldecode($password), $database); } - public function getDatabase(): string + /** + * Gets the unique name of this connection. + */ + public function getIdentifier(): string { - return $this->database; + return sha1(sprintf('%s.%s.%s', $this->host, $this->database, $this->username)); + } + + public function getHost(): string + { + return $this->host; } public function getUsername(): string @@ -71,14 +120,8 @@ public function getPassword(): string return $this->password; } - /** - * Gets the unique name of this connection. - */ - public function getIdentifier(): string + public function getDatabase(): string { - $database = preg_replace('([^a-zA-Z0-9_])', '_', $this->database); - $user = preg_replace('([^a-zA-Z0-9_])', '_', $this->username); - - return sprintf('%s.%s.%s', sha1($this->url), $database, $user); + return $this->database; } } diff --git a/src/Exception/ConnectionException.php b/src/Exception/ConnectionException.php new file mode 100644 index 0000000..3f7e10c --- /dev/null +++ b/src/Exception/ConnectionException.php @@ -0,0 +1,23 @@ + + */ +class ConnectionException extends \InvalidArgumentException implements ExceptionInterface +{ + public static function invalidDsn(string $dsn, ?string $message = null, ?\Throwable $previous = null): self + { + return new self(sprintf('The DSN "%s" is not valid - %s', $dsn, $message ?: 'Unknown error'), 0, $previous); + } +} diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index a3304e6..ee50698 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -interface ExceptionInterface extends \Throwable {} +interface ExceptionInterface extends \Throwable +{ +} diff --git a/src/Exception/MissingConfigParameterException.php b/src/Exception/MissingConfigParameterException.php deleted file mode 100644 index fdb39a0..0000000 --- a/src/Exception/MissingConfigParameterException.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ -class MissingConfigParameterException extends \InvalidArgumentException implements ExceptionInterface {} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 50cf65a..204c7dc 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface {} +class RequestException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 057e514..9e8bba3 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface {} +class TransportException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 728879b..67d4ebe 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -21,7 +21,8 @@ public function __construct( private readonly string $buildIdentifier, private readonly string $buildVersion, private readonly int $protocolVersion - ) {} + ) { + } /** * Creates the instance from Odoo response payload. diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index 71be8ef..ed3b1a1 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -29,7 +29,8 @@ class JsonRpcPhpStreamTransport implements TransportInterface public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) {} + ) { + } public function request(string $service, string $method, array $arguments = []): mixed { @@ -57,7 +58,7 @@ public function request(string $service, string $method, array $arguments = []): ], ]); - $endpointUrl = $this->connection->getUrl().self::DEFAULT_ENDPOINT; + $endpointUrl = $this->connection->getHost().self::DEFAULT_ENDPOINT; $request = file_get_contents($endpointUrl, false, $context); if (false === $request) { diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php new file mode 100644 index 0000000..36e27b5 --- /dev/null +++ b/tests/ConnectionTest.php @@ -0,0 +1,130 @@ +host = 'my-company.odoo.com'; + $this->username = 'my-account@my-domain.com'; + $this->password = self::faker()->password(8); + $this->database = 'my-database-3548373'; + $this->connection = new Connection($this->host, $this->username, $this->password, $this->database); + $this->dsn = sprintf('odoo://%s:%s@%s/%s', $this->username, urlencode($this->password), $this->host, $this->database); + } + + /** + * @covers ::__toString + */ + public function testToString(): void + { + static::assertSame($this->dsn, (string) $this->connection); + } + + /** + * @covers ::create + * + * @depends testGetters + */ + public function testCreate(): void + { + $connection = Connection::create([ + 'host' => $this->host, + 'username' => $this->username, + 'password' => $this->password, + 'database' => $this->database, + ]); + + $this->testGetters($connection); + } + + /** + * @covers ::create + * + * @depends testGetters + * + * @testWith [null, "user", "pass", "database"] + * ["host", null, "pass", "database"] + * ["host", "user", null, "database"] + * ["host", "user", "pass", null] + */ + public function testCreateWithMissingParameters( + ?string $host = null, + ?string $username = null, + ?string $password = null, + ?string $database = null + ): void + { + $this->expectException(ConnectionException::class); + + Connection::create([ + 'host' => $host, + 'username' => $username, + 'password' => $password, + 'database' => $database, + ]); + } + + /** + * @covers ::parseDsn + * + * @depends testGetters + */ + public function testParseDsn(): void + { + $connection = Connection::parseDsn($this->dsn); + $this->testGetters($connection); + } + + /** + * @covers ::getDatabase + * @covers ::getHost + * @covers ::getPassword + * @covers ::getUsername + */ + public function testGetters(?Connection $connection = null): void + { + $connection = $connection ?: $this->connection; + static::assertSame($this->host, $connection->getHost()); + static::assertSame($this->username, $connection->getUsername()); + static::assertSame($this->password, $connection->getPassword()); + static::assertSame($this->database, $connection->getDatabase()); + } + + /** + * @covers ::getIdentifier + */ + public function testGetIdentifier(): void + { + static::assertSame(sha1(sprintf('%s.%s.%s', $this->host, $this->database, $this->username)), $this->connection->getIdentifier()); + } +} diff --git a/tests/FakerTrait.php b/tests/FakerTrait.php new file mode 100644 index 0000000..c04fa29 --- /dev/null +++ b/tests/FakerTrait.php @@ -0,0 +1,29 @@ +connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::SUCCESS_ENDPOINT); + $this->connection->expects(static::once())->method('getHost')->willReturn(self::TEST_URL.self::SUCCESS_ENDPOINT); $result = $this->transport->request($service, $method, $arguments); static::assertSame(['success' => 'true'], $result); @@ -59,7 +59,7 @@ public function testRequest(): void public function testRequestDecodingError(): void { [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; - $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::JSON_ERROR_ENDPOINT); + $this->connection->expects(static::once())->method('getHost')->willReturn(self::TEST_URL.self::JSON_ERROR_ENDPOINT); $this->expectException(TransportException::class); $this->transport->request($service, $method, $arguments); @@ -71,7 +71,7 @@ public function testRequestDecodingError(): void public function testRequestRemoteError(): void { [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; - $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::REMOTE_ERROR_ENDPOINT); + $this->connection->expects(static::once())->method('getHost')->willReturn(self::TEST_URL.self::REMOTE_ERROR_ENDPOINT); $this->expectException(RemoteException::class); $this->transport->request($service, $method, $arguments); From 963f0711a762356c68bd025ac94f6d9711e22069 Mon Sep 17 00:00:00 2001 From: Ang3 Date: Tue, 21 Nov 2023 09:10:18 +0000 Subject: [PATCH 67/80] Fix PHP code via php-cs-fixer (auto) [no ci] --- src/Connection.php | 3 +-- src/Exception/ConnectionException.php | 2 +- src/Exception/ExceptionInterface.php | 4 +--- src/Exception/RequestException.php | 4 +--- src/Exception/TransportException.php | 4 +--- src/Metadata/Version.php | 3 +-- src/Transport/JsonRpcPhpStreamTransport.php | 3 +-- 7 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/Connection.php b/src/Connection.php index 4a8cb76..dc1de49 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -20,8 +20,7 @@ public function __construct( private readonly string $username, private readonly string $password, private readonly string $database - ) { - } + ) {} public function __toString(): string { diff --git a/src/Exception/ConnectionException.php b/src/Exception/ConnectionException.php index 3f7e10c..05a8a50 100644 --- a/src/Exception/ConnectionException.php +++ b/src/Exception/ConnectionException.php @@ -16,7 +16,7 @@ */ class ConnectionException extends \InvalidArgumentException implements ExceptionInterface { - public static function invalidDsn(string $dsn, ?string $message = null, ?\Throwable $previous = null): self + public static function invalidDsn(string $dsn, string $message = null, \Throwable $previous = null): self { return new self(sprintf('The DSN "%s" is not valid - %s', $dsn, $message ?: 'Unknown error'), 0, $previous); } diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index ee50698..a3304e6 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -interface ExceptionInterface extends \Throwable -{ -} +interface ExceptionInterface extends \Throwable {} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 204c7dc..50cf65a 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface -{ -} +class RequestException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 9e8bba3..057e514 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface -{ -} +class TransportException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 67d4ebe..728879b 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -21,8 +21,7 @@ public function __construct( private readonly string $buildIdentifier, private readonly string $buildVersion, private readonly int $protocolVersion - ) { - } + ) {} /** * Creates the instance from Odoo response payload. diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index ed3b1a1..9126fce 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -29,8 +29,7 @@ class JsonRpcPhpStreamTransport implements TransportInterface public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) { - } + ) {} public function request(string $service, string $method, array $arguments = []): mixed { From d52b348d4c559a6dfc6e11cabe81e8054dc6c544 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Tue, 21 Nov 2023 10:17:34 +0100 Subject: [PATCH 68/80] Fix code --- tests/ConnectionTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index 36e27b5..13cff0b 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -82,8 +82,7 @@ public function testCreateWithMissingParameters( ?string $username = null, ?string $password = null, ?string $database = null - ): void - { + ): void { $this->expectException(ConnectionException::class); Connection::create([ From 1f6ee1726ad12fca1c0a632486837a53d13af0d9 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Tue, 21 Nov 2023 10:17:36 +0100 Subject: [PATCH 69/80] Update README.md --- README.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 987505a..d75430b 100644 --- a/README.md +++ b/README.md @@ -47,23 +47,28 @@ Basic usage ### Create a client -You can create a client via DSN or array configuration. +You can create a client with DSN or array configuration. +Both methods will create a connection to construct the client. #### By DSN -First, create a client instance statically with your config: +First, create your DSN from your connection settings. The DSN must look like this: -```php -use Ang3\Component\Odoo\Client; +`odoo://:@/` -$dsn = `odoo://:@/`; -$client = Client::create($dsn, $transport = null, $logger = null); +The scheme must be `odoo`. +If your password contains special characters, encode it with the native function `urlencode()`: + +```php +$myEncodedPassword = urlencode('high_password_with_special_charaters'); ``` -If your password contains special characters, encode it with the native function `urlencode()`: +Then, use the DSN to create the client: ```php -$myEncodedPassword = urlencode('high_password'); +use Ang3\Component\Odoo\Client; + +$client = Client::create($dsn, $transport = null, $logger = null); ``` #### Array configuration @@ -71,8 +76,6 @@ $myEncodedPassword = urlencode('high_password'); To create a client from an array configuration: ```php -use Ang3\Component\Odoo\Client; - $client = Client::create([ 'url' => '', 'database' => '', @@ -81,6 +84,8 @@ $client = Client::create([ ], $transport = null, $logger = null); ``` +All parameters in this example are required. + #### Error handling Exceptions: From 871d46e0bc5e65880c4e594ca5d196dfeb53b2b1 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Tue, 21 Nov 2023 10:21:52 +0100 Subject: [PATCH 70/80] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d75430b..0a0fb90 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Basic usage You can create a client with DSN or array configuration. Both methods will create a connection to construct the client. -#### By DSN +#### DSN First, create your DSN from your connection settings. The DSN must look like this: From 64feccf1ae45727d40acc6b1ab26eb2ce55fa6aa Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Tue, 21 Nov 2023 10:25:12 +0100 Subject: [PATCH 71/80] Update UPGRADE-8.0.md --- UPGRADE-8.0.md | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/UPGRADE-8.0.md b/UPGRADE-8.0.md index 4b201bb..1171353 100644 --- a/UPGRADE-8.0.md +++ b/UPGRADE-8.0.md @@ -3,24 +3,8 @@ UPGRADE FROM 7.x to 8.0 **Binary compatibility break (BC break)** -Global ------- - +- PHP 8.1+ required - The PHP extension ```php-xmlrpc``` is not used anymore. - The PHP extension ```php-json``` is now required. -- Github workflows - -Client ------- - -- Renamed method ```Client::create()``` to ```Client::insert()``` -- Renamed static method ```Client::createFromConfig()``` to ```Client::create()``` -- All methods fixed and tested. - -Expression builder ------------------- - -Architecture update: - - Domain classes moved from `Ang3\Component\Odoo\DBAL\Expression` to `Ang3\Component\Odoo\DBAL\Expression\Domain` - - Operation classes moved from `Ang3\Component\Odoo\DBAL\Expression` to `Ang3\Component\Odoo\DBAL\Expression\Operation` -- Domain expressions fixes. +- DBAL features moved into the package [ang3/php-odoo-dbal](https://github.com/ang3/php-odoo-dbal) +- Github workflows and tests From 27ca11fd06ae09045d8b2ac5cd690b045f86af6a Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Tue, 21 Nov 2023 11:07:00 +0100 Subject: [PATCH 72/80] Implemented connection scheme parameter --- README.md | 15 ++++++----- src/Connection.php | 25 ++++++++++++++----- src/Exception/ExceptionInterface.php | 4 ++- src/Exception/RequestException.php | 4 ++- src/Exception/TransportException.php | 4 ++- src/Metadata/Version.php | 3 ++- src/Transport/JsonRpcPhpStreamTransport.php | 11 ++++---- tests/ConnectionTest.php | 2 +- .../JsonRpcPhpStreamTransportTest.php | 6 ++--- 9 files changed, 47 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 0a0fb90..5693954 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,9 @@ Both methods will create a connection to construct the client. First, create your DSN from your connection settings. The DSN must look like this: -`odoo://:@/` +`https://:@/` -The scheme must be `odoo`. +The scheme must be `http` or `https` (by default). If your password contains special characters, encode it with the native function `urlencode()`: ```php @@ -77,15 +77,14 @@ To create a client from an array configuration: ```php $client = Client::create([ - 'url' => '', - 'database' => '', - 'username' => '', - 'password' => '', + 'url' => '', // required + 'database' => '', // required + 'username' => '', // required + 'password' => '', // required + 'scheme' => 'https', // optional ], $transport = null, $logger = null); ``` -All parameters in this example are required. - #### Error handling Exceptions: diff --git a/src/Connection.php b/src/Connection.php index dc1de49..f6d76c2 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -19,12 +19,14 @@ public function __construct( private readonly string $host, private readonly string $username, private readonly string $password, - private readonly string $database - ) {} + private readonly string $database, + private readonly string $scheme = 'https' + ) { + } public function __toString(): string { - return sprintf('odoo://%s:%s@%s/%s', $this->username, urlencode($this->password), $this->host, $this->database); + return sprintf('%s://%s:%s@%s/%s', $this->scheme, $this->username, urlencode($this->password), $this->host, $this->database); } public static function create(array $config): self @@ -43,7 +45,8 @@ public static function create(array $config): self $getParam($config, 'host'), $getParam($config, 'username'), $getParam($config, 'password'), - $getParam($config, 'database') + $getParam($config, 'database'), + $config['scheme'] ?? 'https', ); } @@ -71,8 +74,8 @@ public static function parseDsn(string $dsn): self throw ConnectionException::invalidDsn($dsn, 'Missing scheme.'); } - if ('odoo' !== $scheme) { - throw ConnectionException::invalidDsn($dsn, sprintf('The scheme "%s" is not supported (expecting "odoo").', $scheme)); + if (!\in_array($scheme, ['http', 'https'], true)) { + throw ConnectionException::invalidDsn($dsn, sprintf('The scheme "%s" is not supported (supported: "http" or "https").', $scheme)); } if (!$host) { @@ -123,4 +126,14 @@ public function getDatabase(): string { return $this->database; } + + public function getScheme(): string + { + return $this->scheme; + } + + public function getUrl(): string + { + return sprintf('%s://%s', $this->scheme, $this->host); + } } diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index a3304e6..ee50698 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -interface ExceptionInterface extends \Throwable {} +interface ExceptionInterface extends \Throwable +{ +} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 50cf65a..204c7dc 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface {} +class RequestException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 057e514..9e8bba3 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface {} +class TransportException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 728879b..67d4ebe 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -21,7 +21,8 @@ public function __construct( private readonly string $buildIdentifier, private readonly string $buildVersion, private readonly int $protocolVersion - ) {} + ) { + } /** * Creates the instance from Odoo response payload. diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index 9126fce..07258ff 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -29,7 +29,8 @@ class JsonRpcPhpStreamTransport implements TransportInterface public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) {} + ) { + } public function request(string $service, string $method, array $arguments = []): mixed { @@ -57,14 +58,14 @@ public function request(string $service, string $method, array $arguments = []): ], ]); - $endpointUrl = $this->connection->getHost().self::DEFAULT_ENDPOINT; - $request = file_get_contents($endpointUrl, false, $context); + $endpointUrl = $this->connection->getUrl().self::DEFAULT_ENDPOINT; + $response = file_get_contents($endpointUrl, false, $context); - if (false === $request) { + if (false === $response) { throw new TransportException('JSON RPC request failed - Unable to get stream contents.'); } - $data = (array) json_decode($request, true); + $data = (array) json_decode($response, true); if (JSON_ERROR_NONE !== json_last_error()) { throw new TransportException(sprintf('Failed to decode JSON data: %s', json_last_error_msg())); diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index 13cff0b..a5fd46c 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -39,7 +39,7 @@ protected function setUp(): void $this->password = self::faker()->password(8); $this->database = 'my-database-3548373'; $this->connection = new Connection($this->host, $this->username, $this->password, $this->database); - $this->dsn = sprintf('odoo://%s:%s@%s/%s', $this->username, urlencode($this->password), $this->host, $this->database); + $this->dsn = sprintf('https://%s:%s@%s/%s', $this->username, urlencode($this->password), $this->host, $this->database); } /** diff --git a/tests/Transport/JsonRpcPhpStreamTransportTest.php b/tests/Transport/JsonRpcPhpStreamTransportTest.php index 1838e29..61fba7a 100644 --- a/tests/Transport/JsonRpcPhpStreamTransportTest.php +++ b/tests/Transport/JsonRpcPhpStreamTransportTest.php @@ -47,7 +47,7 @@ protected function setUp(): void public function testRequest(): void { [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; - $this->connection->expects(static::once())->method('getHost')->willReturn(self::TEST_URL.self::SUCCESS_ENDPOINT); + $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::SUCCESS_ENDPOINT); $result = $this->transport->request($service, $method, $arguments); static::assertSame(['success' => 'true'], $result); @@ -59,7 +59,7 @@ public function testRequest(): void public function testRequestDecodingError(): void { [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; - $this->connection->expects(static::once())->method('getHost')->willReturn(self::TEST_URL.self::JSON_ERROR_ENDPOINT); + $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::JSON_ERROR_ENDPOINT); $this->expectException(TransportException::class); $this->transport->request($service, $method, $arguments); @@ -71,7 +71,7 @@ public function testRequestDecodingError(): void public function testRequestRemoteError(): void { [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; - $this->connection->expects(static::once())->method('getHost')->willReturn(self::TEST_URL.self::REMOTE_ERROR_ENDPOINT); + $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::REMOTE_ERROR_ENDPOINT); $this->expectException(RemoteException::class); $this->transport->request($service, $method, $arguments); From c3621c9eb6ae82eccd524ef893eeab1f87a6fc65 Mon Sep 17 00:00:00 2001 From: Joanis Rouanet Date: Tue, 21 Nov 2023 11:08:33 +0100 Subject: [PATCH 73/80] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5693954..ffb76bd 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ To create a client from an array configuration: ```php $client = Client::create([ - 'url' => '', // required + 'host' => '', // required (without scheme!) 'database' => '', // required 'username' => '', // required 'password' => '', // required From 1656305ef6e252525c27db38e45ec104c73110e4 Mon Sep 17 00:00:00 2001 From: Ang3 Date: Tue, 21 Nov 2023 10:10:03 +0000 Subject: [PATCH 74/80] Fix PHP code via php-cs-fixer (auto) [no ci] --- src/Connection.php | 3 +-- src/Exception/ExceptionInterface.php | 4 +--- src/Exception/RequestException.php | 4 +--- src/Exception/TransportException.php | 4 +--- src/Metadata/Version.php | 3 +-- src/Transport/JsonRpcPhpStreamTransport.php | 3 +-- 6 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/Connection.php b/src/Connection.php index f6d76c2..f73b7bb 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -21,8 +21,7 @@ public function __construct( private readonly string $password, private readonly string $database, private readonly string $scheme = 'https' - ) { - } + ) {} public function __toString(): string { diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index ee50698..a3304e6 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -interface ExceptionInterface extends \Throwable -{ -} +interface ExceptionInterface extends \Throwable {} diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 204c7dc..50cf65a 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface -{ -} +class RequestException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 9e8bba3..057e514 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,6 +14,4 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface -{ -} +class TransportException extends \RuntimeException implements ExceptionInterface {} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 67d4ebe..728879b 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -21,8 +21,7 @@ public function __construct( private readonly string $buildIdentifier, private readonly string $buildVersion, private readonly int $protocolVersion - ) { - } + ) {} /** * Creates the instance from Odoo response payload. diff --git a/src/Transport/JsonRpcPhpStreamTransport.php b/src/Transport/JsonRpcPhpStreamTransport.php index 07258ff..8131d2d 100644 --- a/src/Transport/JsonRpcPhpStreamTransport.php +++ b/src/Transport/JsonRpcPhpStreamTransport.php @@ -29,8 +29,7 @@ class JsonRpcPhpStreamTransport implements TransportInterface public function __construct( private readonly Connection $connection, private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) { - } + ) {} public function request(string $service, string $method, array $arguments = []): mixed { From 325119dd9fb87e94f8ed7654616bb77671aaa663 Mon Sep 17 00:00:00 2001 From: Ang3 Date: Fri, 16 Jan 2026 18:06:25 +0100 Subject: [PATCH 75/80] Fix tests --- .gitignore | 1 + .../08c243b330d480e348c2e23951b2ed4e | 1 + .../2ab0a544083c0b866e56d1a1a72d953b | 1 + .../2b8f89c7eb6feacfb35a96b5ed5ba6dc | 1 + .../369798d81ca7dddb00be7f6947eede91 | 1 + .../4bf226508f77741cf5afa74dafa86178 | 1 + .../52118a736c1a03a7e6b2f995149dfe68 | 1 + .../58806f641bbffe5922122c226ac27b4a | 1 + .../717ffe8417576cb1f922c16743e6f6ff | 1 + .../99a617401202e025c1ebee54d349c25b | 1 + .../a8bb1700d6e80837ca1c1a13e68a74e6 | 1 + .../aafdcf027cacd0bd24b17d8fccd53308 | 1 + .../ad5d7750bc032bbc4e872fe2b81bd1d4 | 1 + .../af0a8e802569cf7c13c2b0d56d761e60 | 1 + .../cc2997b47b202f04df6c5d311bddc88f | 1 + .../e0b8c8e2f445c41b4306b462a52a7a83 | 1 + .../e3753fe5e4e60324f36fee0947c42207 | 1 + .../e8d63d9025d0e3a640ba472491b40e70 | 1 + .phpunit.cache/test-results | 1 + Dockerfile | 32 + Makefile | 27 + bin/check-code | 5 + bin/fix-code | 9 + bin/test | 5 + compose.yaml | 9 + composer.json | 8 +- composer.lock | 4405 +++++++++++++---- docker/logs | 5 + docker/php | 5 + phpstan.neon | 12 +- phpunit.xml.dist | 35 +- src/Client.php | 45 +- src/Connection.php | 81 +- src/Enum/OdooRpcMethod.php | 5 + src/Enum/OdooRpcService.php | 5 + src/Exception/AuthenticationException.php | 2 +- src/Exception/ConnectionException.php | 23 - src/Exception/ExceptionInterface.php | 6 +- src/Exception/RemoteException.php | 14 + src/Exception/RequestException.php | 4 +- src/Exception/TransportException.php | 4 +- src/Metadata/Version.php | 39 +- src/Transport/Client/JsonRpcHttpClient.php | 39 + .../Client/JsonRpcHttpClientInterface.php | 20 + ...reamTransport.php => JsonRpcTransport.php} | 49 +- src/Transport/TransportInterface.php | 5 +- tests.phpstan.neon | 9 - tests/ClientTest.php | 232 +- tests/ConnectionTest.php | 133 +- tests/FakerTrait.php | 29 - tests/Metadata/VersionTest.php | 78 + .../Client/JsonRpcHttpClientTest.php | 45 + .../JsonRpcPhpStreamTransportTest.php | 79 - tests/Transport/JsonRpcTransportTest.php | 95 + 54 files changed, 4030 insertions(+), 1587 deletions(-) create mode 100644 .phpunit.cache/code-coverage/08c243b330d480e348c2e23951b2ed4e create mode 100644 .phpunit.cache/code-coverage/2ab0a544083c0b866e56d1a1a72d953b create mode 100644 .phpunit.cache/code-coverage/2b8f89c7eb6feacfb35a96b5ed5ba6dc create mode 100644 .phpunit.cache/code-coverage/369798d81ca7dddb00be7f6947eede91 create mode 100644 .phpunit.cache/code-coverage/4bf226508f77741cf5afa74dafa86178 create mode 100644 .phpunit.cache/code-coverage/52118a736c1a03a7e6b2f995149dfe68 create mode 100644 .phpunit.cache/code-coverage/58806f641bbffe5922122c226ac27b4a create mode 100644 .phpunit.cache/code-coverage/717ffe8417576cb1f922c16743e6f6ff create mode 100644 .phpunit.cache/code-coverage/99a617401202e025c1ebee54d349c25b create mode 100644 .phpunit.cache/code-coverage/a8bb1700d6e80837ca1c1a13e68a74e6 create mode 100644 .phpunit.cache/code-coverage/aafdcf027cacd0bd24b17d8fccd53308 create mode 100644 .phpunit.cache/code-coverage/ad5d7750bc032bbc4e872fe2b81bd1d4 create mode 100644 .phpunit.cache/code-coverage/af0a8e802569cf7c13c2b0d56d761e60 create mode 100644 .phpunit.cache/code-coverage/cc2997b47b202f04df6c5d311bddc88f create mode 100644 .phpunit.cache/code-coverage/e0b8c8e2f445c41b4306b462a52a7a83 create mode 100644 .phpunit.cache/code-coverage/e3753fe5e4e60324f36fee0947c42207 create mode 100644 .phpunit.cache/code-coverage/e8d63d9025d0e3a640ba472491b40e70 create mode 100644 .phpunit.cache/test-results create mode 100644 Dockerfile create mode 100644 Makefile create mode 100755 bin/check-code create mode 100755 bin/fix-code create mode 100755 bin/test create mode 100644 compose.yaml create mode 100755 docker/logs create mode 100755 docker/php delete mode 100644 src/Exception/ConnectionException.php create mode 100644 src/Transport/Client/JsonRpcHttpClient.php create mode 100644 src/Transport/Client/JsonRpcHttpClientInterface.php rename src/Transport/{JsonRpcPhpStreamTransport.php => JsonRpcTransport.php} (63%) delete mode 100644 tests.phpstan.neon delete mode 100644 tests/FakerTrait.php create mode 100644 tests/Metadata/VersionTest.php create mode 100644 tests/Transport/Client/JsonRpcHttpClientTest.php delete mode 100644 tests/Transport/JsonRpcPhpStreamTransportTest.php create mode 100644 tests/Transport/JsonRpcTransportTest.php diff --git a/.gitignore b/.gitignore index b698aba..91671fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /vendor/ /.idea/ +/var/ .php_cs.cache .php_cs-fixer.cache .php-cs-fixer.cache diff --git a/.phpunit.cache/code-coverage/08c243b330d480e348c2e23951b2ed4e b/.phpunit.cache/code-coverage/08c243b330d480e348c2e23951b2ed4e new file mode 100644 index 0000000..28667dc --- /dev/null +++ b/.phpunit.cache/code-coverage/08c243b330d480e348c2e23951b2ed4e @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";a:6:{s:4:"name";s:17:"JsonRpcHttpClient";s:14:"namespacedName";s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";s:9:"namespace";s:36:"Ang3\Component\Odoo\Transport\Client";s:9:"startLine";i:14;s:7:"endLine";i:39;s:7:"methods";a:2:{s:4:"post";a:6:{s:10:"methodName";s:4:"post";s:9:"signature";s:62:"post(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:6:"public";s:9:"startLine";i:16;s:7:"endLine";i:19;s:3:"ccn";i:1;}s:9:"doRequest";a:6:{s:10:"methodName";s:9:"doRequest";s:9:"signature";s:67:"doRequest(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:9:"protected";s:9:"startLine";i:26;s:7:"endLine";i:38;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:40;s:18:"commentLinesOfCode";i:11;s:21:"nonCommentLinesOfCode";i:29;}s:15:"ignoredLinesFor";a:14:{i:0;i:14;i:1;i:26;i:2;i:27;i:3;i:28;i:4;i:29;i:5;i:30;i:6;i:31;i:7;i:32;i:8;i:33;i:9;i:34;i:10;i:35;i:11;i:36;i:12;i:37;i:13;i:38;}s:17:"executableLinesIn";a:10:{i:18;i:1;i:28;i:2;i:29;i:2;i:30;i:2;i:31;i:2;i:32;i:2;i:33;i:2;i:34;i:2;i:35;i:2;i:37;i:3;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/2ab0a544083c0b866e56d1a1a72d953b b/.phpunit.cache/code-coverage/2ab0a544083c0b866e56d1a1a72d953b new file mode 100644 index 0000000..28667dc --- /dev/null +++ b/.phpunit.cache/code-coverage/2ab0a544083c0b866e56d1a1a72d953b @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";a:6:{s:4:"name";s:17:"JsonRpcHttpClient";s:14:"namespacedName";s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";s:9:"namespace";s:36:"Ang3\Component\Odoo\Transport\Client";s:9:"startLine";i:14;s:7:"endLine";i:39;s:7:"methods";a:2:{s:4:"post";a:6:{s:10:"methodName";s:4:"post";s:9:"signature";s:62:"post(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:6:"public";s:9:"startLine";i:16;s:7:"endLine";i:19;s:3:"ccn";i:1;}s:9:"doRequest";a:6:{s:10:"methodName";s:9:"doRequest";s:9:"signature";s:67:"doRequest(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:9:"protected";s:9:"startLine";i:26;s:7:"endLine";i:38;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:40;s:18:"commentLinesOfCode";i:11;s:21:"nonCommentLinesOfCode";i:29;}s:15:"ignoredLinesFor";a:14:{i:0;i:14;i:1;i:26;i:2;i:27;i:3;i:28;i:4;i:29;i:5;i:30;i:6;i:31;i:7;i:32;i:8;i:33;i:9;i:34;i:10;i:35;i:11;i:36;i:12;i:37;i:13;i:38;}s:17:"executableLinesIn";a:10:{i:18;i:1;i:28;i:2;i:29;i:2;i:30;i:2;i:31;i:2;i:32;i:2;i:33;i:2;i:34;i:2;i:35;i:2;i:37;i:3;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/2b8f89c7eb6feacfb35a96b5ed5ba6dc b/.phpunit.cache/code-coverage/2b8f89c7eb6feacfb35a96b5ed5ba6dc new file mode 100644 index 0000000..40b94de --- /dev/null +++ b/.phpunit.cache/code-coverage/2b8f89c7eb6feacfb35a96b5ed5ba6dc @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:46:"Ang3\Component\Odoo\Transport\JsonRpcTransport";a:6:{s:4:"name";s:16:"JsonRpcTransport";s:14:"namespacedName";s:46:"Ang3\Component\Odoo\Transport\JsonRpcTransport";s:9:"namespace";s:29:"Ang3\Component\Odoo\Transport";s:9:"startLine";i:24;s:7:"endLine";i:76;s:7:"methods";a:2:{s:11:"__construct";a:6:{s:10:"methodName";s:11:"__construct";s:9:"signature";s:147:"__construct(Ang3\Component\Odoo\Connection $connection, ?Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClientInterface $httpClient, int $timeOut)";s:10:"visibility";s:6:"public";s:9:"startLine";i:30;s:7:"endLine";i:36;s:3:"ccn";i:2;}s:7:"request";a:6:{s:10:"methodName";s:7:"request";s:9:"signature";s:65:"request(string $service, string $method, array $arguments): mixed";s:10:"visibility";s:6:"public";s:9:"startLine";i:41;s:7:"endLine";i:75;s:3:"ccn";i:5;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:77;s:18:"commentLinesOfCode";i:13;s:21:"nonCommentLinesOfCode";i:64;}s:15:"ignoredLinesFor";a:1:{i:0;i:24;}s:17:"executableLinesIn";a:23:{i:35;i:4;i:43;i:6;i:44;i:6;i:45;i:6;i:46;i:6;i:47;i:6;i:48;i:6;i:49;i:6;i:50;i:6;i:51;i:6;i:52;i:6;i:54;i:7;i:55;i:8;i:58;i:9;i:59;i:10;i:61;i:11;i:62;i:12;i:65;i:13;i:66;i:14;i:67;i:15;i:70;i:16;i:71;i:17;i:74;i:18;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/369798d81ca7dddb00be7f6947eede91 b/.phpunit.cache/code-coverage/369798d81ca7dddb00be7f6947eede91 new file mode 100644 index 0000000..79a6c26 --- /dev/null +++ b/.phpunit.cache/code-coverage/369798d81ca7dddb00be7f6947eede91 @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:0:{}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:25;s:18:"commentLinesOfCode";i:11;s:21:"nonCommentLinesOfCode";i:14;}s:15:"ignoredLinesFor";a:6:{i:0;i:19;i:1;i:20;i:2;i:21;i:3;i:22;i:4;i:23;i:5;i:24;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/4bf226508f77741cf5afa74dafa86178 b/.phpunit.cache/code-coverage/4bf226508f77741cf5afa74dafa86178 new file mode 100644 index 0000000..fd47b68 --- /dev/null +++ b/.phpunit.cache/code-coverage/4bf226508f77741cf5afa74dafa86178 @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:30:"Ang3\Component\Odoo\Connection";a:6:{s:4:"name";s:10:"Connection";s:14:"namespacedName";s:30:"Ang3\Component\Odoo\Connection";s:9:"namespace";s:19:"Ang3\Component\Odoo";s:9:"startLine";i:20;s:7:"endLine";i:151;s:7:"methods";a:11:{s:11:"__construct";a:6:{s:10:"methodName";s:11:"__construct";s:9:"signature";s:95:"__construct(string $host, string $username, string $password, string $database, string $scheme)";s:10:"visibility";s:6:"public";s:9:"startLine";i:22;s:7:"endLine";i:29;s:3:"ccn";i:1;}s:10:"__toString";a:6:{s:10:"methodName";s:10:"__toString";s:9:"signature";s:20:"__toString(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:31;s:7:"endLine";i:34;s:3:"ccn";i:1;}s:6:"create";a:6:{s:10:"methodName";s:6:"create";s:9:"signature";s:27:"create(array $config): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:41;s:7:"endLine";i:64;s:3:"ccn";i:3;}s:8:"parseDsn";a:6:{s:10:"methodName";s:8:"parseDsn";s:9:"signature";s:27:"parseDsn(string $dsn): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:69;s:7:"endLine";i:112;s:3:"ccn";i:9;}s:13:"getIdentifier";a:6:{s:10:"methodName";s:13:"getIdentifier";s:9:"signature";s:23:"getIdentifier(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:117;s:7:"endLine";i:120;s:3:"ccn";i:1;}s:7:"getHost";a:6:{s:10:"methodName";s:7:"getHost";s:9:"signature";s:17:"getHost(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:122;s:7:"endLine";i:125;s:3:"ccn";i:1;}s:11:"getUsername";a:6:{s:10:"methodName";s:11:"getUsername";s:9:"signature";s:21:"getUsername(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:127;s:7:"endLine";i:130;s:3:"ccn";i:1;}s:11:"getPassword";a:6:{s:10:"methodName";s:11:"getPassword";s:9:"signature";s:21:"getPassword(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:132;s:7:"endLine";i:135;s:3:"ccn";i:1;}s:11:"getDatabase";a:6:{s:10:"methodName";s:11:"getDatabase";s:9:"signature";s:21:"getDatabase(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:137;s:7:"endLine";i:140;s:3:"ccn";i:1;}s:9:"getScheme";a:6:{s:10:"methodName";s:9:"getScheme";s:9:"signature";s:19:"getScheme(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:142;s:7:"endLine";i:145;s:3:"ccn";i:1;}s:6:"getUrl";a:6:{s:10:"methodName";s:6:"getUrl";s:9:"signature";s:16:"getUrl(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:147;s:7:"endLine";i:150;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:152;s:18:"commentLinesOfCode";i:20;s:21:"nonCommentLinesOfCode";i:132;}s:15:"ignoredLinesFor";a:1:{i:0;i:20;}s:17:"executableLinesIn";a:48:{i:29;i:1;i:33;i:2;i:43;i:3;i:55;i:3;i:44;i:4;i:46;i:5;i:47;i:6;i:50;i:7;i:51;i:8;i:54;i:9;i:57;i:10;i:58;i:10;i:59;i:10;i:60;i:10;i:61;i:10;i:62;i:10;i:63;i:10;i:72;i:11;i:73;i:12;i:74;i:13;i:77;i:14;i:78;i:14;i:79;i:14;i:80;i:14;i:81;i:14;i:82;i:14;i:83;i:14;i:85;i:15;i:86;i:16;i:89;i:17;i:90;i:18;i:93;i:19;i:94;i:20;i:97;i:21;i:98;i:22;i:101;i:23;i:102;i:24;i:105;i:25;i:106;i:26;i:109;i:27;i:111;i:28;i:119;i:29;i:124;i:30;i:129;i:31;i:134;i:32;i:139;i:33;i:144;i:34;i:149;i:35;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/52118a736c1a03a7e6b2f995149dfe68 b/.phpunit.cache/code-coverage/52118a736c1a03a7e6b2f995149dfe68 new file mode 100644 index 0000000..7ce9224 --- /dev/null +++ b/.phpunit.cache/code-coverage/52118a736c1a03a7e6b2f995149dfe68 @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:53:"Ang3\Component\Odoo\Exception\AuthenticationException";a:6:{s:4:"name";s:23:"AuthenticationException";s:14:"namespacedName";s:53:"Ang3\Component\Odoo\Exception\AuthenticationException";s:9:"namespace";s:29:"Ang3\Component\Odoo\Exception";s:9:"startLine";i:17;s:7:"endLine";i:23;s:7:"methods";a:1:{s:11:"__construct";a:6:{s:10:"methodName";s:11:"__construct";s:9:"signature";s:33:"__construct(?Throwable $previous)";s:10:"visibility";s:6:"public";s:9:"startLine";i:19;s:7:"endLine";i:22;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:24;s:18:"commentLinesOfCode";i:9;s:21:"nonCommentLinesOfCode";i:15;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:1:{i:21;i:1;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/58806f641bbffe5922122c226ac27b4a b/.phpunit.cache/code-coverage/58806f641bbffe5922122c226ac27b4a new file mode 100644 index 0000000..a7e9fee --- /dev/null +++ b/.phpunit.cache/code-coverage/58806f641bbffe5922122c226ac27b4a @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:36:"Ang3\Component\Odoo\Metadata\Version";a:6:{s:4:"name";s:7:"Version";s:14:"namespacedName";s:36:"Ang3\Component\Odoo\Metadata\Version";s:9:"namespace";s:28:"Ang3\Component\Odoo\Metadata";s:9:"startLine";i:17;s:7:"endLine";i:97;s:7:"methods";a:11:{s:11:"__construct";a:6:{s:10:"methodName";s:11:"__construct";s:9:"signature";s:156:"__construct(int $majorVersion, int $minorVersion, int $patchVersion, string $buildName, string $buildIdentifier, string $buildVersion, int $protocolVersion)";s:10:"visibility";s:6:"public";s:9:"startLine";i:19;s:7:"endLine";i:28;s:3:"ccn";i:1;}s:6:"create";a:6:{s:10:"methodName";s:6:"create";s:9:"signature";s:28:"create(array $payload): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:35;s:7:"endLine";i:51;s:3:"ccn";i:1;}s:10:"__toString";a:6:{s:10:"methodName";s:10:"__toString";s:9:"signature";s:20:"__toString(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:53;s:7:"endLine";i:56;s:3:"ccn";i:1;}s:7:"getName";a:6:{s:10:"methodName";s:7:"getName";s:9:"signature";s:17:"getName(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:58;s:7:"endLine";i:61;s:3:"ccn";i:1;}s:15:"getMajorVersion";a:6:{s:10:"methodName";s:15:"getMajorVersion";s:9:"signature";s:22:"getMajorVersion(): int";s:10:"visibility";s:6:"public";s:9:"startLine";i:63;s:7:"endLine";i:66;s:3:"ccn";i:1;}s:15:"getMinorVersion";a:6:{s:10:"methodName";s:15:"getMinorVersion";s:9:"signature";s:22:"getMinorVersion(): int";s:10:"visibility";s:6:"public";s:9:"startLine";i:68;s:7:"endLine";i:71;s:3:"ccn";i:1;}s:15:"getPatchVersion";a:6:{s:10:"methodName";s:15:"getPatchVersion";s:9:"signature";s:22:"getPatchVersion(): int";s:10:"visibility";s:6:"public";s:9:"startLine";i:73;s:7:"endLine";i:76;s:3:"ccn";i:1;}s:12:"getBuildName";a:6:{s:10:"methodName";s:12:"getBuildName";s:9:"signature";s:22:"getBuildName(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:78;s:7:"endLine";i:81;s:3:"ccn";i:1;}s:18:"getBuildIdentifier";a:6:{s:10:"methodName";s:18:"getBuildIdentifier";s:9:"signature";s:28:"getBuildIdentifier(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:83;s:7:"endLine";i:86;s:3:"ccn";i:1;}s:15:"getBuildVersion";a:6:{s:10:"methodName";s:15:"getBuildVersion";s:9:"signature";s:25:"getBuildVersion(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:88;s:7:"endLine";i:91;s:3:"ccn";i:1;}s:18:"getProtocolVersion";a:6:{s:10:"methodName";s:18:"getProtocolVersion";s:9:"signature";s:25:"getProtocolVersion(): int";s:10:"visibility";s:6:"public";s:9:"startLine";i:93;s:7:"endLine";i:96;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:98;s:18:"commentLinesOfCode";i:16;s:21:"nonCommentLinesOfCode";i:82;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:21:{i:28;i:1;i:38;i:2;i:40;i:3;i:42;i:4;i:43;i:4;i:44;i:4;i:45;i:4;i:46;i:4;i:47;i:4;i:48;i:4;i:49;i:4;i:50;i:4;i:55;i:5;i:60;i:6;i:65;i:7;i:70;i:8;i:75;i:9;i:80;i:10;i:85;i:11;i:90;i:12;i:95;i:13;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/717ffe8417576cb1f922c16743e6f6ff b/.phpunit.cache/code-coverage/717ffe8417576cb1f922c16743e6f6ff new file mode 100644 index 0000000..5bd38d0 --- /dev/null +++ b/.phpunit.cache/code-coverage/717ffe8417576cb1f922c16743e6f6ff @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:0:{}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:31;s:18:"commentLinesOfCode";i:14;s:21:"nonCommentLinesOfCode";i:17;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/99a617401202e025c1ebee54d349c25b b/.phpunit.cache/code-coverage/99a617401202e025c1ebee54d349c25b new file mode 100644 index 0000000..e7f28a3 --- /dev/null +++ b/.phpunit.cache/code-coverage/99a617401202e025c1ebee54d349c25b @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:0:{}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:24;s:18:"commentLinesOfCode";i:11;s:21:"nonCommentLinesOfCode";i:13;}s:15:"ignoredLinesFor";a:5:{i:0;i:19;i:1;i:20;i:2;i:21;i:3;i:22;i:4;i:23;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/a8bb1700d6e80837ca1c1a13e68a74e6 b/.phpunit.cache/code-coverage/a8bb1700d6e80837ca1c1a13e68a74e6 new file mode 100644 index 0000000..a290216 --- /dev/null +++ b/.phpunit.cache/code-coverage/a8bb1700d6e80837ca1c1a13e68a74e6 @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:46:"Ang3\Component\Odoo\Exception\RequestException";a:6:{s:4:"name";s:16:"RequestException";s:14:"namespacedName";s:46:"Ang3\Component\Odoo\Exception\RequestException";s:9:"namespace";s:29:"Ang3\Component\Odoo\Exception";s:9:"startLine";i:17;s:7:"endLine";i:19;s:7:"methods";a:0:{}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:20;s:18:"commentLinesOfCode";i:9;s:21:"nonCommentLinesOfCode";i:11;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/aafdcf027cacd0bd24b17d8fccd53308 b/.phpunit.cache/code-coverage/aafdcf027cacd0bd24b17d8fccd53308 new file mode 100644 index 0000000..216a7d2 --- /dev/null +++ b/.phpunit.cache/code-coverage/aafdcf027cacd0bd24b17d8fccd53308 @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:0:{}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:22;s:18:"commentLinesOfCode";i:11;s:21:"nonCommentLinesOfCode";i:11;}s:15:"ignoredLinesFor";a:1:{i:0;i:19;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/ad5d7750bc032bbc4e872fe2b81bd1d4 b/.phpunit.cache/code-coverage/ad5d7750bc032bbc4e872fe2b81bd1d4 new file mode 100644 index 0000000..86d4df2 --- /dev/null +++ b/.phpunit.cache/code-coverage/ad5d7750bc032bbc4e872fe2b81bd1d4 @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";a:6:{s:4:"name";s:17:"JsonRpcHttpClient";s:14:"namespacedName";s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";s:9:"namespace";s:36:"Ang3\Component\Odoo\Transport\Client";s:9:"startLine";i:14;s:7:"endLine";i:29;s:7:"methods";a:1:{s:4:"post";a:6:{s:10:"methodName";s:4:"post";s:9:"signature";s:62:"post(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:6:"public";s:9:"startLine";i:16;s:7:"endLine";i:28;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:30;s:18:"commentLinesOfCode";i:6;s:21:"nonCommentLinesOfCode";i:24;}s:15:"ignoredLinesFor";a:1:{i:0;i:14;}s:17:"executableLinesIn";a:9:{i:18;i:1;i:19;i:1;i:20;i:1;i:21;i:1;i:22;i:1;i:23;i:1;i:24;i:1;i:25;i:1;i:27;i:2;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/af0a8e802569cf7c13c2b0d56d761e60 b/.phpunit.cache/code-coverage/af0a8e802569cf7c13c2b0d56d761e60 new file mode 100644 index 0000000..1b8f6f4 --- /dev/null +++ b/.phpunit.cache/code-coverage/af0a8e802569cf7c13c2b0d56d761e60 @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";a:6:{s:4:"name";s:17:"JsonRpcHttpClient";s:14:"namespacedName";s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";s:9:"namespace";s:36:"Ang3\Component\Odoo\Transport\Client";s:9:"startLine";i:14;s:7:"endLine";i:37;s:7:"methods";a:2:{s:4:"post";a:6:{s:10:"methodName";s:4:"post";s:9:"signature";s:62:"post(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:6:"public";s:9:"startLine";i:16;s:7:"endLine";i:19;s:3:"ccn";i:1;}s:9:"doRequest";a:6:{s:10:"methodName";s:9:"doRequest";s:9:"signature";s:67:"doRequest(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:9:"protected";s:9:"startLine";i:24;s:7:"endLine";i:36;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:38;s:18:"commentLinesOfCode";i:9;s:21:"nonCommentLinesOfCode";i:29;}s:15:"ignoredLinesFor";a:1:{i:0;i:14;}s:17:"executableLinesIn";a:10:{i:18;i:1;i:26;i:2;i:27;i:2;i:28;i:2;i:29;i:2;i:30;i:2;i:31;i:2;i:32;i:2;i:33;i:2;i:35;i:3;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/cc2997b47b202f04df6c5d311bddc88f b/.phpunit.cache/code-coverage/cc2997b47b202f04df6c5d311bddc88f new file mode 100644 index 0000000..1485b39 --- /dev/null +++ b/.phpunit.cache/code-coverage/cc2997b47b202f04df6c5d311bddc88f @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:48:"Ang3\Component\Odoo\Exception\TransportException";a:6:{s:4:"name";s:18:"TransportException";s:14:"namespacedName";s:48:"Ang3\Component\Odoo\Exception\TransportException";s:9:"namespace";s:29:"Ang3\Component\Odoo\Exception";s:9:"startLine";i:17;s:7:"endLine";i:19;s:7:"methods";a:0:{}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:20;s:18:"commentLinesOfCode";i:9;s:21:"nonCommentLinesOfCode";i:11;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/e0b8c8e2f445c41b4306b462a52a7a83 b/.phpunit.cache/code-coverage/e0b8c8e2f445c41b4306b462a52a7a83 new file mode 100644 index 0000000..ff7b3f9 --- /dev/null +++ b/.phpunit.cache/code-coverage/e0b8c8e2f445c41b4306b462a52a7a83 @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:0:{}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:21;s:18:"commentLinesOfCode";i:9;s:21:"nonCommentLinesOfCode";i:12;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/e3753fe5e4e60324f36fee0947c42207 b/.phpunit.cache/code-coverage/e3753fe5e4e60324f36fee0947c42207 new file mode 100644 index 0000000..523ab5b --- /dev/null +++ b/.phpunit.cache/code-coverage/e3753fe5e4e60324f36fee0947c42207 @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:26:"Ang3\Component\Odoo\Client";a:6:{s:4:"name";s:6:"Client";s:14:"namespacedName";s:26:"Ang3\Component\Odoo\Client";s:9:"namespace";s:19:"Ang3\Component\Odoo";s:9:"startLine";i:27;s:7:"endLine";i:147;s:7:"methods";a:11:{s:11:"__construct";a:6:{s:10:"methodName";s:11:"__construct";s:9:"signature";s:151:"__construct(Ang3\Component\Odoo\Connection $connection, ?Ang3\Component\Odoo\Transport\TransportInterface $transport, ?Psr\Log\LoggerInterface $logger)";s:10:"visibility";s:6:"public";s:9:"startLine";i:32;s:7:"endLine";i:38;s:3:"ccn";i:2;}s:9:"executeKw";a:6:{s:10:"methodName";s:9:"executeKw";s:9:"signature";s:81:"executeKw(string $name, string $method, array $parameters, array $options): mixed";s:10:"visibility";s:6:"public";s:9:"startLine";i:44;s:7:"endLine";i:57;s:3:"ccn";i:1;}s:7:"version";a:6:{s:10:"methodName";s:7:"version";s:9:"signature";s:47:"version(): Ang3\Component\Odoo\Metadata\Version";s:10:"visibility";s:6:"public";s:9:"startLine";i:59;s:7:"endLine";i:62;s:3:"ccn";i:1;}s:12:"authenticate";a:6:{s:10:"methodName";s:12:"authenticate";s:9:"signature";s:19:"authenticate(): int";s:10:"visibility";s:6:"public";s:9:"startLine";i:67;s:7:"endLine";i:87;s:3:"ccn";i:3;}s:7:"request";a:6:{s:10:"methodName";s:7:"request";s:9:"signature";s:65:"request(string $service, string $method, mixed $arguments): mixed";s:10:"visibility";s:6:"public";s:9:"startLine";i:93;s:7:"endLine";i:116;s:3:"ccn";i:1;}s:13:"getConnection";a:6:{s:10:"methodName";s:13:"getConnection";s:9:"signature";s:47:"getConnection(): Ang3\Component\Odoo\Connection";s:10:"visibility";s:6:"public";s:9:"startLine";i:118;s:7:"endLine";i:121;s:3:"ccn";i:1;}s:12:"getTransport";a:6:{s:10:"methodName";s:12:"getTransport";s:9:"signature";s:64:"getTransport(): Ang3\Component\Odoo\Transport\TransportInterface";s:10:"visibility";s:6:"public";s:9:"startLine";i:123;s:7:"endLine";i:126;s:3:"ccn";i:1;}s:13:"withTransport";a:6:{s:10:"methodName";s:13:"withTransport";s:9:"signature";s:80:"withTransport(Ang3\Component\Odoo\Transport\TransportInterface $transport): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:128;s:7:"endLine";i:131;s:3:"ccn";i:1;}s:9:"getLogger";a:6:{s:10:"methodName";s:9:"getLogger";s:9:"signature";s:37:"getLogger(): ?Psr\Log\LoggerInterface";s:10:"visibility";s:6:"public";s:9:"startLine";i:133;s:7:"endLine";i:136;s:3:"ccn";i:1;}s:10:"withLogger";a:6:{s:10:"methodName";s:10:"withLogger";s:9:"signature";s:49:"withLogger(Psr\Log\LoggerInterface $logger): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:138;s:7:"endLine";i:141;s:3:"ccn";i:1;}s:6:"getUid";a:6:{s:10:"methodName";s:6:"getUid";s:9:"signature";s:14:"getUid(): ?int";s:10:"visibility";s:6:"public";s:9:"startLine";i:143;s:7:"endLine";i:146;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:148;s:18:"commentLinesOfCode";i:21;s:21:"nonCommentLinesOfCode";i:127;}s:15:"ignoredLinesFor";a:1:{i:0;i:27;}s:17:"executableLinesIn";a:48:{i:37;i:3;i:46;i:5;i:47;i:5;i:48;i:5;i:49;i:5;i:50;i:5;i:51;i:5;i:52;i:5;i:53;i:5;i:54;i:5;i:55;i:5;i:56;i:5;i:61;i:6;i:69;i:7;i:71;i:8;i:72;i:8;i:73;i:8;i:74;i:8;i:75;i:8;i:76;i:8;i:77;i:8;i:79;i:9;i:80;i:10;i:83;i:11;i:86;i:12;i:95;i:13;i:96;i:13;i:97;i:13;i:98;i:13;i:99;i:13;i:100;i:13;i:101;i:13;i:103;i:14;i:105;i:15;i:106;i:16;i:107;i:17;i:109;i:18;i:110;i:18;i:111;i:18;i:112;i:18;i:113;i:18;i:115;i:19;i:120;i:20;i:125;i:21;i:130;i:22;i:135;i:23;i:140;i:24;i:145;i:25;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/e8d63d9025d0e3a640ba472491b40e70 b/.phpunit.cache/code-coverage/e8d63d9025d0e3a640ba472491b40e70 new file mode 100644 index 0000000..009cea0 --- /dev/null +++ b/.phpunit.cache/code-coverage/e8d63d9025d0e3a640ba472491b40e70 @@ -0,0 +1 @@ +a:6:{s:9:"classesIn";a:1:{s:45:"Ang3\Component\Odoo\Exception\RemoteException";a:6:{s:4:"name";s:15:"RemoteException";s:14:"namespacedName";s:45:"Ang3\Component\Odoo\Exception\RemoteException";s:9:"namespace";s:29:"Ang3\Component\Odoo\Exception";s:9:"startLine";i:21;s:7:"endLine";i:83;s:7:"methods";a:2:{s:6:"create";a:6:{s:10:"methodName";s:6:"create";s:9:"signature";s:28:"create(array $payload): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:31;s:7:"endLine";i:74;s:3:"ccn";i:8;}s:14:"getRemoteTrace";a:6:{s:10:"methodName";s:14:"getRemoteTrace";s:9:"signature";s:23:"getRemoteTrace(): array";s:10:"visibility";s:6:"public";s:9:"startLine";i:79;s:7:"endLine";i:82;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:84;s:18:"commentLinesOfCode";i:23;s:21:"nonCommentLinesOfCode";i:61;}s:15:"ignoredLinesFor";a:1:{i:0;i:21;}s:17:"executableLinesIn";a:27:{i:34;i:2;i:35;i:3;i:36;i:4;i:38;i:5;i:39;i:6;i:41;i:7;i:42;i:8;i:45;i:9;i:46;i:10;i:47;i:11;i:48;i:12;i:50;i:13;i:51;i:14;i:52;i:15;i:53;i:15;i:54;i:15;i:55;i:15;i:56;i:15;i:57;i:15;i:59;i:16;i:62;i:17;i:63;i:18;i:67;i:19;i:68;i:20;i:70;i:21;i:73;i:22;i:81;i:23;}} \ No newline at end of file diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results new file mode 100644 index 0000000..43c9400 --- /dev/null +++ b/.phpunit.cache/test-results @@ -0,0 +1 @@ +{"version":2,"defects":[],"times":{"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateReturnsUid":0.013,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateThrowsExceptionOnFailure":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwCallsTransportCorrectly":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testVersionReturnsVersionObject":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testWithLoggerReturnsNewInstance":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testGettersAndToString":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfig":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateThrowsExceptionOnMissingParam":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsn":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsnWithMissingPartsThrowsException":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsnWithUnsupportedSchemeThrowsException":0,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testConstructorAndGetters":0,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testCreateFromPayload":0,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testToStringReturnsName":0,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestReturnsResult":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsTransportExceptionOnFalse":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsRemoteExceptionOnError":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsTransportExceptionOnInvalidJson":0,"Ang3\\Component\\Odoo\\Tests\\Transport\\Client\\JsonRpcHttpClientTest::testPostReturnsResponse":0.001}} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..80b3df8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM php:8.2-cli-alpine + +# Install system packages for compilation +RUN apk add --no-cache \ + bash \ + git \ + unzip \ + curl \ + autoconf \ + make \ + gcc \ + g++ \ + musl-dev \ + icu-dev \ + zlib-dev \ + libzip-dev \ + oniguruma-dev \ + linux-headers + +# Install PHP extensions +RUN docker-php-ext-install intl zip + +# Install PCOV +RUN pecl install pcov \ + && docker-php-ext-enable pcov \ + && echo "pcov.enabled=1" >> /usr/local/etc/php/conf.d/pcov.ini \ + && echo "pcov.directory=/app/src" >> /usr/local/etc/php/conf.d/pcov.ini + +# Install Composer +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + +WORKDIR /app diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dd6e505 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +SHELL := /bin/bash + +DC := docker compose +SERVICE := php + +.DEFAULT_GOAL := help + +.PHONY: help build up down bin logs + +help: + @echo "Targets:" + @echo " build Build image" + @echo " up Launch the container" + @echo " down Stop and delete the container" + @echo " logs Display logs" + +build: + $(DC) build + +up: + $(DC) up -d + +down: + $(DC) down --remove-orphans + +logs: + $(DC) logs -f $(SERVICE) \ No newline at end of file diff --git a/bin/check-code b/bin/check-code new file mode 100755 index 0000000..58888f5 --- /dev/null +++ b/bin/check-code @@ -0,0 +1,5 @@ +#!/bin/bash +set -uoe pipefail + +echo -e "\033[33;1mChecking source code\033[0m" +vendor/bin/phpstan analyse src --memory-limit=2G "$@" diff --git a/bin/fix-code b/bin/fix-code new file mode 100755 index 0000000..1627f74 --- /dev/null +++ b/bin/fix-code @@ -0,0 +1,9 @@ +#!/bin/bash +set -uoe pipefail + +echo -e "\033[33;1mFixing code in '/src'\033[0m" +vendor/bin/php-cs-fixer fix src "$@" + +echo +echo -e "\033[33;1mFixing code in '/tests'\033[0m" +vendor/bin/php-cs-fixer fix tests "$@" diff --git a/bin/test b/bin/test new file mode 100755 index 0000000..a1469a5 --- /dev/null +++ b/bin/test @@ -0,0 +1,5 @@ +#!/bin/bash +set -uoe pipefail + +echo -e "\033[33;1mRunning PHPUnit\033[0m" +vendor/bin/phpunit "$@" diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..2edbe63 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,9 @@ +services: + php: + build: + context: . + dockerfile: Dockerfile + volumes: + - ./:/app + working_dir: /app + tty: true \ No newline at end of file diff --git a/composer.json b/composer.json index 010b150..5725772 100644 --- a/composer.json +++ b/composer.json @@ -17,13 +17,15 @@ } }, "require": { - "php": ">=8.1", + "php": ">=8.2", "ext-json": "*", - "psr/log": "^1.1|^2.0|^3.0" + "psr/log": "^1.1|^2.0|^3.0", + "nyholm/dsn": "^2.0" }, "require-dev": { "symfony/test-pack": "^1.0", "symfony/var-dumper": "^6.3", - "fakerphp/faker": "^1.23" + "phpstan/phpstan": "^2.1", + "friendsofphp/php-cs-fixer": "^3.92" } } diff --git a/composer.lock b/composer.lock index 90721c3..0510b5b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,81 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "292357a4a55eb972941159d021f33f33", + "content-hash": "fcf1f081bffa7a2d8116b14b231003d7", "packages": [ + { + "name": "nyholm/dsn", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/dsn.git", + "reference": "9445621b426bac8c0ca161db8cd700da00a4e618" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/dsn/zipball/9445621b426bac8c0ca161db8cd700da00a4e618", + "reference": "9445621b426bac8c0ca161db8cd700da00a4e618", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "symfony/phpunit-bridge": "^5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Nyholm\\Dsn\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + } + ], + "description": "Parse your DSN strings in a powerful and flexible way", + "homepage": "http://tnyholm.se", + "keywords": [ + "database", + "dsn", + "dsn parser", + "parser" + ], + "support": { + "issues": "https://github.com/Nyholm/dsn/issues", + "source": "https://github.com/Nyholm/dsn/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/Nyholm", + "type": "github" + } + ], + "time": "2021-11-18T09:23:29+00:00" + }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -52,43 +113,38 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" } ], "packages-dev": [ { - "name": "doctrine/instantiator", - "version": "2.0.0", + "name": "clue/ndjson-react", + "version": "v1.3.0", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + "url": "https://github.com/clue/reactphp-ndjson.git", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", "shasum": "" }, "require": { - "php": "^8.1" + "php": ">=5.3", + "react/stream": "^1.2" }, "require-dev": { - "doctrine/coding-standard": "^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + "Clue\\React\\NDJson\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -97,82 +153,75 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" + "name": "Christian Lück", + "email": "christian@clue.engineering" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", + "homepage": "https://github.com/clue/reactphp-ndjson", "keywords": [ - "constructor", - "instantiate" + "NDJSON", + "json", + "jsonlines", + "newline", + "reactphp", + "streaming" ], "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + "issues": "https://github.com/clue/reactphp-ndjson/issues", + "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" }, "funding": [ { - "url": "https://www.doctrine-project.org/sponsorship.html", + "url": "https://clue.engineering/support", "type": "custom" }, { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" + "url": "https://github.com/clue", + "type": "github" } ], - "time": "2022-12-30T00:23:10+00:00" + "time": "2022-12-23T10:58:28+00:00" }, { - "name": "fakerphp/faker", - "version": "v1.23.0", + "name": "composer/pcre", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/FakerPHP/Faker.git", - "reference": "e3daa170d00fde61ea7719ef47bb09bb8f1d9b01" + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e3daa170d00fde61ea7719ef47bb09bb8f1d9b01", - "reference": "e3daa170d00fde61ea7719ef47bb09bb8f1d9b01", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0", - "psr/container": "^1.0 || ^2.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" + "php": "^7.4 || ^8.0" }, "conflict": { - "fzaninotto/faker": "*" + "phpstan/phpstan": "<1.11.10" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "doctrine/persistence": "^1.3 || ^2.0", - "ext-intl": "*", - "phpunit/phpunit": "^9.5.26", - "symfony/phpunit-bridge": "^5.4.16" - }, - "suggest": { - "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", - "ext-curl": "Required by Faker\\Provider\\Image to download images.", - "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", - "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", - "ext-mbstring": "Required for multibyte Unicode string functionality." + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" }, "type": "library", "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, "branch-alias": { - "dev-main": "v1.21-dev" + "dev-main": "3.x-dev" } }, "autoload": { "psr-4": { - "Faker\\": "src/Faker/" + "Composer\\Pcre\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -181,51 +230,68 @@ ], "authors": [ { - "name": "François Zaninotto" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Faker is a PHP library that generates fake data for you.", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", "keywords": [ - "data", - "faker", - "fixtures" + "PCRE", + "preg", + "regex", + "regular expression" ], "support": { - "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.23.0" + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" }, - "time": "2023-06-12T08:44:38+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" }, { - "name": "masterminds/html5", - "version": "2.8.1", + "name": "composer/semver", + "version": "3.4.4", "source": { "type": "git", - "url": "https://github.com/Masterminds/html5-php.git", - "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf" + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", - "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { - "ext-dom": "*", - "php": ">=5.3.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-main": "3.x-dev" } }, "autoload": { "psr-4": { - "Masterminds\\": "src" + "Composer\\Semver\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -234,433 +300,487 @@ ], "authors": [ { - "name": "Matt Butcher", - "email": "technosophos@gmail.com" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" }, { - "name": "Matt Farina", - "email": "matt@mattfarina.com" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" }, { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "An HTML5 parser and serializer.", - "homepage": "http://masterminds.github.io/html5-php", + "description": "Semver library that offers utilities, version constraint parsing and validation.", "keywords": [ - "HTML5", - "dom", - "html", - "parser", - "querypath", - "serializer", - "xml" + "semantic", + "semver", + "validation", + "versioning" ], "support": { - "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.8.1" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" }, - "time": "2023-05-10T11:58:31+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T19:15:30+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.11.1", + "name": "composer/xdebug-handler", + "version": "3.0.5", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], "psr-4": { - "DeepCopy\\": "src/DeepCopy/" + "Composer\\XdebugHandler\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "Xdebug", + "performance" ], "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2024-05-06T16:37:16+00:00" }, { - "name": "nikic/php-parser", - "version": "v4.17.1", + "name": "evenement/evenement", + "version": "v3.0.2", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", "shasum": "" }, "require": { - "ext-tokenizer": "*", "php": ">=7.0" }, "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9 || ^6" }, - "bin": [ - "bin/php-parse" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, "autoload": { "psr-4": { - "PhpParser\\": "lib/PhpParser" + "Evenement\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Nikita Popov" + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" } ], - "description": "A PHP parser written in PHP", + "description": "Événement is a very simple event dispatching library for PHP", "keywords": [ - "parser", - "php" + "event-dispatcher", + "event-emitter" ], "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" }, - "time": "2023-08-13T19:53:39+00:00" + "time": "2023-08-08T05:53:35+00:00" }, { - "name": "phar-io/manifest", - "version": "2.0.3", + "name": "fidry/cpu-core-counter", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", "php": "^7.2 || ^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" }, + "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" } ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-08-14T07:29:31+00:00" }, { - "name": "phar-io/version", - "version": "3.2.1", + "name": "friendsofphp/php-cs-fixer", + "version": "v3.92.5", "source": { "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58", + "reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "clue/ndjson-react": "^1.3", + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.5", + "ext-filter": "*", + "ext-hash": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "fidry/cpu-core-counter": "^1.3", + "php": "^7.4 || ^8.0", + "react/child-process": "^0.6.6", + "react/event-loop": "^1.5", + "react/socket": "^1.16", + "react/stream": "^1.4", + "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", + "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.33", + "symfony/polyfill-php80": "^1.33", + "symfony/polyfill-php81": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0", + "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" }, - "type": "library", + "require-dev": { + "facile-it/paraunit": "^1.3.1 || ^2.7", + "infection/infection": "^0.31", + "justinrainbow/json-schema": "^6.6", + "keradus/cli-executor": "^2.3", + "mikey179/vfsstream": "^1.6.12", + "php-coveralls/php-coveralls": "^2.9", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", + "phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.46", + "symfony/polyfill-php85": "^1.33", + "symfony/var-dumper": "^5.4.48 || ^6.4.26 || ^7.4.0 || ^8.0", + "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", "autoload": { - "classmap": [ - "src/" + "psr-4": { + "PhpCsFixer\\": "src/" + }, + "exclude-from-classmap": [ + "src/**/Internal/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" } ], - "description": "Library for handling version information and constraints", + "description": "A tool to automatically fix PHP code style", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.5" }, - "time": "2022-02-21T01:04:05+00:00" + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2026-01-08T21:57:37+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "9.2.29", + "name": "masterminds/html5", + "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "fcf91eb64359852f00d921887b219479b4f21251" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", "shasum": "" }, "require": { "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-master": "2.7-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Masterminds\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", "keywords": [ - "coverage", - "testing", - "xunit" + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-09-19T04:57:46+00:00" + "time": "2025-07-25T09:04:22+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "name": "myclabs/deep-copy", + "version": "1.13.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { - "php": ">=7.3" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } + "MIT" ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "description": "Create deep copies (clones) of your objects", "keywords": [ - "filesystem", - "iterator" + "clone", + "copy", + "duplicate", + "object", + "object graph" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { - "name": "phpunit/php-invoker", - "version": "3.1.1", + "name": "nikic/php-parser", + "version": "v5.7.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { - "php": ">=7.3" + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" }, "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" }, + "bin": [ + "bin/php-parse" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "5.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -668,52 +788,46 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Nikita Popov" } ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "description": "A PHP parser written in PHP", "keywords": [ - "process" + "parser", + "php" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { - "name": "phpunit/php-text-template", + "name": "phar-io/manifest", "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -726,55 +840,53 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", - "role": "lead" + "role": "Developer" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/theseer", "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2024-03-03T12:33:53+00:00" }, { - "name": "phpunit/php-timer", - "version": "5.0.3", + "name": "phar-io/version", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "php": "^7.2 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, "autoload": { "classmap": [ "src/" @@ -785,89 +897,125 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", - "role": "lead" + "role": "Developer" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.1.33", + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f", + "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", "keywords": [ - "timer" + "dev", + "static analysis" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2025-12-05T10:24:31+00:00" }, { - "name": "phpunit/phpunit", - "version": "9.6.13", + "name": "phpunit/php-code-coverage", + "version": "11.0.12", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f3d767f7f9e191eab4189abe41ab37797e30b1be", - "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", - "ext-json": "*", "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", - "sebastian/version": "^3.0.2" + "nikic/php-parser": "^5.7.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.1", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.3.1" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.46" }, "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-pcov": "PHP extension that provides line coverage", "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "9.6-dev" + "dev-main": "11.0.x-dev" } }, "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], "classmap": [ "src/" ] @@ -883,111 +1031,62 @@ "role": "lead" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ - "phpunit", + "coverage", "testing", "xunit" ], "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.13" + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12" }, "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, { "url": "https://github.com/sebastianbergmann", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", "type": "tidelift" } ], - "time": "2023-09-19T05:39:22+00:00" + "time": "2025-12-24T07:01:01+00:00" }, { - "name": "psr/container", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" - }, - "time": "2021-11-05T16:47:00+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", + "name": "phpunit/php-file-iterator", + "version": "5.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -1006,11 +1105,16 @@ "role": "lead" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" }, "funding": [ { @@ -1018,32 +1122,36 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-08-27T05:02:59+00:00" }, { - "name": "sebastian/code-unit", - "version": "1.0.8", + "name": "phpunit/php-invoker", + "version": "5.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -1062,11 +1170,15 @@ "role": "lead" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" }, "funding": [ { @@ -1074,32 +1186,32 @@ "type": "github" } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2024-07-03T05:07:44+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "name": "phpunit/php-text-template", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -1114,14 +1226,19 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" }, "funding": [ { @@ -1129,34 +1246,32 @@ "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2024-07-03T05:08:43+00:00" }, { - "name": "sebastian/comparator", - "version": "4.0.8", + "name": "phpunit/php-timer", + "version": "7.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -1171,31 +1286,19 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "comparator", - "compare", - "equality" + "timer" ], "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" }, "funding": [ { @@ -1203,36 +1306,66 @@ "type": "github" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2024-07-03T05:09:35+00:00" }, { - "name": "sebastian/complexity", - "version": "2.0.2", + "name": "phpunit/phpunit", + "version": "11.5.47", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "a8c3c540923f8a3d499659b927228059bb3809d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a8c3c540923f8a3d499659b927228059bb3809d8", + "reference": "a8c3c540923f8a3d499659b927228059bb3809d8", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.12", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.2", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.2", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" }, + "bin": [ + "phpunit" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "11.5-dev" } }, "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], "classmap": [ "src/" ] @@ -1248,483 +1381,695 @@ "role": "lead" } ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.47" }, "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2026-01-15T12:00:46+00:00" }, { - "name": "sebastian/diff", - "version": "4.0.5", + "name": "psr/container", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "php": ">=7.4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Container\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" ], "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-07T05:35:17+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { - "name": "sebastian/environment", - "version": "5.1.5", + "name": "psr/event-dispatcher", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" + "php": ">=7.2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "description": "Standard interfaces for event handling.", "keywords": [ - "Xdebug", - "environment", - "hhvm" + "events", + "psr", + "psr-14" ], "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2019-01-08T18:20:26+00:00" }, { - "name": "sebastian/exporter", - "version": "4.0.5", + "name": "react/cache", + "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } + ], + "time": "2022-11-30T15:59:55+00:00" + }, + { + "name": "react/child-process", + "version": "v0.6.7", + "source": { + "type": "git", + "url": "https://github.com/reactphp/child-process.git", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3" }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/970f0e71945556422ee4570ccbabaedc3cf04ad3", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/socket": "^1.16", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" + }, + "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\ChildProcess\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" }, { - "name": "Volker Dusch", - "email": "github@wallbash.com" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven library for executing child processes with ReactPHP.", + "keywords": [ + "event-driven", + "process", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.7" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-12-23T15:25:20+00:00" + }, + { + "name": "react/dns", + "version": "v1.14.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", + "description": "Async DNS resolver for ReactPHP", "keywords": [ - "export", - "exporter" + "async", + "dns", + "dns-resolver", + "reactphp" ], "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.14.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2025-11-18T19:34:28+00:00" }, { - "name": "sebastian/global-state", - "version": "5.0.6", + "name": "react/event-loop", + "version": "v1.6.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" + "url": "https://github.com/reactphp/event-loop.git", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=5.3.0" }, "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "suggest": { - "ext-uopz": "*" + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\EventLoop\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", "keywords": [ - "global state" + "asynchronous", + "event-loop" ], "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2023-08-02T09:26:13+00:00" + "time": "2025-11-17T20:46:25+00:00" }, { - "name": "sebastian/lines-of-code", - "version": "1.0.3", + "name": "react/promise", + "version": "v3.3.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "url": "https://github.com/reactphp/promise.git", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" + "php": ">=7.1.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpstan/phpstan": "1.12.28 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.3.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2025-08-19T18:57:03+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "4.0.4", + "name": "react/socket", + "version": "v1.17.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "url": "https://github.com/reactphp/socket.git", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\Socket\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.17.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2025-11-19T20:47:34+00:00" }, { - "name": "sebastian/object-reflector", - "version": "2.0.4", + "name": "react/stream", + "version": "v1.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", "shasum": "" }, "require": { - "php": ">=7.3" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\Stream\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2024-06-11T12:45:25+00:00" }, { - "name": "sebastian/recursion-context", - "version": "4.0.5", + "name": "sebastian/cli-parser", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -1739,22 +2084,16 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" }, "funding": [ { @@ -1762,32 +2101,32 @@ "type": "github" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2024-07-03T04:41:36+00:00" }, { - "name": "sebastian/resource-operations", + "name": "sebastian/code-unit", "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^11.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -1802,14 +2141,16 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" }, "funding": [ { @@ -1817,32 +2158,181 @@ "type": "github" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2025-03-19T07:56:08+00:00" }, { - "name": "sebastian/type", - "version": "3.2.1", + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.4" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2025-08-10T08:07:46+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" } }, "autoload": { @@ -1861,104 +2351,1856 @@ "role": "lead" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2025-05-21T11:55:47+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:12:51+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:58:38+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:00:13+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:01:32+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:42:22+00:00" + }, + { + "name": "sebastian/type", + "version": "5.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:55:48+00:00" + }, + { + "name": "sebastian/version", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T05:16:32+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "symfony/browser-kit", + "version": "v7.4.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "d5b5c731005f224fbc25289587a8538e4f62c762" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/d5b5c731005f224fbc25289587a8538e4f62c762", + "reference": "d5b5c731005f224fbc25289587a8538e4f62c762", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/dom-crawler": "^6.4|^7.0|^8.0" + }, + "require-dev": { + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/browser-kit/tree/v7.4.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-12-16T08:02:06+00:00" + }, + { + "name": "symfony/console", + "version": "v7.4.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/732a9ca6cd9dfd940c639062d5edbde2f6727fb6", + "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.4.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-12-23T14:50:43+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "ab862f478513e7ca2fe9ec117a6f01a8da6e1135" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/ab862f478513e7ca2fe9ec117a6f01a8da6e1135", + "reference": "ab862f478513e7ca2fe9ec117a6f01a8da6e1135", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-10-30T13:39:42+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "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.6.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-09-25T14:21:43+00:00" + }, + { + "name": "symfony/dom-crawler", + "version": "v7.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "0c5e8f20c74c78172a8ee72b125909b505033597" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/0c5e8f20c74c78172a8ee72b125909b505033597", + "reference": "0c5e8f20c74c78172a8ee72b125909b505033597", + "shasum": "" + }, + "require": { + "masterminds/html5": "^2.6", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v7.4.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-12-06T15:47:47+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-10-28T09:38:46+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "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": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.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-09-25T14:21:43+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "d551b38811096d0be9c4691d406991b47c0c630a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a", + "reference": "d551b38811096d0be9c4691d406991b47c0c630a", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-11-27T13:27:24+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.4.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "fffe05569336549b20a1be64250b40516d6e8d06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/fffe05569336549b20a1be64250b40516d6e8d06", + "reference": "fffe05569336549b20a1be64250b40516d6e8d06", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.4.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-12-23T14:50:43+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "b38026df55197f9e39a44f3215788edf83187b80" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b38026df55197f9e39a44f3215788edf83187b80", + "reference": "b38026df55197f9e39a44f3215788edf83187b80", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v7.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-11-12T15:39:26+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "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": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-27T09:58:17+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-02-03T06:13:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "sebastian/version", - "version": "3.0.2", + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { - "php": ">=7.3" + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.0-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { - "name": "symfony/browser-kit", - "version": "v6.3.8", + "name": "symfony/polyfill-php80", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "e270297dbee59168274c2b535ab1bccd593e6ffe" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/e270297dbee59168274c2b535ab1bccd593e6ffe", - "reference": "e270297dbee59168274c2b535ab1bccd593e6ffe", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/dom-crawler": "^5.4|^6.0" - }, - "require-dev": { - "symfony/css-selector": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/mime": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\BrowserKit\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1967,18 +4209,28 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/browser-kit/tree/v6.3.8" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -1989,37 +4241,50 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-10-31T08:07:48+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { - "name": "symfony/css-selector", - "version": "v6.3.2", + "name": "symfony/polyfill-php81", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "883d961421ab1709877c10ac99451632a3d6fa57" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57", - "reference": "883d961421ab1709877c10ac99451632a3d6fa57", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\CssSelector\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2028,22 +4293,24 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Converts CSS selectors to XPath expressions", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.3.2" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" }, "funding": [ { @@ -2054,43 +4321,50 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-07-12T16:00:22+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/deprecation-contracts", - "version": "v3.4.0", + "name": "symfony/polyfill-php84", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { "files": [ - "function.php" + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2107,10 +4381,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "A generic function and convention to trigger deprecation notices", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" }, "funding": [ { @@ -2121,40 +4401,38 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2025-06-24T13:30:11+00:00" }, { - "name": "symfony/dom-crawler", - "version": "v6.3.4", + "name": "symfony/process", + "version": "v7.4.3", "source": { "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "3fdd2a3d5fdc363b2e8dbf817f9726a4d013cbd1" + "url": "https://github.com/symfony/process.git", + "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/3fdd2a3d5fdc363b2e8dbf817f9726a4d013cbd1", - "reference": "3fdd2a3d5fdc363b2e8dbf817f9726a4d013cbd1", + "url": "https://api.github.com/repos/symfony/process/zipball/2f8e1a6cdf590ca63715da4d3a7a3327404a523f", + "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f", "shasum": "" }, "require": { - "masterminds/html5": "^2.6", - "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "symfony/css-selector": "^5.4|^6.0" + "php": ">=8.2" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" + "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2174,10 +4452,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Eases DOM navigation for HTML and XML documents", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v6.3.4" + "source": "https://github.com/symfony/process/tree/v7.4.3" }, "funding": [ { @@ -2188,57 +4466,55 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-08-01T07:43:40+00:00" + "time": "2025-12-19T10:00:43+00:00" }, { - "name": "symfony/phpunit-bridge", - "version": "v6.3.8", + "name": "symfony/service-contracts", + "version": "v3.6.1", "source": { "type": "git", - "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "45610900872a35b77db7698651f36129906041ea" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/45610900872a35b77db7698651f36129906041ea", - "reference": "45610900872a35b77db7698651f36129906041ea", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { - "php": ">=7.1.3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { - "phpunit/phpunit": "<7.5|9.1.2" - }, - "require-dev": { - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/error-handler": "^5.4|^6.0", - "symfony/polyfill-php81": "^1.27" + "ext-psr": "<1.1|>=2" }, - "bin": [ - "bin/simple-phpunit" - ], - "type": "symfony-bridge", + "type": "library", "extra": { "thanks": { - "name": "phpunit/phpunit", - "url": "https://github.com/sebastianbergmann/phpunit" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Bridge\\PhpUnit\\": "" + "Symfony\\Contracts\\Service\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2255,10 +4531,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides utilities for PHPUnit, especially user deprecation notices management", + "description": "Generic abstractions related to writing services", "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v6.3.8" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -2269,53 +4553,43 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-10-31T08:07:48+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.28.0", + "name": "symfony/stopwatch", + "version": "v7.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + "url": "https://github.com/symfony/stopwatch.git", + "reference": "8a24af0a2e8a872fb745047180649b8418303084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/8a24af0a2e8a872fb745047180649b8418303084", + "reference": "8a24af0a2e8a872fb745047180649b8418303084", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2323,24 +4597,18 @@ ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Provides a way to profile code", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" + "source": "https://github.com/symfony/stopwatch/tree/v7.4.0" }, "funding": [ { @@ -2351,53 +4619,60 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2025-08-04T07:05:15+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", + "name": "symfony/string", + "version": "v7.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" + "url": "https://github.com/symfony/string.git", + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", + "url": "https://api.github.com/repos/symfony/string/zipball/d50e862cb0a0e0886f73ca1f31b865efbb795003", + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.33", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" }, - "provide": { - "ext-mbstring": "*" + "conflict": { + "symfony/translation-contracts": "<2.5" }, - "suggest": { - "ext-mbstring": "For best performance" + "require-dev": { + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, "autoload": { "files": [ - "bootstrap.php" + "Resources/functions.php" ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2413,17 +4688,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + "source": "https://github.com/symfony/string/tree/v7.4.0" }, "funding": [ { @@ -2434,38 +4710,43 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-07-28T09:04:16+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { "name": "symfony/test-pack", - "version": "v1.1.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/symfony/test-pack.git", - "reference": "7b708c588cb3e1c8f0281889f273b920cbddff9b" + "reference": "88285bd1b12ec408c09fd6abac0ba290ff81fd04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/test-pack/zipball/7b708c588cb3e1c8f0281889f273b920cbddff9b", - "reference": "7b708c588cb3e1c8f0281889f273b920cbddff9b", + "url": "https://api.github.com/repos/symfony/test-pack/zipball/88285bd1b12ec408c09fd6abac0ba290ff81fd04", + "reference": "88285bd1b12ec408c09fd6abac0ba290ff81fd04", "shasum": "" }, "require": { - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "*", "symfony/browser-kit": "*", - "symfony/css-selector": "*", - "symfony/phpunit-bridge": "*" + "symfony/css-selector": "*" + }, + "conflict": { + "phpunit/phpunit": "<11.1" }, "require-dev": { - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "*", "symfony/browser-kit": "*", - "symfony/css-selector": "*", - "symfony/phpunit-bridge": "*" + "symfony/css-selector": "*" }, "type": "symfony-pack", "notification-url": "https://packagist.org/downloads/", @@ -2475,7 +4756,7 @@ "description": "A pack for functional and end-to-end testing within a Symfony app", "support": { "issues": "https://github.com/symfony/test-pack/issues", - "source": "https://github.com/symfony/test-pack/tree/v1.1.0" + "source": "https://github.com/symfony/test-pack/tree/v1.2.0" }, "funding": [ { @@ -2491,20 +4772,20 @@ "type": "tidelift" } ], - "time": "2023-02-06T16:00:54+00:00" + "time": "2025-05-20T15:03:19+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.3.8", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "81acabba9046550e89634876ca64bfcd3c06aa0a" + "reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/81acabba9046550e89634876ca64bfcd3c06aa0a", - "reference": "81acabba9046550e89634876ca64bfcd3c06aa0a", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfae1497a2f1eaad78dbc0590311c599c7178d4a", + "reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a", "shasum": "" }, "require": { @@ -2516,11 +4797,11 @@ "symfony/console": "<5.4" }, "require-dev": { - "ext-iconv": "*", - "symfony/console": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/uid": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", "twig/twig": "^2.13|^3.0.4" }, "bin": [ @@ -2559,7 +4840,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.3.8" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.26" }, "funding": [ { @@ -2570,25 +4851,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-11-08T10:42:36+00:00" + "time": "2025-09-25T15:37:27+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.2", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -2617,7 +4902,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.2" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -2625,18 +4910,18 @@ "type": "github" } ], - "time": "2023-11-20T00:12:19+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.1", + "php": ">=8.2", "ext-json": "*" }, - "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform-dev": {}, + "plugin-api-version": "2.9.0" } diff --git a/docker/logs b/docker/logs new file mode 100755 index 0000000..013f26c --- /dev/null +++ b/docker/logs @@ -0,0 +1,5 @@ +#!/bin/bash +set -uoe pipefail + +echo -e "\033[33;1mChecking logs for PHP container\033[0m" +docker compose -f compose.yaml logs php diff --git a/docker/php b/docker/php new file mode 100755 index 0000000..37071a3 --- /dev/null +++ b/docker/php @@ -0,0 +1,5 @@ +#!/bin/bash +set -uoe pipefail + +echo -e "\033[33;1mExecuting bash inside PHP container ('exit' to quit)\033[0m" +docker compose -f compose.yaml exec php bash diff --git a/phpstan.neon b/phpstan.neon index cd4c474..089aeed 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,9 +1,5 @@ parameters: - inferPrivatePropertyTypeFromConstructor: true - checkGenericClassInNonGenericObjectType: false - checkMissingIterableValueType: false - treatPhpDocTypesAsCertain: false - paths: - - %currentWorkingDirectory%/src - ignoreErrors: - - '#Cannot cast mixed to int\.#' \ No newline at end of file + level: max + paths: + - src/ + treatPhpDocTypesAsCertain: false diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 421dd9e..bebbda7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,11 +2,12 @@ @@ -19,14 +20,20 @@ - - - ./src - ./vendor - - ./tests - ./vendor - - - + + + src + + + + + + + + + diff --git a/src/Client.php b/src/Client.php index 7952ab8..6aa500c 100644 --- a/src/Client.php +++ b/src/Client.php @@ -14,11 +14,10 @@ use Ang3\Component\Odoo\Enum\OdooRpcMethod; use Ang3\Component\Odoo\Enum\OdooRpcService; use Ang3\Component\Odoo\Exception\AuthenticationException; -use Ang3\Component\Odoo\Exception\ConnectionException; use Ang3\Component\Odoo\Exception\RequestException; use Ang3\Component\Odoo\Exception\TransportException; use Ang3\Component\Odoo\Metadata\Version; -use Ang3\Component\Odoo\Transport\JsonRpcPhpStreamTransport; +use Ang3\Component\Odoo\Transport\JsonRpcTransport; use Ang3\Component\Odoo\Transport\TransportInterface; use Psr\Log\LoggerInterface; @@ -32,25 +31,16 @@ class Client public function __construct( private readonly Connection $connection, - TransportInterface $transport = null, - private ?LoggerInterface $logger = null + ?TransportInterface $transport = null, + private readonly ?LoggerInterface $logger = null, ) { - $this->transport = $transport ?: new JsonRpcPhpStreamTransport($this->connection); + $this->transport = $transport ?: new JsonRpcTransport($this->connection); } /** - * Create a new client instance from a DSN. - * DSN format: odoo://:@/. - * - * @static - * - * @throws ConnectionException on invalid DSN + * @param mixed[] $parameters + * @param mixed[] $options */ - public static function create(string $dsn, TransportInterface $transport = null, LoggerInterface $logger = null): self - { - return new self(Connection::parseDsn($dsn), $transport, $logger); - } - public function executeKw(string $name, string $method, array $parameters = [], array $options = []): mixed { return $this->request( @@ -77,7 +67,8 @@ public function version(): Version public function authenticate(): int { if (null === $this->uid) { - $this->uid = (int) $this->request( + /** @var int|null $uid */ + $uid = $this->request( OdooRpcService::Common->value, OdooRpcMethod::Login->value, $this->connection->getDatabase(), @@ -85,9 +76,11 @@ public function authenticate(): int $this->connection->getPassword() ); - if (!$this->uid) { + if (!$uid) { throw new AuthenticationException(); } + + $this->uid = $uid; } return $this->uid; @@ -132,15 +125,9 @@ public function getTransport(): TransportInterface return $this->transport; } - public function setTransport(TransportInterface $transport): self + public function withTransport(TransportInterface $transport): self { - if ($transport !== $this->transport) { - $this->uid = null; - } - - $this->transport = $transport; - - return $this; + return new self($this->connection, $transport, $this->logger); } public function getLogger(): ?LoggerInterface @@ -148,11 +135,9 @@ public function getLogger(): ?LoggerInterface return $this->logger; } - public function setLogger(?LoggerInterface $logger): self + public function withLogger(LoggerInterface $logger): self { - $this->logger = $logger; - - return $this; + return new self($this->connection, $this->transport, $logger); } public function getUid(): ?int diff --git a/src/Connection.php b/src/Connection.php index f73b7bb..98edf59 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -11,86 +11,99 @@ namespace Ang3\Component\Odoo; -use Ang3\Component\Odoo\Exception\ConnectionException; +use Nyholm\Dsn\DsnParser; +use Nyholm\Dsn\Exception\InvalidDsnException; -class Connection +/** + * @author Joanis ROUANET + */ +readonly class Connection { public function __construct( - private readonly string $host, - private readonly string $username, - private readonly string $password, - private readonly string $database, - private readonly string $scheme = 'https' - ) {} + private string $host, + private string $username, + private string $password, + private string $database, + private string $scheme = 'https', + ) { + } public function __toString(): string { - return sprintf('%s://%s:%s@%s/%s', $this->scheme, $this->username, urlencode($this->password), $this->host, $this->database); + return \sprintf('%s://%s:%s@%s/%s', $this->scheme, $this->username, urlencode($this->password), $this->host, $this->database); } + /** + * @param array $config + * + * @throws \InvalidArgumentException on invalid config + */ public static function create(array $config): self { - $getParam = static function ($config, $paramName) { + $getParam = static function (string $paramName) use ($config): string { $value = $config[$paramName] ?? null; if (null === $value) { - throw new ConnectionException(sprintf('Missing configuration parameter "%s".', $paramName)); + throw new \InvalidArgumentException(\sprintf('Missing configuration parameter "%s".', $paramName)); + } + + if (!\is_string($value)) { + throw new \InvalidArgumentException(\sprintf('The parameter "%s" should be a string, got "%s".', $paramName, \gettype($value))); } return $value; }; return new self( - $getParam($config, 'host'), - $getParam($config, 'username'), - $getParam($config, 'password'), - $getParam($config, 'database'), + $getParam('host'), + $getParam('username'), + $getParam('password'), + $getParam('database'), $config['scheme'] ?? 'https', ); } /** - * @throws ConnectionException on invalid DSN + * @throws \InvalidArgumentException on invalid config */ public static function parseDsn(string $dsn): self { - /** @var array|false $parsedUrl */ - $parsedUrl = parse_url($dsn); - - if (!\is_array($parsedUrl) && $parsedUrl) { - throw ConnectionException::invalidDsn($dsn); + try { + $dsn = DsnParser::parse($dsn); + } catch (InvalidDsnException $e) { + throw new \InvalidArgumentException('Invalid DSN', 0, $e); } [$scheme, $host, $user, $password, $path] = [ - $parsedUrl['scheme'] ?? null, - $parsedUrl['host'] ?? null, - $parsedUrl['user'] ?? null, - $parsedUrl['pass'] ?? null, - $parsedUrl['path'] ?? null, + $dsn->getScheme(), + $dsn->getHost(), + $dsn->getUser(), + $dsn->getPassword(), + $dsn->getPath(), ]; if (!$scheme) { - throw ConnectionException::invalidDsn($dsn, 'Missing scheme.'); + throw new \InvalidArgumentException('Missing DSN scheme.'); } if (!\in_array($scheme, ['http', 'https'], true)) { - throw ConnectionException::invalidDsn($dsn, sprintf('The scheme "%s" is not supported (supported: "http" or "https").', $scheme)); + throw new \InvalidArgumentException(\sprintf('The DSN scheme "%s" is not supported (supported: "http" or "https").', $scheme)); } if (!$host) { - throw ConnectionException::invalidDsn($dsn, 'Missing host.'); + throw new \InvalidArgumentException('Missing DSN host.'); } if (!$user) { - throw ConnectionException::invalidDsn($dsn, 'Missing username.'); + throw new \InvalidArgumentException('Missing DSN username.'); } if (!$password) { - throw ConnectionException::invalidDsn($dsn, 'Missing user password.'); + throw new \InvalidArgumentException('Missing DSN user password.'); } if (!$path) { - throw ConnectionException::invalidDsn($dsn, 'Missing path.'); + throw new \InvalidArgumentException('Missing DSN path.'); } $database = str_starts_with($path, '/') ? substr($path, 1) : $path; @@ -103,7 +116,7 @@ public static function parseDsn(string $dsn): self */ public function getIdentifier(): string { - return sha1(sprintf('%s.%s.%s', $this->host, $this->database, $this->username)); + return sha1(\sprintf('%s.%s.%s', $this->host, $this->database, $this->username)); } public function getHost(): string @@ -133,6 +146,6 @@ public function getScheme(): string public function getUrl(): string { - return sprintf('%s://%s', $this->scheme, $this->host); + return \sprintf('%s://%s', $this->scheme, $this->host); } } diff --git a/src/Enum/OdooRpcMethod.php b/src/Enum/OdooRpcMethod.php index 174135a..6099f4e 100644 --- a/src/Enum/OdooRpcMethod.php +++ b/src/Enum/OdooRpcMethod.php @@ -11,6 +11,11 @@ namespace Ang3\Component\Odoo\Enum; +/** + * @author Joanis ROUANET + * + * @codeCoverageIgnore + */ enum OdooRpcMethod: string { case Login = 'login'; diff --git a/src/Enum/OdooRpcService.php b/src/Enum/OdooRpcService.php index b8069b9..74b0d9f 100644 --- a/src/Enum/OdooRpcService.php +++ b/src/Enum/OdooRpcService.php @@ -11,6 +11,11 @@ namespace Ang3\Component\Odoo\Enum; +/** + * @author Joanis ROUANET + * + * @codeCoverageIgnore + */ enum OdooRpcService: string { case Common = 'common'; diff --git a/src/Exception/AuthenticationException.php b/src/Exception/AuthenticationException.php index f2ed09b..af91f0a 100644 --- a/src/Exception/AuthenticationException.php +++ b/src/Exception/AuthenticationException.php @@ -16,7 +16,7 @@ */ class AuthenticationException extends RequestException { - public function __construct(\Throwable $previous = null) + public function __construct(?\Throwable $previous = null) { parent::__construct('Bad credentials', 0, $previous); } diff --git a/src/Exception/ConnectionException.php b/src/Exception/ConnectionException.php deleted file mode 100644 index 05a8a50..0000000 --- a/src/Exception/ConnectionException.php +++ /dev/null @@ -1,23 +0,0 @@ - - */ -class ConnectionException extends \InvalidArgumentException implements ExceptionInterface -{ - public static function invalidDsn(string $dsn, string $message = null, \Throwable $previous = null): self - { - return new self(sprintf('The DSN "%s" is not valid - %s', $dsn, $message ?: 'Unknown error'), 0, $previous); - } -} diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index a3304e6..e035d7b 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -13,5 +13,9 @@ /** * @author Joanis ROUANET + * + * @codeCoverageIgnore */ -interface ExceptionInterface extends \Throwable {} +interface ExceptionInterface extends \Throwable +{ +} diff --git a/src/Exception/RemoteException.php b/src/Exception/RemoteException.php index c60b61a..37ce5d5 100644 --- a/src/Exception/RemoteException.php +++ b/src/Exception/RemoteException.php @@ -13,13 +13,24 @@ /** * @author Joanis ROUANET + * + * @phpstan-type ErrorArray array{code: int, message: string, data: array{debug: string}} + * @phpstan-type Payload array{error: ErrorArray} + * @phpstan-type RemoteTraceArray array, array{file: string, line: int, method: string, statement: string}> */ class RemoteException extends RequestException { + /** + * @var RemoteTraceArray + */ protected array $remoteTrace = []; + /** + * @param mixed[] $payload + */ public static function create(array $payload): self { + /** @var Payload $payload */ $errorCode = $payload['error']['code'] ?? 0; $errorMessage = $payload['error']['message'] ?? 'Unknown error.'; $remoteTrace = trim($payload['error']['data']['debug']); @@ -62,6 +73,9 @@ public static function create(array $payload): self return new self($errorMessage, $errorCode); } + /** + * @return RemoteTraceArray + */ public function getRemoteTrace(): array { return $this->remoteTrace; diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 50cf65a..204c7dc 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class RequestException extends \RuntimeException implements ExceptionInterface {} +class RequestException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 057e514..9e8bba3 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -14,4 +14,6 @@ /** * @author Joanis ROUANET */ -class TransportException extends \RuntimeException implements ExceptionInterface {} +class TransportException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/src/Metadata/Version.php b/src/Metadata/Version.php index 728879b..f24d189 100644 --- a/src/Metadata/Version.php +++ b/src/Metadata/Version.php @@ -11,26 +11,43 @@ namespace Ang3\Component\Odoo\Metadata; -class Version +/** + * @author Joanis ROUANET + */ +readonly class Version { public function __construct( - private readonly int $majorVersion, - private readonly int $minorVersion, - private readonly int $patchVersion, - private readonly string $buildName, - private readonly string $buildIdentifier, - private readonly string $buildVersion, - private readonly int $protocolVersion - ) {} + private int $majorVersion, + private int $minorVersion, + private int $patchVersion, + private string $buildName, + private string $buildIdentifier, + private string $buildVersion, + private int $protocolVersion, + ) { + } /** * Creates the instance from Odoo response payload. + * + * @param mixed[] $payload */ public static function create(array $payload): self { + /** @var int|string $protocolVersion */ + $protocolVersion = $payload['protocol_version']; + /** @var array{int, int, int, string, string, string} $infos */ $infos = $payload['server_version_info']; - return new self($infos[0], $infos[1], $infos[2], (string) $infos[3], (string) $infos[4], (string) $infos[5], $payload['protocol_version']); + return new self( + (int) $infos[0], + (int) $infos[1], + (int) $infos[2], + (string) $infos[3], + (string) $infos[4], + (string) $infos[5], + (int) $protocolVersion + ); } public function __toString(): string @@ -40,7 +57,7 @@ public function __toString(): string public function getName(): string { - return sprintf('%s.%s.%s+%s', $this->majorVersion, $this->minorVersion, $this->patchVersion, $this->buildVersion); + return \sprintf('%s.%s.%s+%s', $this->majorVersion, $this->minorVersion, $this->patchVersion, $this->buildName); } public function getMajorVersion(): int diff --git a/src/Transport/Client/JsonRpcHttpClient.php b/src/Transport/Client/JsonRpcHttpClient.php new file mode 100644 index 0000000..0753f38 --- /dev/null +++ b/src/Transport/Client/JsonRpcHttpClient.php @@ -0,0 +1,39 @@ +doRequest($url, $payload, $timeout); + } + + /** + * Extracted for testing / mocking. + * + * @codeCoverageIgnore + */ + protected function doRequest(string $url, string $payload, int $timeout): string|false + { + $context = stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'timeout' => $timeout, + 'header' => 'Content-Type: application/json', + 'content' => $payload, + ], + ]); + + return file_get_contents($url, false, $context); + } +} diff --git a/src/Transport/Client/JsonRpcHttpClientInterface.php b/src/Transport/Client/JsonRpcHttpClientInterface.php new file mode 100644 index 0000000..8b8ecc1 --- /dev/null +++ b/src/Transport/Client/JsonRpcHttpClientInterface.php @@ -0,0 +1,20 @@ + * @author Jules Sayer */ -class JsonRpcPhpStreamTransport implements TransportInterface +class JsonRpcTransport implements TransportInterface { - /** - * JSON-RPC endpoint. - */ public const DEFAULT_ENDPOINT = '/jsonrpc'; + private JsonRpcHttpClientInterface $httpClient; + public function __construct( private readonly Connection $connection, - private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT - ) {} + ?JsonRpcHttpClientInterface $httpClient = null, + private readonly int $timeOut = TransportInterface::DEFAULT_TIMEOUT, + ) { + $this->httpClient = $httpClient ?: new JsonRpcHttpClient(); + } + /** + * @param mixed[] $arguments + */ public function request(string $service, string $method, array $arguments = []): mixed { - $payload = json_encode([ + $payload = (string) json_encode([ 'jsonrpc' => '2.0', 'method' => 'call', 'params' => [ @@ -45,29 +52,19 @@ public function request(string $service, string $method, array $arguments = []): ]); if (JSON_ERROR_NONE !== json_last_error()) { - throw new TransportException(sprintf('Failed to encode data to JSON: %s', json_last_error_msg())); + throw new TransportException(\sprintf('Failed to encode data to JSON: %s', json_last_error_msg())); } - $context = stream_context_create([ - 'http' => [ - 'method' => 'POST', - 'timeout' => $this->timeOut, - 'header' => 'Content-Type: application/json', - 'content' => $payload, - ], - ]); - $endpointUrl = $this->connection->getUrl().self::DEFAULT_ENDPOINT; - $response = file_get_contents($endpointUrl, false, $context); + $response = $this->httpClient->post($endpointUrl, $payload, $this->timeOut); if (false === $response) { - throw new TransportException('JSON RPC request failed - Unable to get stream contents.'); + throw new TransportException('JSON RPC request failed - Unable to get response.'); } $data = (array) json_decode($response, true); - if (JSON_ERROR_NONE !== json_last_error()) { - throw new TransportException(sprintf('Failed to decode JSON data: %s', json_last_error_msg())); + throw new TransportException(\sprintf('Failed to decode JSON data: %s', json_last_error_msg())); } if (\is_array($data['error'] ?? null)) { @@ -76,14 +73,4 @@ public function request(string $service, string $method, array $arguments = []): return $data['result'] ?? null; } - - public function getConnection(): Connection - { - return $this->connection; - } - - public function getTimeOut(): int - { - return $this->timeOut; - } } diff --git a/src/Transport/TransportInterface.php b/src/Transport/TransportInterface.php index b5df559..1fbcdae 100644 --- a/src/Transport/TransportInterface.php +++ b/src/Transport/TransportInterface.php @@ -14,9 +14,6 @@ use Ang3\Component\Odoo\Exception\RequestException; use Ang3\Component\Odoo\Exception\TransportException; -/** - * @author Joanis ROUANET - */ interface TransportInterface { public const DEFAULT_TIMEOUT = 120; @@ -24,6 +21,8 @@ interface TransportInterface /** * Make a request to Odoo database. * + * @param mixed[] $arguments + * * @throws RequestException on bad request * @throws TransportException on transport errors */ diff --git a/tests.phpstan.neon b/tests.phpstan.neon deleted file mode 100644 index 91db039..0000000 --- a/tests.phpstan.neon +++ /dev/null @@ -1,9 +0,0 @@ -parameters: - checkMissingIterableValueType: false - checkGenericClassInNonGenericObjectType: false - autoload_files: - - vendor/autoload.php - paths: - - %currentWorkingDirectory%/tests - ignoreErrors: - - '#(.*) expects (.*\|)?class\-string(\||<.*)?, (.*) given#' \ No newline at end of file diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 8abdd6d..70f96eb 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -15,223 +15,129 @@ use Ang3\Component\Odoo\Connection; use Ang3\Component\Odoo\Enum\OdooRpcMethod; use Ang3\Component\Odoo\Enum\OdooRpcService; -use Ang3\Component\Odoo\Exception\RemoteException; +use Ang3\Component\Odoo\Exception\AuthenticationException; use Ang3\Component\Odoo\Metadata\Version; -use Ang3\Component\Odoo\Transport\JsonRpcPhpStreamTransport; use Ang3\Component\Odoo\Transport\TransportInterface; -use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; /** - * @coversDefaultClass \Ang3\Component\Odoo\Client - * * @internal */ +#[CoversClass(Client::class)] final class ClientTest extends TestCase { - private Client $client; - private MockObject $connection; - private MockObject $transport; - private MockObject $logger; + private Connection $connection; + private TransportInterface $transport; + private LoggerInterface $logger; protected function setUp(): void { - parent::setUp(); $this->connection = $this->createMock(Connection::class); $this->transport = $this->createMock(TransportInterface::class); $this->logger = $this->createMock(LoggerInterface::class); - $this->client = new Client($this->connection, $this->transport, $this->logger); - } - - /** - * @covers ::__construct - */ - public function testConstruct(): void - { - $client = new Client($this->connection); - - // Asserting default transport - $clientTransport = $client->getTransport(); - static::assertInstanceOf(JsonRpcPhpStreamTransport::class, $clientTransport); - - // Asserting optional logger - $clientLogger = $client->getLogger(); - static::assertNull($clientLogger); } - /** - * @covers ::__construct - */ - public function testConstructWithCustomTransport(): void + public function testAuthenticateReturnsUid(): void { - $customTransport = $this->createMock(TransportInterface::class); - $client = new Client($this->connection, $customTransport); + $this->connection->method('getDatabase')->willReturn('test_db'); + $this->connection->method('getUsername')->willReturn('admin'); + $this->connection->method('getPassword')->willReturn('secret'); - // Asserting custom transport - $clientTransport = $client->getTransport(); - static::assertInstanceOf(TransportInterface::class, $clientTransport); - static::assertSame($customTransport, $clientTransport); - - // Asserting optional logger - static::assertNull($client->getLogger()); - } - - /** - * @covers ::__construct - */ - public function testConstructWithLogger(): void - { - $logger = $this->createMock(LoggerInterface::class); - $client = new Client($this->connection, null, $logger); + $this->transport + ->method('request') + ->willReturnCallback(static function (string $service, string $method, mixed ...$args) { + if ('common' === $service && 'login' === $method) { + return 42; // UID + } - // Asserting default transport - $clientTransport = $client->getTransport(); - static::assertInstanceOf(JsonRpcPhpStreamTransport::class, $clientTransport); + return null; + }) + ; - // Asserting logger - $clientLogger = $client->getLogger(); - static::assertInstanceOf(LoggerInterface::class, $clientLogger); - static::assertSame($logger, $clientLogger); + $client = new Client($this->connection, $this->transport); + self::assertSame(42, $client->authenticate()); } - public static function provideRequestData(): array + public function testAuthenticateThrowsExceptionOnFailure(): void { - return [ - [OdooRpcService::Common, OdooRpcMethod::Login], - [OdooRpcService::Common, OdooRpcMethod::Version], - [OdooRpcService::Object, OdooRpcMethod::ExecuteKw], - ]; - } + $this->connection->method('getDatabase')->willReturn('test_db'); + $this->connection->method('getUsername')->willReturn('admin'); + $this->connection->method('getPassword')->willReturn('secret'); - /** - * @covers ::request - * - * @dataProvider provideRequestData - */ - public function testRequest(OdooRpcService $service, OdooRpcMethod $method): void - { + // Mock transport to return null (failed login) $this->transport - ->expects(static::once()) ->method('request') - ->with($service->value, $method->value, [1, 2, 3]) - ->willReturn('foo') + ->willReturn(null) ; - $result = $this->client->request($service->value, $method->value, 1, 2, 3); - static::assertSame('foo', $result); + $client = new Client($this->connection, $this->transport); + + $this->expectException(AuthenticationException::class); + $client->authenticate(); } - /** - * @covers ::request - * - * @dataProvider provideRequestData - */ - public function testRequestRemoteError(OdooRpcService $service, OdooRpcMethod $method): void + public function testExecuteKwCallsTransportCorrectly(): void { - self::expectException(RemoteException::class); - $this->transport - ->expects(static::once()) - ->method('request') - ->with($service->value, $method->value, [1, 2, 3]) - ->willThrowException(RemoteException::create([ - 'error' => [ - 'code' => 123, - 'message' => 'Test error', - 'data' => [ - 'debug' => 'foo', - ], - ], - ])) - ; + $this->connection->method('getDatabase')->willReturn('test_db'); + $this->connection->method('getPassword')->willReturn('secret'); - $this->client->request($service->value, $method->value, 1, 2, 3); - } + $client = $this->getMockBuilder(Client::class) + ->setConstructorArgs([$this->connection, $this->transport]) + ->onlyMethods(['authenticate']) + ->getMock() + ; - /** - * @covers ::executeKw - * - * @depends testRequest - */ - public function testExecuteKw(): void - { - [$database, $username, $password] = ['foo', 'bar', 'qux']; - $this->connection->expects(static::exactly(2))->method('getDatabase')->willReturn($database); - $this->connection->expects(static::once())->method('getUsername')->willReturn($username); - $this->connection->expects(static::exactly(2))->method('getPassword')->willReturn($password); - $expectedUid = 1337; - $expectedResult = 'foo'; - - $authenticationArguments = [OdooRpcService::Common->value, OdooRpcMethod::Login->value, [$database, $username, $password]]; - $requestArguments = [OdooRpcService::Object->value, OdooRpcMethod::ExecuteKw->value, [ - $database, - $expectedUid, - $password, - $name = 'object_name', - $method = 'object_method', - $parameters = [1, 2, 3], - $options = [4, 5, 6], - ]]; + $client->method('authenticate')->willReturn(42); $this->transport - ->expects(static::exactly(2)) ->method('request') - ->withConsecutive($authenticationArguments, $requestArguments) - ->willReturn(static::returnCallback(function ($service) use ($expectedUid) { - return match ($service) { - OdooRpcService::Common->value => $expectedUid, - default => 'foo' - }; - })) + ->willReturnCallback(static function (string $service, string $method, mixed ...$args) { + if ('object' === $service && 'execute_kw' === $method) { + return [1, 2, 3]; + } + + return null; + }) ; - $result = $this->client->executeKw($name, $method, $parameters, $options); - static::assertSame($expectedResult, $result); + $result = $client->executeKw('res.partner', 'search', [['is_company' => true]]); + self::assertSame([1, 2, 3], $result); } - /** - * @covers ::version - * - * @depends testRequest - */ - public function testVersion(): void + public function testVersionReturnsVersionObject(): void { $this->transport - ->expects(static::once()) ->method('request') ->with(OdooRpcService::Common->value, OdooRpcMethod::Version->value) ->willReturn([ - 'server_version_info' => [13, 3, 7, 'a', 'b', 'c'], + 'server_version_info' => [16, 0, 0, 'final', 'build_id', 'build_ver'], 'protocol_version' => 1, ]) ; - $version = $this->client->version(); - static::assertInstanceOf(Version::class, $version); + $client = new Client($this->connection, $this->transport); + $version = $client->version(); + + self::assertInstanceOf(Version::class, $version); + + // Assert the getters + self::assertSame(16, $version->getMajorVersion()); + self::assertSame(0, $version->getMinorVersion()); + self::assertSame(0, $version->getPatchVersion()); + self::assertSame('final', $version->getBuildName()); + self::assertSame('build_id', $version->getBuildIdentifier()); + self::assertSame('build_ver', $version->getBuildVersion()); + self::assertSame(1, $version->getProtocolVersion()); } - /** - * @covers ::authenticate - * - * @depends testRequest - */ - public function testAuthenticate(): void + public function testWithLoggerReturnsNewInstance(): void { - [$database, $username, $password] = ['foo', 'bar', 'qux']; - $this->connection->expects(static::once())->method('getDatabase')->willReturn($database); - $this->connection->expects(static::once())->method('getUsername')->willReturn($username); - $this->connection->expects(static::once())->method('getPassword')->willReturn($password); - $expectedUid = 1337; - - $this->transport - ->expects(static::once()) - ->method('request') - ->with(OdooRpcService::Common->value, OdooRpcMethod::Login->value, [$database, $username, $password]) - ->willReturn($expectedUid) - ; + $client = new Client($this->connection, $this->transport); + $newClient = $client->withLogger($this->logger); - $uid = $this->client->authenticate(); - static::assertSame($expectedUid, $uid); - static::assertSame($expectedUid, $this->client->getUid()); + self::assertNotSame($client, $newClient); + self::assertSame($this->logger, $newClient->getLogger()); } } diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index a5fd46c..215e43c 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -12,118 +12,79 @@ namespace Ang3\Component\Odoo\Tests; use Ang3\Component\Odoo\Connection; -use Ang3\Component\Odoo\Exception\ConnectionException; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; /** - * @coversDefaultClass \Ang3\Component\Odoo\Connection - * * @internal */ +#[CoversClass(Connection::class)] final class ConnectionTest extends TestCase { - use FakerTrait; - - private Connection $connection; - private string $host; - private string $username; - private string $password; - private string $database; - private string $dsn; - - protected function setUp(): void + public function testGettersAndToString(): void { - parent::setUp(); - $this->host = 'my-company.odoo.com'; - $this->username = 'my-account@my-domain.com'; - $this->password = self::faker()->password(8); - $this->database = 'my-database-3548373'; - $this->connection = new Connection($this->host, $this->username, $this->password, $this->database); - $this->dsn = sprintf('https://%s:%s@%s/%s', $this->username, urlencode($this->password), $this->host, $this->database); - } + $connection = new Connection('odoo.local', 'admin', 'secret', 'test_db', 'https'); - /** - * @covers ::__toString - */ - public function testToString(): void - { - static::assertSame($this->dsn, (string) $this->connection); + self::assertSame('odoo.local', $connection->getHost()); + self::assertSame('admin', $connection->getUsername()); + self::assertSame('secret', $connection->getPassword()); + self::assertSame('test_db', $connection->getDatabase()); + self::assertSame('https', $connection->getScheme()); + self::assertSame('https://admin:secret@odoo.local/test_db', (string) $connection); + self::assertSame('https://odoo.local', $connection->getUrl()); + self::assertMatchesRegularExpression('/^[a-f0-9]{40}$/', $connection->getIdentifier()); } - /** - * @covers ::create - * - * @depends testGetters - */ - public function testCreate(): void + public function testCreateFromConfig(): void { - $connection = Connection::create([ - 'host' => $this->host, - 'username' => $this->username, - 'password' => $this->password, - 'database' => $this->database, - ]); + $config = [ + 'host' => 'odoo.local', + 'username' => 'admin', + 'password' => 'secret', + 'database' => 'test_db', + ]; - $this->testGetters($connection); - } + $connection = Connection::create($config); - /** - * @covers ::create - * - * @depends testGetters - * - * @testWith [null, "user", "pass", "database"] - * ["host", null, "pass", "database"] - * ["host", "user", null, "database"] - * ["host", "user", "pass", null] - */ - public function testCreateWithMissingParameters( - ?string $host = null, - ?string $username = null, - ?string $password = null, - ?string $database = null - ): void { - $this->expectException(ConnectionException::class); + self::assertSame('odoo.local', $connection->getHost()); + self::assertSame('admin', $connection->getUsername()); + self::assertSame('secret', $connection->getPassword()); + self::assertSame('test_db', $connection->getDatabase()); + self::assertSame('https', $connection->getScheme()); + } + public function testCreateThrowsExceptionOnMissingParam(): void + { + $this->expectException(\InvalidArgumentException::class); Connection::create([ - 'host' => $host, - 'username' => $username, - 'password' => $password, - 'database' => $database, + 'host' => 'odoo.local', + 'username' => 'admin', + // missing password + 'database' => 'test_db', ]); } - /** - * @covers ::parseDsn - * - * @depends testGetters - */ public function testParseDsn(): void { - $connection = Connection::parseDsn($this->dsn); - $this->testGetters($connection); + $dsn = 'https://admin:secret@odoo.local/test_db'; + $connection = Connection::parseDsn($dsn); + + self::assertSame('odoo.local', $connection->getHost()); + self::assertSame('admin', $connection->getUsername()); + self::assertSame('secret', $connection->getPassword()); + self::assertSame('test_db', $connection->getDatabase()); + self::assertSame('https', $connection->getScheme()); } - /** - * @covers ::getDatabase - * @covers ::getHost - * @covers ::getPassword - * @covers ::getUsername - */ - public function testGetters(?Connection $connection = null): void + public function testParseDsnWithMissingPartsThrowsException(): void { - $connection = $connection ?: $this->connection; - static::assertSame($this->host, $connection->getHost()); - static::assertSame($this->username, $connection->getUsername()); - static::assertSame($this->password, $connection->getPassword()); - static::assertSame($this->database, $connection->getDatabase()); + $this->expectException(\InvalidArgumentException::class); + Connection::parseDsn('https://@odoo.local/test_db'); } - /** - * @covers ::getIdentifier - */ - public function testGetIdentifier(): void + public function testParseDsnWithUnsupportedSchemeThrowsException(): void { - static::assertSame(sha1(sprintf('%s.%s.%s', $this->host, $this->database, $this->username)), $this->connection->getIdentifier()); + $this->expectException(\InvalidArgumentException::class); + Connection::parseDsn('ftp://admin:secret@odoo.local/test_db'); } } diff --git a/tests/FakerTrait.php b/tests/FakerTrait.php deleted file mode 100644 index c04fa29..0000000 --- a/tests/FakerTrait.php +++ /dev/null @@ -1,29 +0,0 @@ -getMajorVersion()); + self::assertSame(2, $version->getMinorVersion()); + self::assertSame(3, $version->getPatchVersion()); + self::assertSame('nightly', $version->getBuildName()); + self::assertSame('abc123', $version->getBuildIdentifier()); + self::assertSame('15.2.3+nightly', $version->getBuildVersion()); + self::assertSame(2, $version->getProtocolVersion()); + } + + public function testCreateFromPayload(): void + { + $payload = [ + 'protocol_version' => '3', + 'server_version_info' => [16, 1, 4, 'rc', 'xyz789', '16.1.4+rc'], + ]; + + $version = Version::create($payload); + + self::assertSame(16, $version->getMajorVersion()); + self::assertSame(1, $version->getMinorVersion()); + self::assertSame(4, $version->getPatchVersion()); + self::assertSame('rc', $version->getBuildName()); + self::assertSame('xyz789', $version->getBuildIdentifier()); + self::assertSame('16.1.4+rc', $version->getBuildVersion()); + self::assertSame(3, $version->getProtocolVersion()); + } + + public function testToStringReturnsName(): void + { + $version = new Version( + majorVersion: 15, + minorVersion: 2, + patchVersion: 3, + buildName: 'nightly', + buildIdentifier: 'abc123', + buildVersion: '15.2.3+nightly', + protocolVersion: 2 + ); + + self::assertSame('15.2.3+nightly', $version->__toString()); + self::assertSame('15.2.3+nightly', $version->getName()); + } +} diff --git a/tests/Transport/Client/JsonRpcHttpClientTest.php b/tests/Transport/Client/JsonRpcHttpClientTest.php new file mode 100644 index 0000000..61b9e5c --- /dev/null +++ b/tests/Transport/Client/JsonRpcHttpClientTest.php @@ -0,0 +1,45 @@ +getMockBuilder(JsonRpcHttpClient::class) + ->onlyMethods(['doRequest']) + ->getMock() + ; + + $url = 'https://example.com/jsonrpc'; + $payload = '{"jsonrpc":"2.0","method":"ping"}'; + $timeout = 5; + + $client->expects(self::once()) + ->method('doRequest') + ->with($url, $payload, $timeout) + ->willReturn('{"result":"pong"}') + ; + + $result = $client->post($url, $payload, $timeout); + + self::assertSame('{"result":"pong"}', $result); + } +} diff --git a/tests/Transport/JsonRpcPhpStreamTransportTest.php b/tests/Transport/JsonRpcPhpStreamTransportTest.php deleted file mode 100644 index 61fba7a..0000000 --- a/tests/Transport/JsonRpcPhpStreamTransportTest.php +++ /dev/null @@ -1,79 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->transport = new JsonRpcPhpStreamTransport($this->connection, TransportInterface::DEFAULT_TIMEOUT, ''); - } - - /** - * @covers ::request - */ - public function testRequest(): void - { - [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; - $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::SUCCESS_ENDPOINT); - - $result = $this->transport->request($service, $method, $arguments); - static::assertSame(['success' => 'true'], $result); - } - - /** - * @covers ::request - */ - public function testRequestDecodingError(): void - { - [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; - $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::JSON_ERROR_ENDPOINT); - - $this->expectException(TransportException::class); - $this->transport->request($service, $method, $arguments); - } - - /** - * @covers ::request - */ - public function testRequestRemoteError(): void - { - [$service, $method, $arguments] = ['foo', 'bar', [1, 2, 3]]; - $this->connection->expects(static::once())->method('getUrl')->willReturn(self::TEST_URL.self::REMOTE_ERROR_ENDPOINT); - - $this->expectException(RemoteException::class); - $this->transport->request($service, $method, $arguments); - } -} diff --git a/tests/Transport/JsonRpcTransportTest.php b/tests/Transport/JsonRpcTransportTest.php new file mode 100644 index 0000000..47d178b --- /dev/null +++ b/tests/Transport/JsonRpcTransportTest.php @@ -0,0 +1,95 @@ +connection = $this->createMock(Connection::class); + $this->connection->method('getUrl')->willReturn($this->baseUrl); + } + + public function testRequestReturnsResult(): void + { + $httpClient = $this->createMock(JsonRpcHttpClientInterface::class); + $httpClient->method('post') + ->willReturn(json_encode(['result' => 'success'])) + ; + + $transport = new JsonRpcTransport($this->connection, $httpClient); + + $result = $transport->request('service', 'method'); + self::assertSame('success', $result); + } + + public function testRequestThrowsTransportExceptionOnFalse(): void + { + $httpClient = $this->createMock(JsonRpcHttpClientInterface::class); + $httpClient->method('post')->willReturn(false); + + $transport = new JsonRpcTransport($this->connection, $httpClient); + + $this->expectException(TransportException::class); + $transport->request('service', 'method'); + } + + public function testRequestThrowsRemoteExceptionOnError(): void + { + $httpClient = $this->createMock(JsonRpcHttpClientInterface::class); + $httpClient->method('post') + ->willReturn(json_encode([ + 'error' => [ + 'code' => 123, + 'message' => 'Fail', + 'data' => [ + 'debug' => 'Traceback (most recent call last):'."\n". + 'File "/app/test.py", line 10, in '."\n". + 'print("Fail")', + ], + ], + ])) + ; + + $transport = new JsonRpcTransport($this->connection, $httpClient); + + $this->expectException(RemoteException::class); + $transport->request('service', 'method'); + } + + public function testRequestThrowsTransportExceptionOnInvalidJson(): void + { + $httpClient = $this->createMock(JsonRpcHttpClientInterface::class); + $httpClient->method('post')->willReturn('{"invalidJson":'); // JSON invalide + + $transport = new JsonRpcTransport($this->connection, $httpClient); + + $this->expectException(TransportException::class); + $transport->request('service', 'method'); + } +} From 161c9536360a189ef32c3048c6905206a8459bae Mon Sep 17 00:00:00 2001 From: Ang3 Date: Tue, 3 Feb 2026 17:51:43 +0100 Subject: [PATCH 76/80] Fix client and connection --- .github/workflows/phpunit.yml | 2 +- .gitignore | 1 + .phpunit.cache/test-results | 2 +- README.md | 16 +++- composer.json | 3 +- composer.lock | 64 +++++++++++++- src/Client.php | 38 ++++---- src/Connection.php | 89 +++++++------------ tests/ClientTest.php | 157 ++++++++++++++++++++-------------- tests/ConnectionTest.php | 127 +++++++++++++++++---------- 10 files changed, 305 insertions(+), 194 deletions(-) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index d6ce694..f4c5e96 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -9,7 +9,7 @@ jobs: tests_suite: strategy: matrix: - php_version: [ '8.1', '8.2' ] + php_version: [ '8.2', '8.3', '8.4', '8.5' ] runs-on: ubuntu-latest steps: - uses: shivammathur/setup-php@v2 diff --git a/.gitignore b/.gitignore index 91671fa..92af99c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.phpunit.cache/ /vendor/ /.idea/ /var/ diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results index 43c9400..ccb2553 100644 --- a/.phpunit.cache/test-results +++ b/.phpunit.cache/test-results @@ -1 +1 @@ -{"version":2,"defects":[],"times":{"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateReturnsUid":0.013,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateThrowsExceptionOnFailure":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwCallsTransportCorrectly":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testVersionReturnsVersionObject":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testWithLoggerReturnsNewInstance":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testGettersAndToString":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfig":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateThrowsExceptionOnMissingParam":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsn":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsnWithMissingPartsThrowsException":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsnWithUnsupportedSchemeThrowsException":0,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testConstructorAndGetters":0,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testCreateFromPayload":0,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testToStringReturnsName":0,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestReturnsResult":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsTransportExceptionOnFalse":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsRemoteExceptionOnError":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsTransportExceptionOnInvalidJson":0,"Ang3\\Component\\Odoo\\Tests\\Transport\\Client\\JsonRpcHttpClientTest::testPostReturnsResponse":0.001}} \ No newline at end of file +{"version":2,"defects":{"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsnWithMissingPartsThrowsException":7,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateSuccess":8,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateFailureThrowsException":7,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwCallsTransportWithCorrectArguments":8,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestUsesCachedUid":8,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwAuthenticatesAndExecutesRequest":8,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestAfterAuthenticationUsesCachedUid":8,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateFailureThrowsAuthenticationException":7,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testWithLoggerReturnsNewInstanceAndLoggerIsUsed":8,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfigSuccess":7,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromDsnSuccess":7},"times":{"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateReturnsUid":0.013,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateThrowsExceptionOnFailure":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwCallsTransportCorrectly":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testVersionReturnsVersionObject":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testWithLoggerReturnsNewInstance":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testGettersAndToString":0.051,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfig":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateThrowsExceptionOnMissingParam":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsn":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsnWithMissingPartsThrowsException":0.008,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsnWithUnsupportedSchemeThrowsException":0,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testConstructorAndGetters":0.002,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testCreateFromPayload":0,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testToStringReturnsName":0,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestReturnsResult":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsTransportExceptionOnFalse":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsRemoteExceptionOnError":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsTransportExceptionOnInvalidJson":0,"Ang3\\Component\\Odoo\\Tests\\Transport\\Client\\JsonRpcHttpClientTest::testPostReturnsResponse":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromDsn":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateSuccess":0.006,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateFailureThrowsException":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwCallsTransportWithCorrectArguments":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestUsesCachedUid":0,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testWithTransportReturnsNewInstance":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestWithoutAuthenticationThrowsException":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwAuthenticatesAndExecutesRequest":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestAfterAuthenticationUsesCachedUid":0,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateFailureThrowsAuthenticationException":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestWithoutAuthenticationThrowsRequestException":0,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testWithLoggerReturnsNewInstanceAndLoggerIsUsed":0.002,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testConstructorAndGetters":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfigSuccess":0.003,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfigDefaultsSchemeToHttps":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfigInvalidConfigThrows":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfigUnsupportedSchemeThrows":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromDsnSuccess":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromDsnInvalidThrows":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testToStringAndGetUrl":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testGetIdentifierIsStable":0}} \ No newline at end of file diff --git a/README.md b/README.md index ffb76bd..3112a30 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ It follows [the official Odoo documentation](https://www.odoo.com/documentation/ Installation ------------ -PHP version 8.1 or newer to develop using the client. Other requirements, such as PHP extensions, are enforced by +PHP version 8.2 or newer to develop using the client. Other requirements, such as PHP extensions, are enforced by composer. See the `require` section of [composer.json file](../composer.json) for details. @@ -63,17 +63,25 @@ If your password contains special characters, encode it with the native function $myEncodedPassword = urlencode('high_password_with_special_charaters'); ``` -Then, use the DSN to create the client: +Then, use the DSN to create a connection: + +```php +use Ang3\Component\Odoo\Connection; + +$connection = Connection::createFromDsn($dsn, $transport = null, $logger = null); +``` + +Finally, create your client with the connection: ```php use Ang3\Component\Odoo\Client; -$client = Client::create($dsn, $transport = null, $logger = null); +$client = new Client($connection, $transport = null, $logger = null); ``` #### Array configuration -To create a client from an array configuration: +To create a connection from a configuration as array: ```php $client = Client::create([ diff --git a/composer.json b/composer.json index 5725772..694ffc7 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,8 @@ "php": ">=8.2", "ext-json": "*", "psr/log": "^1.1|^2.0|^3.0", - "nyholm/dsn": "^2.0" + "nyholm/dsn": "^2.0", + "webmozart/assert": "^2.1" }, "require-dev": { "symfony/test-pack": "^1.0", diff --git a/composer.lock b/composer.lock index 0510b5b..dc1a598 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fcf1f081bffa7a2d8116b14b231003d7", + "content-hash": "c7770aad4076f9c223f8da2ca5590321", "packages": [ { "name": "nyholm/dsn", @@ -116,6 +116,68 @@ "source": "https://github.com/php-fig/log/tree/3.0.2" }, "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.1.2", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", + "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.1.2" + }, + "time": "2026-01-13T14:02:24+00:00" } ], "packages-dev": [ diff --git a/src/Client.php b/src/Client.php index 6aa500c..cafc9b8 100644 --- a/src/Client.php +++ b/src/Client.php @@ -62,27 +62,25 @@ public function version(): Version } /** - * @throws AuthenticationException when authentication failed + * @throws AuthenticationException on authentication error */ public function authenticate(): int { - if (null === $this->uid) { - /** @var int|null $uid */ - $uid = $this->request( - OdooRpcService::Common->value, - OdooRpcMethod::Login->value, - $this->connection->getDatabase(), - $this->connection->getUsername(), - $this->connection->getPassword() - ); - - if (!$uid) { - throw new AuthenticationException(); - } - - $this->uid = $uid; + /** @var int|null $uid */ + $uid = $this->request( + OdooRpcService::Common->value, + OdooRpcMethod::Login->value, + $this->connection->getDatabase(), + $this->connection->getUsername(), + $this->connection->getPassword() + ); + + if (!$uid) { + throw new AuthenticationException(); } + $this->uid = $uid; + return $this->uid; } @@ -92,10 +90,16 @@ public function authenticate(): int */ public function request(string $service, string $method, mixed ...$arguments): mixed { + $uid = $this->uid; + + if ($service !== OdooRpcService::Common->value || $method !== OdooRpcMethod::Login->value) { + $uid ?: throw new RequestException('You must authenticate before making requests.'); + } + $context = [ 'service' => $service, 'method' => $method, - 'uid' => (int) $this->uid, + 'uid' => $uid, 'arguments' => \array_slice($arguments, 3), 'request_id' => uniqid('rpc', true), ]; diff --git a/src/Connection.php b/src/Connection.php index 98edf59..517d9cc 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -13,6 +13,8 @@ use Nyholm\Dsn\DsnParser; use Nyholm\Dsn\Exception\InvalidDsnException; +use Webmozart\Assert\Assert; +use Webmozart\Assert\InvalidArgumentException; /** * @author Joanis ROUANET @@ -33,40 +35,10 @@ public function __toString(): string return \sprintf('%s://%s:%s@%s/%s', $this->scheme, $this->username, urlencode($this->password), $this->host, $this->database); } - /** - * @param array $config - * - * @throws \InvalidArgumentException on invalid config - */ - public static function create(array $config): self - { - $getParam = static function (string $paramName) use ($config): string { - $value = $config[$paramName] ?? null; - - if (null === $value) { - throw new \InvalidArgumentException(\sprintf('Missing configuration parameter "%s".', $paramName)); - } - - if (!\is_string($value)) { - throw new \InvalidArgumentException(\sprintf('The parameter "%s" should be a string, got "%s".', $paramName, \gettype($value))); - } - - return $value; - }; - - return new self( - $getParam('host'), - $getParam('username'), - $getParam('password'), - $getParam('database'), - $config['scheme'] ?? 'https', - ); - } - /** * @throws \InvalidArgumentException on invalid config */ - public static function parseDsn(string $dsn): self + public static function createFromDsn(string $dsn): self { try { $dsn = DsnParser::parse($dsn); @@ -74,41 +46,40 @@ public static function parseDsn(string $dsn): self throw new \InvalidArgumentException('Invalid DSN', 0, $e); } - [$scheme, $host, $user, $password, $path] = [ - $dsn->getScheme(), - $dsn->getHost(), - $dsn->getUser(), - $dsn->getPassword(), - $dsn->getPath(), - ]; + $path = (string) $dsn->getPath(); - if (!$scheme) { - throw new \InvalidArgumentException('Missing DSN scheme.'); - } - - if (!\in_array($scheme, ['http', 'https'], true)) { - throw new \InvalidArgumentException(\sprintf('The DSN scheme "%s" is not supported (supported: "http" or "https").', $scheme)); - } + return self::createFromConfig([ + 'host' => (string) $dsn->getHost(), + 'username' => (string) $dsn->getUser(), + 'password' => urldecode((string) $dsn->getPassword()), + 'database' => str_starts_with($path, '/') ? substr($path, 1) : $path, + 'scheme' => (string) $dsn->getScheme(), + ]); + } - if (!$host) { - throw new \InvalidArgumentException('Missing DSN host.'); - } + /** + * @param array{host: string, username: string, password: string, database: string, scheme: string|null} $config + * + * @throws InvalidArgumentException on invalid configuration + */ + public static function createFromConfig(array $config): self + { + $host = $config['host'] ?? null; + Assert::stringNotEmpty($host, 'The host cannot be empty.'); - if (!$user) { - throw new \InvalidArgumentException('Missing DSN username.'); - } + $username = $config['username'] ?? null; + Assert::stringNotEmpty($username, 'The username cannot be empty.'); - if (!$password) { - throw new \InvalidArgumentException('Missing DSN user password.'); - } + $password = $config['password'] ?? null; + Assert::stringNotEmpty($password, 'The password cannot be empty.'); - if (!$path) { - throw new \InvalidArgumentException('Missing DSN path.'); - } + $database = $config['database'] ?? null; + Assert::stringNotEmpty($database, 'The database cannot be empty.'); - $database = str_starts_with($path, '/') ? substr($path, 1) : $path; + $scheme = $config['scheme'] ?? 'https'; + Assert::inArray($scheme, ['http', 'https'], \sprintf('The DSN scheme "%s" is not supported (supported: "http" or "https").', $scheme)); - return new self($host, $user, urldecode($password), $database); + return new self($host, $username, $password, $database, $scheme); } /** diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 70f96eb..4da05ad 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -16,128 +16,155 @@ use Ang3\Component\Odoo\Enum\OdooRpcMethod; use Ang3\Component\Odoo\Enum\OdooRpcService; use Ang3\Component\Odoo\Exception\AuthenticationException; -use Ang3\Component\Odoo\Metadata\Version; +use Ang3\Component\Odoo\Exception\RequestException; use Ang3\Component\Odoo\Transport\TransportInterface; -use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; /** * @internal + * + * @coversNothing */ -#[CoversClass(Client::class)] final class ClientTest extends TestCase { - private Connection $connection; - private TransportInterface $transport; - private LoggerInterface $logger; + private Connection&MockObject $connection; + private TransportInterface&MockObject $transport; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); $this->transport = $this->createMock(TransportInterface::class); - $this->logger = $this->createMock(LoggerInterface::class); - } - public function testAuthenticateReturnsUid(): void - { - $this->connection->method('getDatabase')->willReturn('test_db'); + $this->connection->method('getDatabase')->willReturn('odoo_db'); $this->connection->method('getUsername')->willReturn('admin'); $this->connection->method('getPassword')->willReturn('secret'); + } + public function testAuthenticateSuccess(): void + { $this->transport + ->expects(self::once()) ->method('request') - ->willReturnCallback(static function (string $service, string $method, mixed ...$args) { - if ('common' === $service && 'login' === $method) { - return 42; // UID - } - - return null; - }) + ->with( + OdooRpcService::Common->value, + OdooRpcMethod::Login->value, + ['odoo_db', 'admin', 'secret'] + ) + ->willReturn(42) ; $client = new Client($this->connection, $this->transport); - self::assertSame(42, $client->authenticate()); + $uid = $client->authenticate(); + + self::assertSame(42, $uid); + self::assertSame(42, $client->getUid()); } - public function testAuthenticateThrowsExceptionOnFailure(): void + public function testAuthenticateFailureThrowsAuthenticationException(): void { - $this->connection->method('getDatabase')->willReturn('test_db'); - $this->connection->method('getUsername')->willReturn('admin'); - $this->connection->method('getPassword')->willReturn('secret'); - - // Mock transport to return null (failed login) $this->transport ->method('request') ->willReturn(null) ; $client = new Client($this->connection, $this->transport); - $this->expectException(AuthenticationException::class); $client->authenticate(); } - public function testExecuteKwCallsTransportCorrectly(): void + public function testRequestWithoutAuthenticationThrowsRequestException(): void { - $this->connection->method('getDatabase')->willReturn('test_db'); - $this->connection->method('getPassword')->willReturn('secret'); + $client = new Client($this->connection, $this->transport); - $client = $this->getMockBuilder(Client::class) - ->setConstructorArgs([$this->connection, $this->transport]) - ->onlyMethods(['authenticate']) - ->getMock() - ; + $this->expectException(RequestException::class); + $this->expectExceptionMessage('You must authenticate before making requests.'); - $client->method('authenticate')->willReturn(42); + $client->request( + OdooRpcService::Object->value, + OdooRpcMethod::ExecuteKw->value, + 'odoo_db', + 1, + 'secret' + ); + } + public function testExecuteKwAuthenticatesAndExecutesRequest(): void + { $this->transport + ->expects(self::exactly(2)) ->method('request') - ->willReturnCallback(static function (string $service, string $method, mixed ...$args) { - if ('object' === $service && 'execute_kw' === $method) { - return [1, 2, 3]; - } - - return null; - }) + ->willReturnOnConsecutiveCalls( + 7, // authenticate() + ['result' => true] // execute_kw + ) ; - $result = $client->executeKw('res.partner', 'search', [['is_company' => true]]); - self::assertSame([1, 2, 3], $result); + $client = new Client($this->connection, $this->transport); + $result = $client->executeKw( + 'res.partner', + 'search_read', + [['is_company', '=', true]], + ['limit' => 10] + ); + + self::assertSame(['result' => true], $result); + self::assertSame(7, $client->getUid()); } - public function testVersionReturnsVersionObject(): void + public function testRequestAfterAuthenticationUsesCachedUid(): void { $this->transport + ->expects(self::exactly(2)) ->method('request') - ->with(OdooRpcService::Common->value, OdooRpcMethod::Version->value) - ->willReturn([ - 'server_version_info' => [16, 0, 0, 'final', 'build_id', 'build_ver'], - 'protocol_version' => 1, - ]) + ->willReturnOnConsecutiveCalls( + 5, // authenticate() + 'ok' // request() + ) ; $client = new Client($this->connection, $this->transport); - $version = $client->version(); - - self::assertInstanceOf(Version::class, $version); - - // Assert the getters - self::assertSame(16, $version->getMajorVersion()); - self::assertSame(0, $version->getMinorVersion()); - self::assertSame(0, $version->getPatchVersion()); - self::assertSame('final', $version->getBuildName()); - self::assertSame('build_id', $version->getBuildIdentifier()); - self::assertSame('build_ver', $version->getBuildVersion()); - self::assertSame(1, $version->getProtocolVersion()); + $client->authenticate(); + $result = $client->request( + OdooRpcService::Object->value, + OdooRpcMethod::ExecuteKw->value, + 'odoo_db', + 5, + 'secret', + 'model', + 'method' + ); + + self::assertSame('ok', $result); } - public function testWithLoggerReturnsNewInstance(): void + public function testWithTransportReturnsNewInstance(): void { + $newTransport = $this->createMock(TransportInterface::class); + $client = new Client($this->connection, $this->transport); - $newClient = $client->withLogger($this->logger); + $newClient = $client->withTransport($newTransport); self::assertNotSame($client, $newClient); - self::assertSame($this->logger, $newClient->getLogger()); + self::assertSame($newTransport, $newClient->getTransport()); + } + + public function testWithLoggerReturnsNewInstanceAndLoggerIsUsed(): void + { + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once()) + ->method('info') + ->with( + self::stringContains('Odoo request'), + self::arrayHasKey('service') + ) + ; + + $transport = $this->createMock(TransportInterface::class); + $transport->method('request')->willReturn(7); + + $client = new Client($this->connection, $transport, $logger); + $client->authenticate(); } } diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index 215e43c..8f27701 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -12,79 +12,116 @@ namespace Ang3\Component\Odoo\Tests; use Ang3\Component\Odoo\Connection; -use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; +use Webmozart\Assert\InvalidArgumentException; /** * @internal + * + * @coversNothing */ -#[CoversClass(Connection::class)] final class ConnectionTest extends TestCase { - public function testGettersAndToString(): void + public function testConstructorAndGetters(): void { - $connection = new Connection('odoo.local', 'admin', 'secret', 'test_db', 'https'); - - self::assertSame('odoo.local', $connection->getHost()); - self::assertSame('admin', $connection->getUsername()); - self::assertSame('secret', $connection->getPassword()); - self::assertSame('test_db', $connection->getDatabase()); - self::assertSame('https', $connection->getScheme()); - self::assertSame('https://admin:secret@odoo.local/test_db', (string) $connection); - self::assertSame('https://odoo.local', $connection->getUrl()); - self::assertMatchesRegularExpression('/^[a-f0-9]{40}$/', $connection->getIdentifier()); + $conn = new Connection('host', 'user', 'pass', 'db', 'http'); + + self::assertSame('host', $conn->getHost()); + self::assertSame('user', $conn->getUsername()); + self::assertSame('pass', $conn->getPassword()); + self::assertSame('db', $conn->getDatabase()); + self::assertSame('http', $conn->getScheme()); + + self::assertStringContainsString('http://user:pass@host/db', (string) $conn); + self::assertStringContainsString('http://host', $conn->getUrl()); + self::assertNotEmpty($conn->getIdentifier()); } - public function testCreateFromConfig(): void + public function testCreateFromConfigSuccess(): void { $config = [ - 'host' => 'odoo.local', - 'username' => 'admin', - 'password' => 'secret', - 'database' => 'test_db', + 'host' => 'myhost', + 'username' => 'myuser', + 'password' => 'mypass', + 'database' => 'mydb', + 'scheme' => 'https', ]; - $connection = Connection::create($config); + $conn = Connection::createFromConfig($config); - self::assertSame('odoo.local', $connection->getHost()); - self::assertSame('admin', $connection->getUsername()); - self::assertSame('secret', $connection->getPassword()); - self::assertSame('test_db', $connection->getDatabase()); - self::assertSame('https', $connection->getScheme()); + self::assertSame('myhost', $conn->getHost()); + self::assertSame('myuser', $conn->getUsername()); + self::assertSame('mypass', $conn->getPassword()); + self::assertSame('mydb', $conn->getDatabase()); + self::assertSame('https', $conn->getScheme()); } - public function testCreateThrowsExceptionOnMissingParam(): void + public function testCreateFromConfigDefaultsSchemeToHttps(): void { - $this->expectException(\InvalidArgumentException::class); - Connection::create([ - 'host' => 'odoo.local', - 'username' => 'admin', - // missing password - 'database' => 'test_db', + $config = [ + 'host' => 'h', + 'username' => 'u', + 'password' => 'p', + 'database' => 'd', + ]; + + $conn = Connection::createFromConfig($config); + + self::assertSame('https', $conn->getScheme()); + } + + public function testCreateFromConfigInvalidConfigThrows(): void + { + $this->expectException(InvalidArgumentException::class); + Connection::createFromConfig([]); + } + + public function testCreateFromConfigUnsupportedSchemeThrows(): void + { + $this->expectException(InvalidArgumentException::class); + + Connection::createFromConfig([ + 'host' => 'h', + 'username' => 'u', + 'password' => 'p', + 'database' => 'd', + 'scheme' => 'ftp', ]); } - public function testParseDsn(): void + public function testCreateFromDsnSuccess(): void { - $dsn = 'https://admin:secret@odoo.local/test_db'; - $connection = Connection::parseDsn($dsn); - - self::assertSame('odoo.local', $connection->getHost()); - self::assertSame('admin', $connection->getUsername()); - self::assertSame('secret', $connection->getPassword()); - self::assertSame('test_db', $connection->getDatabase()); - self::assertSame('https', $connection->getScheme()); + $dsn = 'https://user:pass@host/db'; + $conn = Connection::createFromDsn($dsn); + + self::assertSame('host', $conn->getHost()); + self::assertSame('user', $conn->getUsername()); + self::assertSame('pass', $conn->getPassword()); + self::assertSame('db', $conn->getDatabase()); + self::assertSame('https', $conn->getScheme()); } - public function testParseDsnWithMissingPartsThrowsException(): void + public function testCreateFromDsnInvalidThrows(): void { $this->expectException(\InvalidArgumentException::class); - Connection::parseDsn('https://@odoo.local/test_db'); + + Connection::createFromDsn('invalid-dsn'); } - public function testParseDsnWithUnsupportedSchemeThrowsException(): void + public function testToStringAndGetUrl(): void { - $this->expectException(\InvalidArgumentException::class); - Connection::parseDsn('ftp://admin:secret@odoo.local/test_db'); + $conn = new Connection('host', 'user', 'pass', 'db', 'https'); + + $str = (string) $conn; + self::assertStringContainsString('https://user:pass@host/db', $str); + self::assertSame('https://host', $conn->getUrl()); + } + + public function testGetIdentifierIsStable(): void + { + $conn1 = new Connection('host', 'user', 'pass', 'db'); + $conn2 = new Connection('host', 'user', 'pass', 'db'); + + self::assertSame($conn1->getIdentifier(), $conn2->getIdentifier()); } } From 2ea6fb50818b19d58fb075b2e04a170f5ccd4e83 Mon Sep 17 00:00:00 2001 From: Ang3 Date: Tue, 3 Feb 2026 17:53:06 +0100 Subject: [PATCH 77/80] Removes phpunit cache folder --- .phpunit.cache/code-coverage/08c243b330d480e348c2e23951b2ed4e | 1 - .phpunit.cache/code-coverage/2ab0a544083c0b866e56d1a1a72d953b | 1 - .phpunit.cache/code-coverage/2b8f89c7eb6feacfb35a96b5ed5ba6dc | 1 - .phpunit.cache/code-coverage/369798d81ca7dddb00be7f6947eede91 | 1 - .phpunit.cache/code-coverage/4bf226508f77741cf5afa74dafa86178 | 1 - .phpunit.cache/code-coverage/52118a736c1a03a7e6b2f995149dfe68 | 1 - .phpunit.cache/code-coverage/58806f641bbffe5922122c226ac27b4a | 1 - .phpunit.cache/code-coverage/717ffe8417576cb1f922c16743e6f6ff | 1 - .phpunit.cache/code-coverage/99a617401202e025c1ebee54d349c25b | 1 - .phpunit.cache/code-coverage/a8bb1700d6e80837ca1c1a13e68a74e6 | 1 - .phpunit.cache/code-coverage/aafdcf027cacd0bd24b17d8fccd53308 | 1 - .phpunit.cache/code-coverage/ad5d7750bc032bbc4e872fe2b81bd1d4 | 1 - .phpunit.cache/code-coverage/af0a8e802569cf7c13c2b0d56d761e60 | 1 - .phpunit.cache/code-coverage/cc2997b47b202f04df6c5d311bddc88f | 1 - .phpunit.cache/code-coverage/e0b8c8e2f445c41b4306b462a52a7a83 | 1 - .phpunit.cache/code-coverage/e3753fe5e4e60324f36fee0947c42207 | 1 - .phpunit.cache/code-coverage/e8d63d9025d0e3a640ba472491b40e70 | 1 - .phpunit.cache/test-results | 1 - 18 files changed, 18 deletions(-) delete mode 100644 .phpunit.cache/code-coverage/08c243b330d480e348c2e23951b2ed4e delete mode 100644 .phpunit.cache/code-coverage/2ab0a544083c0b866e56d1a1a72d953b delete mode 100644 .phpunit.cache/code-coverage/2b8f89c7eb6feacfb35a96b5ed5ba6dc delete mode 100644 .phpunit.cache/code-coverage/369798d81ca7dddb00be7f6947eede91 delete mode 100644 .phpunit.cache/code-coverage/4bf226508f77741cf5afa74dafa86178 delete mode 100644 .phpunit.cache/code-coverage/52118a736c1a03a7e6b2f995149dfe68 delete mode 100644 .phpunit.cache/code-coverage/58806f641bbffe5922122c226ac27b4a delete mode 100644 .phpunit.cache/code-coverage/717ffe8417576cb1f922c16743e6f6ff delete mode 100644 .phpunit.cache/code-coverage/99a617401202e025c1ebee54d349c25b delete mode 100644 .phpunit.cache/code-coverage/a8bb1700d6e80837ca1c1a13e68a74e6 delete mode 100644 .phpunit.cache/code-coverage/aafdcf027cacd0bd24b17d8fccd53308 delete mode 100644 .phpunit.cache/code-coverage/ad5d7750bc032bbc4e872fe2b81bd1d4 delete mode 100644 .phpunit.cache/code-coverage/af0a8e802569cf7c13c2b0d56d761e60 delete mode 100644 .phpunit.cache/code-coverage/cc2997b47b202f04df6c5d311bddc88f delete mode 100644 .phpunit.cache/code-coverage/e0b8c8e2f445c41b4306b462a52a7a83 delete mode 100644 .phpunit.cache/code-coverage/e3753fe5e4e60324f36fee0947c42207 delete mode 100644 .phpunit.cache/code-coverage/e8d63d9025d0e3a640ba472491b40e70 delete mode 100644 .phpunit.cache/test-results diff --git a/.phpunit.cache/code-coverage/08c243b330d480e348c2e23951b2ed4e b/.phpunit.cache/code-coverage/08c243b330d480e348c2e23951b2ed4e deleted file mode 100644 index 28667dc..0000000 --- a/.phpunit.cache/code-coverage/08c243b330d480e348c2e23951b2ed4e +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";a:6:{s:4:"name";s:17:"JsonRpcHttpClient";s:14:"namespacedName";s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";s:9:"namespace";s:36:"Ang3\Component\Odoo\Transport\Client";s:9:"startLine";i:14;s:7:"endLine";i:39;s:7:"methods";a:2:{s:4:"post";a:6:{s:10:"methodName";s:4:"post";s:9:"signature";s:62:"post(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:6:"public";s:9:"startLine";i:16;s:7:"endLine";i:19;s:3:"ccn";i:1;}s:9:"doRequest";a:6:{s:10:"methodName";s:9:"doRequest";s:9:"signature";s:67:"doRequest(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:9:"protected";s:9:"startLine";i:26;s:7:"endLine";i:38;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:40;s:18:"commentLinesOfCode";i:11;s:21:"nonCommentLinesOfCode";i:29;}s:15:"ignoredLinesFor";a:14:{i:0;i:14;i:1;i:26;i:2;i:27;i:3;i:28;i:4;i:29;i:5;i:30;i:6;i:31;i:7;i:32;i:8;i:33;i:9;i:34;i:10;i:35;i:11;i:36;i:12;i:37;i:13;i:38;}s:17:"executableLinesIn";a:10:{i:18;i:1;i:28;i:2;i:29;i:2;i:30;i:2;i:31;i:2;i:32;i:2;i:33;i:2;i:34;i:2;i:35;i:2;i:37;i:3;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/2ab0a544083c0b866e56d1a1a72d953b b/.phpunit.cache/code-coverage/2ab0a544083c0b866e56d1a1a72d953b deleted file mode 100644 index 28667dc..0000000 --- a/.phpunit.cache/code-coverage/2ab0a544083c0b866e56d1a1a72d953b +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";a:6:{s:4:"name";s:17:"JsonRpcHttpClient";s:14:"namespacedName";s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";s:9:"namespace";s:36:"Ang3\Component\Odoo\Transport\Client";s:9:"startLine";i:14;s:7:"endLine";i:39;s:7:"methods";a:2:{s:4:"post";a:6:{s:10:"methodName";s:4:"post";s:9:"signature";s:62:"post(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:6:"public";s:9:"startLine";i:16;s:7:"endLine";i:19;s:3:"ccn";i:1;}s:9:"doRequest";a:6:{s:10:"methodName";s:9:"doRequest";s:9:"signature";s:67:"doRequest(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:9:"protected";s:9:"startLine";i:26;s:7:"endLine";i:38;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:40;s:18:"commentLinesOfCode";i:11;s:21:"nonCommentLinesOfCode";i:29;}s:15:"ignoredLinesFor";a:14:{i:0;i:14;i:1;i:26;i:2;i:27;i:3;i:28;i:4;i:29;i:5;i:30;i:6;i:31;i:7;i:32;i:8;i:33;i:9;i:34;i:10;i:35;i:11;i:36;i:12;i:37;i:13;i:38;}s:17:"executableLinesIn";a:10:{i:18;i:1;i:28;i:2;i:29;i:2;i:30;i:2;i:31;i:2;i:32;i:2;i:33;i:2;i:34;i:2;i:35;i:2;i:37;i:3;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/2b8f89c7eb6feacfb35a96b5ed5ba6dc b/.phpunit.cache/code-coverage/2b8f89c7eb6feacfb35a96b5ed5ba6dc deleted file mode 100644 index 40b94de..0000000 --- a/.phpunit.cache/code-coverage/2b8f89c7eb6feacfb35a96b5ed5ba6dc +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:46:"Ang3\Component\Odoo\Transport\JsonRpcTransport";a:6:{s:4:"name";s:16:"JsonRpcTransport";s:14:"namespacedName";s:46:"Ang3\Component\Odoo\Transport\JsonRpcTransport";s:9:"namespace";s:29:"Ang3\Component\Odoo\Transport";s:9:"startLine";i:24;s:7:"endLine";i:76;s:7:"methods";a:2:{s:11:"__construct";a:6:{s:10:"methodName";s:11:"__construct";s:9:"signature";s:147:"__construct(Ang3\Component\Odoo\Connection $connection, ?Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClientInterface $httpClient, int $timeOut)";s:10:"visibility";s:6:"public";s:9:"startLine";i:30;s:7:"endLine";i:36;s:3:"ccn";i:2;}s:7:"request";a:6:{s:10:"methodName";s:7:"request";s:9:"signature";s:65:"request(string $service, string $method, array $arguments): mixed";s:10:"visibility";s:6:"public";s:9:"startLine";i:41;s:7:"endLine";i:75;s:3:"ccn";i:5;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:77;s:18:"commentLinesOfCode";i:13;s:21:"nonCommentLinesOfCode";i:64;}s:15:"ignoredLinesFor";a:1:{i:0;i:24;}s:17:"executableLinesIn";a:23:{i:35;i:4;i:43;i:6;i:44;i:6;i:45;i:6;i:46;i:6;i:47;i:6;i:48;i:6;i:49;i:6;i:50;i:6;i:51;i:6;i:52;i:6;i:54;i:7;i:55;i:8;i:58;i:9;i:59;i:10;i:61;i:11;i:62;i:12;i:65;i:13;i:66;i:14;i:67;i:15;i:70;i:16;i:71;i:17;i:74;i:18;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/369798d81ca7dddb00be7f6947eede91 b/.phpunit.cache/code-coverage/369798d81ca7dddb00be7f6947eede91 deleted file mode 100644 index 79a6c26..0000000 --- a/.phpunit.cache/code-coverage/369798d81ca7dddb00be7f6947eede91 +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:0:{}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:25;s:18:"commentLinesOfCode";i:11;s:21:"nonCommentLinesOfCode";i:14;}s:15:"ignoredLinesFor";a:6:{i:0;i:19;i:1;i:20;i:2;i:21;i:3;i:22;i:4;i:23;i:5;i:24;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/4bf226508f77741cf5afa74dafa86178 b/.phpunit.cache/code-coverage/4bf226508f77741cf5afa74dafa86178 deleted file mode 100644 index fd47b68..0000000 --- a/.phpunit.cache/code-coverage/4bf226508f77741cf5afa74dafa86178 +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:30:"Ang3\Component\Odoo\Connection";a:6:{s:4:"name";s:10:"Connection";s:14:"namespacedName";s:30:"Ang3\Component\Odoo\Connection";s:9:"namespace";s:19:"Ang3\Component\Odoo";s:9:"startLine";i:20;s:7:"endLine";i:151;s:7:"methods";a:11:{s:11:"__construct";a:6:{s:10:"methodName";s:11:"__construct";s:9:"signature";s:95:"__construct(string $host, string $username, string $password, string $database, string $scheme)";s:10:"visibility";s:6:"public";s:9:"startLine";i:22;s:7:"endLine";i:29;s:3:"ccn";i:1;}s:10:"__toString";a:6:{s:10:"methodName";s:10:"__toString";s:9:"signature";s:20:"__toString(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:31;s:7:"endLine";i:34;s:3:"ccn";i:1;}s:6:"create";a:6:{s:10:"methodName";s:6:"create";s:9:"signature";s:27:"create(array $config): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:41;s:7:"endLine";i:64;s:3:"ccn";i:3;}s:8:"parseDsn";a:6:{s:10:"methodName";s:8:"parseDsn";s:9:"signature";s:27:"parseDsn(string $dsn): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:69;s:7:"endLine";i:112;s:3:"ccn";i:9;}s:13:"getIdentifier";a:6:{s:10:"methodName";s:13:"getIdentifier";s:9:"signature";s:23:"getIdentifier(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:117;s:7:"endLine";i:120;s:3:"ccn";i:1;}s:7:"getHost";a:6:{s:10:"methodName";s:7:"getHost";s:9:"signature";s:17:"getHost(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:122;s:7:"endLine";i:125;s:3:"ccn";i:1;}s:11:"getUsername";a:6:{s:10:"methodName";s:11:"getUsername";s:9:"signature";s:21:"getUsername(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:127;s:7:"endLine";i:130;s:3:"ccn";i:1;}s:11:"getPassword";a:6:{s:10:"methodName";s:11:"getPassword";s:9:"signature";s:21:"getPassword(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:132;s:7:"endLine";i:135;s:3:"ccn";i:1;}s:11:"getDatabase";a:6:{s:10:"methodName";s:11:"getDatabase";s:9:"signature";s:21:"getDatabase(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:137;s:7:"endLine";i:140;s:3:"ccn";i:1;}s:9:"getScheme";a:6:{s:10:"methodName";s:9:"getScheme";s:9:"signature";s:19:"getScheme(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:142;s:7:"endLine";i:145;s:3:"ccn";i:1;}s:6:"getUrl";a:6:{s:10:"methodName";s:6:"getUrl";s:9:"signature";s:16:"getUrl(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:147;s:7:"endLine";i:150;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:152;s:18:"commentLinesOfCode";i:20;s:21:"nonCommentLinesOfCode";i:132;}s:15:"ignoredLinesFor";a:1:{i:0;i:20;}s:17:"executableLinesIn";a:48:{i:29;i:1;i:33;i:2;i:43;i:3;i:55;i:3;i:44;i:4;i:46;i:5;i:47;i:6;i:50;i:7;i:51;i:8;i:54;i:9;i:57;i:10;i:58;i:10;i:59;i:10;i:60;i:10;i:61;i:10;i:62;i:10;i:63;i:10;i:72;i:11;i:73;i:12;i:74;i:13;i:77;i:14;i:78;i:14;i:79;i:14;i:80;i:14;i:81;i:14;i:82;i:14;i:83;i:14;i:85;i:15;i:86;i:16;i:89;i:17;i:90;i:18;i:93;i:19;i:94;i:20;i:97;i:21;i:98;i:22;i:101;i:23;i:102;i:24;i:105;i:25;i:106;i:26;i:109;i:27;i:111;i:28;i:119;i:29;i:124;i:30;i:129;i:31;i:134;i:32;i:139;i:33;i:144;i:34;i:149;i:35;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/52118a736c1a03a7e6b2f995149dfe68 b/.phpunit.cache/code-coverage/52118a736c1a03a7e6b2f995149dfe68 deleted file mode 100644 index 7ce9224..0000000 --- a/.phpunit.cache/code-coverage/52118a736c1a03a7e6b2f995149dfe68 +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:53:"Ang3\Component\Odoo\Exception\AuthenticationException";a:6:{s:4:"name";s:23:"AuthenticationException";s:14:"namespacedName";s:53:"Ang3\Component\Odoo\Exception\AuthenticationException";s:9:"namespace";s:29:"Ang3\Component\Odoo\Exception";s:9:"startLine";i:17;s:7:"endLine";i:23;s:7:"methods";a:1:{s:11:"__construct";a:6:{s:10:"methodName";s:11:"__construct";s:9:"signature";s:33:"__construct(?Throwable $previous)";s:10:"visibility";s:6:"public";s:9:"startLine";i:19;s:7:"endLine";i:22;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:24;s:18:"commentLinesOfCode";i:9;s:21:"nonCommentLinesOfCode";i:15;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:1:{i:21;i:1;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/58806f641bbffe5922122c226ac27b4a b/.phpunit.cache/code-coverage/58806f641bbffe5922122c226ac27b4a deleted file mode 100644 index a7e9fee..0000000 --- a/.phpunit.cache/code-coverage/58806f641bbffe5922122c226ac27b4a +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:36:"Ang3\Component\Odoo\Metadata\Version";a:6:{s:4:"name";s:7:"Version";s:14:"namespacedName";s:36:"Ang3\Component\Odoo\Metadata\Version";s:9:"namespace";s:28:"Ang3\Component\Odoo\Metadata";s:9:"startLine";i:17;s:7:"endLine";i:97;s:7:"methods";a:11:{s:11:"__construct";a:6:{s:10:"methodName";s:11:"__construct";s:9:"signature";s:156:"__construct(int $majorVersion, int $minorVersion, int $patchVersion, string $buildName, string $buildIdentifier, string $buildVersion, int $protocolVersion)";s:10:"visibility";s:6:"public";s:9:"startLine";i:19;s:7:"endLine";i:28;s:3:"ccn";i:1;}s:6:"create";a:6:{s:10:"methodName";s:6:"create";s:9:"signature";s:28:"create(array $payload): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:35;s:7:"endLine";i:51;s:3:"ccn";i:1;}s:10:"__toString";a:6:{s:10:"methodName";s:10:"__toString";s:9:"signature";s:20:"__toString(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:53;s:7:"endLine";i:56;s:3:"ccn";i:1;}s:7:"getName";a:6:{s:10:"methodName";s:7:"getName";s:9:"signature";s:17:"getName(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:58;s:7:"endLine";i:61;s:3:"ccn";i:1;}s:15:"getMajorVersion";a:6:{s:10:"methodName";s:15:"getMajorVersion";s:9:"signature";s:22:"getMajorVersion(): int";s:10:"visibility";s:6:"public";s:9:"startLine";i:63;s:7:"endLine";i:66;s:3:"ccn";i:1;}s:15:"getMinorVersion";a:6:{s:10:"methodName";s:15:"getMinorVersion";s:9:"signature";s:22:"getMinorVersion(): int";s:10:"visibility";s:6:"public";s:9:"startLine";i:68;s:7:"endLine";i:71;s:3:"ccn";i:1;}s:15:"getPatchVersion";a:6:{s:10:"methodName";s:15:"getPatchVersion";s:9:"signature";s:22:"getPatchVersion(): int";s:10:"visibility";s:6:"public";s:9:"startLine";i:73;s:7:"endLine";i:76;s:3:"ccn";i:1;}s:12:"getBuildName";a:6:{s:10:"methodName";s:12:"getBuildName";s:9:"signature";s:22:"getBuildName(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:78;s:7:"endLine";i:81;s:3:"ccn";i:1;}s:18:"getBuildIdentifier";a:6:{s:10:"methodName";s:18:"getBuildIdentifier";s:9:"signature";s:28:"getBuildIdentifier(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:83;s:7:"endLine";i:86;s:3:"ccn";i:1;}s:15:"getBuildVersion";a:6:{s:10:"methodName";s:15:"getBuildVersion";s:9:"signature";s:25:"getBuildVersion(): string";s:10:"visibility";s:6:"public";s:9:"startLine";i:88;s:7:"endLine";i:91;s:3:"ccn";i:1;}s:18:"getProtocolVersion";a:6:{s:10:"methodName";s:18:"getProtocolVersion";s:9:"signature";s:25:"getProtocolVersion(): int";s:10:"visibility";s:6:"public";s:9:"startLine";i:93;s:7:"endLine";i:96;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:98;s:18:"commentLinesOfCode";i:16;s:21:"nonCommentLinesOfCode";i:82;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:21:{i:28;i:1;i:38;i:2;i:40;i:3;i:42;i:4;i:43;i:4;i:44;i:4;i:45;i:4;i:46;i:4;i:47;i:4;i:48;i:4;i:49;i:4;i:50;i:4;i:55;i:5;i:60;i:6;i:65;i:7;i:70;i:8;i:75;i:9;i:80;i:10;i:85;i:11;i:90;i:12;i:95;i:13;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/717ffe8417576cb1f922c16743e6f6ff b/.phpunit.cache/code-coverage/717ffe8417576cb1f922c16743e6f6ff deleted file mode 100644 index 5bd38d0..0000000 --- a/.phpunit.cache/code-coverage/717ffe8417576cb1f922c16743e6f6ff +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:0:{}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:31;s:18:"commentLinesOfCode";i:14;s:21:"nonCommentLinesOfCode";i:17;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/99a617401202e025c1ebee54d349c25b b/.phpunit.cache/code-coverage/99a617401202e025c1ebee54d349c25b deleted file mode 100644 index e7f28a3..0000000 --- a/.phpunit.cache/code-coverage/99a617401202e025c1ebee54d349c25b +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:0:{}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:24;s:18:"commentLinesOfCode";i:11;s:21:"nonCommentLinesOfCode";i:13;}s:15:"ignoredLinesFor";a:5:{i:0;i:19;i:1;i:20;i:2;i:21;i:3;i:22;i:4;i:23;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/a8bb1700d6e80837ca1c1a13e68a74e6 b/.phpunit.cache/code-coverage/a8bb1700d6e80837ca1c1a13e68a74e6 deleted file mode 100644 index a290216..0000000 --- a/.phpunit.cache/code-coverage/a8bb1700d6e80837ca1c1a13e68a74e6 +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:46:"Ang3\Component\Odoo\Exception\RequestException";a:6:{s:4:"name";s:16:"RequestException";s:14:"namespacedName";s:46:"Ang3\Component\Odoo\Exception\RequestException";s:9:"namespace";s:29:"Ang3\Component\Odoo\Exception";s:9:"startLine";i:17;s:7:"endLine";i:19;s:7:"methods";a:0:{}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:20;s:18:"commentLinesOfCode";i:9;s:21:"nonCommentLinesOfCode";i:11;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/aafdcf027cacd0bd24b17d8fccd53308 b/.phpunit.cache/code-coverage/aafdcf027cacd0bd24b17d8fccd53308 deleted file mode 100644 index 216a7d2..0000000 --- a/.phpunit.cache/code-coverage/aafdcf027cacd0bd24b17d8fccd53308 +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:0:{}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:22;s:18:"commentLinesOfCode";i:11;s:21:"nonCommentLinesOfCode";i:11;}s:15:"ignoredLinesFor";a:1:{i:0;i:19;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/ad5d7750bc032bbc4e872fe2b81bd1d4 b/.phpunit.cache/code-coverage/ad5d7750bc032bbc4e872fe2b81bd1d4 deleted file mode 100644 index 86d4df2..0000000 --- a/.phpunit.cache/code-coverage/ad5d7750bc032bbc4e872fe2b81bd1d4 +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";a:6:{s:4:"name";s:17:"JsonRpcHttpClient";s:14:"namespacedName";s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";s:9:"namespace";s:36:"Ang3\Component\Odoo\Transport\Client";s:9:"startLine";i:14;s:7:"endLine";i:29;s:7:"methods";a:1:{s:4:"post";a:6:{s:10:"methodName";s:4:"post";s:9:"signature";s:62:"post(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:6:"public";s:9:"startLine";i:16;s:7:"endLine";i:28;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:30;s:18:"commentLinesOfCode";i:6;s:21:"nonCommentLinesOfCode";i:24;}s:15:"ignoredLinesFor";a:1:{i:0;i:14;}s:17:"executableLinesIn";a:9:{i:18;i:1;i:19;i:1;i:20;i:1;i:21;i:1;i:22;i:1;i:23;i:1;i:24;i:1;i:25;i:1;i:27;i:2;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/af0a8e802569cf7c13c2b0d56d761e60 b/.phpunit.cache/code-coverage/af0a8e802569cf7c13c2b0d56d761e60 deleted file mode 100644 index 1b8f6f4..0000000 --- a/.phpunit.cache/code-coverage/af0a8e802569cf7c13c2b0d56d761e60 +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";a:6:{s:4:"name";s:17:"JsonRpcHttpClient";s:14:"namespacedName";s:54:"Ang3\Component\Odoo\Transport\Client\JsonRpcHttpClient";s:9:"namespace";s:36:"Ang3\Component\Odoo\Transport\Client";s:9:"startLine";i:14;s:7:"endLine";i:37;s:7:"methods";a:2:{s:4:"post";a:6:{s:10:"methodName";s:4:"post";s:9:"signature";s:62:"post(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:6:"public";s:9:"startLine";i:16;s:7:"endLine";i:19;s:3:"ccn";i:1;}s:9:"doRequest";a:6:{s:10:"methodName";s:9:"doRequest";s:9:"signature";s:67:"doRequest(string $url, string $payload, int $timeout): string|false";s:10:"visibility";s:9:"protected";s:9:"startLine";i:24;s:7:"endLine";i:36;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:38;s:18:"commentLinesOfCode";i:9;s:21:"nonCommentLinesOfCode";i:29;}s:15:"ignoredLinesFor";a:1:{i:0;i:14;}s:17:"executableLinesIn";a:10:{i:18;i:1;i:26;i:2;i:27;i:2;i:28;i:2;i:29;i:2;i:30;i:2;i:31;i:2;i:32;i:2;i:33;i:2;i:35;i:3;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/cc2997b47b202f04df6c5d311bddc88f b/.phpunit.cache/code-coverage/cc2997b47b202f04df6c5d311bddc88f deleted file mode 100644 index 1485b39..0000000 --- a/.phpunit.cache/code-coverage/cc2997b47b202f04df6c5d311bddc88f +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:48:"Ang3\Component\Odoo\Exception\TransportException";a:6:{s:4:"name";s:18:"TransportException";s:14:"namespacedName";s:48:"Ang3\Component\Odoo\Exception\TransportException";s:9:"namespace";s:29:"Ang3\Component\Odoo\Exception";s:9:"startLine";i:17;s:7:"endLine";i:19;s:7:"methods";a:0:{}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:20;s:18:"commentLinesOfCode";i:9;s:21:"nonCommentLinesOfCode";i:11;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/e0b8c8e2f445c41b4306b462a52a7a83 b/.phpunit.cache/code-coverage/e0b8c8e2f445c41b4306b462a52a7a83 deleted file mode 100644 index ff7b3f9..0000000 --- a/.phpunit.cache/code-coverage/e0b8c8e2f445c41b4306b462a52a7a83 +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:0:{}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:21;s:18:"commentLinesOfCode";i:9;s:21:"nonCommentLinesOfCode";i:12;}s:15:"ignoredLinesFor";a:1:{i:0;i:17;}s:17:"executableLinesIn";a:0:{}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/e3753fe5e4e60324f36fee0947c42207 b/.phpunit.cache/code-coverage/e3753fe5e4e60324f36fee0947c42207 deleted file mode 100644 index 523ab5b..0000000 --- a/.phpunit.cache/code-coverage/e3753fe5e4e60324f36fee0947c42207 +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:26:"Ang3\Component\Odoo\Client";a:6:{s:4:"name";s:6:"Client";s:14:"namespacedName";s:26:"Ang3\Component\Odoo\Client";s:9:"namespace";s:19:"Ang3\Component\Odoo";s:9:"startLine";i:27;s:7:"endLine";i:147;s:7:"methods";a:11:{s:11:"__construct";a:6:{s:10:"methodName";s:11:"__construct";s:9:"signature";s:151:"__construct(Ang3\Component\Odoo\Connection $connection, ?Ang3\Component\Odoo\Transport\TransportInterface $transport, ?Psr\Log\LoggerInterface $logger)";s:10:"visibility";s:6:"public";s:9:"startLine";i:32;s:7:"endLine";i:38;s:3:"ccn";i:2;}s:9:"executeKw";a:6:{s:10:"methodName";s:9:"executeKw";s:9:"signature";s:81:"executeKw(string $name, string $method, array $parameters, array $options): mixed";s:10:"visibility";s:6:"public";s:9:"startLine";i:44;s:7:"endLine";i:57;s:3:"ccn";i:1;}s:7:"version";a:6:{s:10:"methodName";s:7:"version";s:9:"signature";s:47:"version(): Ang3\Component\Odoo\Metadata\Version";s:10:"visibility";s:6:"public";s:9:"startLine";i:59;s:7:"endLine";i:62;s:3:"ccn";i:1;}s:12:"authenticate";a:6:{s:10:"methodName";s:12:"authenticate";s:9:"signature";s:19:"authenticate(): int";s:10:"visibility";s:6:"public";s:9:"startLine";i:67;s:7:"endLine";i:87;s:3:"ccn";i:3;}s:7:"request";a:6:{s:10:"methodName";s:7:"request";s:9:"signature";s:65:"request(string $service, string $method, mixed $arguments): mixed";s:10:"visibility";s:6:"public";s:9:"startLine";i:93;s:7:"endLine";i:116;s:3:"ccn";i:1;}s:13:"getConnection";a:6:{s:10:"methodName";s:13:"getConnection";s:9:"signature";s:47:"getConnection(): Ang3\Component\Odoo\Connection";s:10:"visibility";s:6:"public";s:9:"startLine";i:118;s:7:"endLine";i:121;s:3:"ccn";i:1;}s:12:"getTransport";a:6:{s:10:"methodName";s:12:"getTransport";s:9:"signature";s:64:"getTransport(): Ang3\Component\Odoo\Transport\TransportInterface";s:10:"visibility";s:6:"public";s:9:"startLine";i:123;s:7:"endLine";i:126;s:3:"ccn";i:1;}s:13:"withTransport";a:6:{s:10:"methodName";s:13:"withTransport";s:9:"signature";s:80:"withTransport(Ang3\Component\Odoo\Transport\TransportInterface $transport): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:128;s:7:"endLine";i:131;s:3:"ccn";i:1;}s:9:"getLogger";a:6:{s:10:"methodName";s:9:"getLogger";s:9:"signature";s:37:"getLogger(): ?Psr\Log\LoggerInterface";s:10:"visibility";s:6:"public";s:9:"startLine";i:133;s:7:"endLine";i:136;s:3:"ccn";i:1;}s:10:"withLogger";a:6:{s:10:"methodName";s:10:"withLogger";s:9:"signature";s:49:"withLogger(Psr\Log\LoggerInterface $logger): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:138;s:7:"endLine";i:141;s:3:"ccn";i:1;}s:6:"getUid";a:6:{s:10:"methodName";s:6:"getUid";s:9:"signature";s:14:"getUid(): ?int";s:10:"visibility";s:6:"public";s:9:"startLine";i:143;s:7:"endLine";i:146;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:148;s:18:"commentLinesOfCode";i:21;s:21:"nonCommentLinesOfCode";i:127;}s:15:"ignoredLinesFor";a:1:{i:0;i:27;}s:17:"executableLinesIn";a:48:{i:37;i:3;i:46;i:5;i:47;i:5;i:48;i:5;i:49;i:5;i:50;i:5;i:51;i:5;i:52;i:5;i:53;i:5;i:54;i:5;i:55;i:5;i:56;i:5;i:61;i:6;i:69;i:7;i:71;i:8;i:72;i:8;i:73;i:8;i:74;i:8;i:75;i:8;i:76;i:8;i:77;i:8;i:79;i:9;i:80;i:10;i:83;i:11;i:86;i:12;i:95;i:13;i:96;i:13;i:97;i:13;i:98;i:13;i:99;i:13;i:100;i:13;i:101;i:13;i:103;i:14;i:105;i:15;i:106;i:16;i:107;i:17;i:109;i:18;i:110;i:18;i:111;i:18;i:112;i:18;i:113;i:18;i:115;i:19;i:120;i:20;i:125;i:21;i:130;i:22;i:135;i:23;i:140;i:24;i:145;i:25;}} \ No newline at end of file diff --git a/.phpunit.cache/code-coverage/e8d63d9025d0e3a640ba472491b40e70 b/.phpunit.cache/code-coverage/e8d63d9025d0e3a640ba472491b40e70 deleted file mode 100644 index 009cea0..0000000 --- a/.phpunit.cache/code-coverage/e8d63d9025d0e3a640ba472491b40e70 +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:9:"classesIn";a:1:{s:45:"Ang3\Component\Odoo\Exception\RemoteException";a:6:{s:4:"name";s:15:"RemoteException";s:14:"namespacedName";s:45:"Ang3\Component\Odoo\Exception\RemoteException";s:9:"namespace";s:29:"Ang3\Component\Odoo\Exception";s:9:"startLine";i:21;s:7:"endLine";i:83;s:7:"methods";a:2:{s:6:"create";a:6:{s:10:"methodName";s:6:"create";s:9:"signature";s:28:"create(array $payload): self";s:10:"visibility";s:6:"public";s:9:"startLine";i:31;s:7:"endLine";i:74;s:3:"ccn";i:8;}s:14:"getRemoteTrace";a:6:{s:10:"methodName";s:14:"getRemoteTrace";s:9:"signature";s:23:"getRemoteTrace(): array";s:10:"visibility";s:6:"public";s:9:"startLine";i:79;s:7:"endLine";i:82;s:3:"ccn";i:1;}}}}s:8:"traitsIn";a:0:{}s:11:"functionsIn";a:0:{}s:14:"linesOfCodeFor";a:3:{s:11:"linesOfCode";i:84;s:18:"commentLinesOfCode";i:23;s:21:"nonCommentLinesOfCode";i:61;}s:15:"ignoredLinesFor";a:1:{i:0;i:21;}s:17:"executableLinesIn";a:27:{i:34;i:2;i:35;i:3;i:36;i:4;i:38;i:5;i:39;i:6;i:41;i:7;i:42;i:8;i:45;i:9;i:46;i:10;i:47;i:11;i:48;i:12;i:50;i:13;i:51;i:14;i:52;i:15;i:53;i:15;i:54;i:15;i:55;i:15;i:56;i:15;i:57;i:15;i:59;i:16;i:62;i:17;i:63;i:18;i:67;i:19;i:68;i:20;i:70;i:21;i:73;i:22;i:81;i:23;}} \ No newline at end of file diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results deleted file mode 100644 index ccb2553..0000000 --- a/.phpunit.cache/test-results +++ /dev/null @@ -1 +0,0 @@ -{"version":2,"defects":{"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsnWithMissingPartsThrowsException":7,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateSuccess":8,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateFailureThrowsException":7,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwCallsTransportWithCorrectArguments":8,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestUsesCachedUid":8,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwAuthenticatesAndExecutesRequest":8,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestAfterAuthenticationUsesCachedUid":8,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateFailureThrowsAuthenticationException":7,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testWithLoggerReturnsNewInstanceAndLoggerIsUsed":8,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfigSuccess":7,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromDsnSuccess":7},"times":{"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateReturnsUid":0.013,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateThrowsExceptionOnFailure":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwCallsTransportCorrectly":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testVersionReturnsVersionObject":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testWithLoggerReturnsNewInstance":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testGettersAndToString":0.051,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfig":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateThrowsExceptionOnMissingParam":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsn":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsnWithMissingPartsThrowsException":0.008,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testParseDsnWithUnsupportedSchemeThrowsException":0,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testConstructorAndGetters":0.002,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testCreateFromPayload":0,"Ang3\\Component\\Odoo\\Tests\\Metadata\\VersionTest::testToStringReturnsName":0,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestReturnsResult":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsTransportExceptionOnFalse":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsRemoteExceptionOnError":0.001,"Ang3\\Component\\Odoo\\Tests\\Transport\\JsonRpcTransportTest::testRequestThrowsTransportExceptionOnInvalidJson":0,"Ang3\\Component\\Odoo\\Tests\\Transport\\Client\\JsonRpcHttpClientTest::testPostReturnsResponse":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromDsn":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateSuccess":0.006,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateFailureThrowsException":0.002,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwCallsTransportWithCorrectArguments":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestUsesCachedUid":0,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testWithTransportReturnsNewInstance":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestWithoutAuthenticationThrowsException":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testExecuteKwAuthenticatesAndExecutesRequest":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestAfterAuthenticationUsesCachedUid":0,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testAuthenticateFailureThrowsAuthenticationException":0.001,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testRequestWithoutAuthenticationThrowsRequestException":0,"Ang3\\Component\\Odoo\\Tests\\ClientTest::testWithLoggerReturnsNewInstanceAndLoggerIsUsed":0.002,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testConstructorAndGetters":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfigSuccess":0.003,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfigDefaultsSchemeToHttps":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfigInvalidConfigThrows":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromConfigUnsupportedSchemeThrows":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromDsnSuccess":0.001,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testCreateFromDsnInvalidThrows":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testToStringAndGetUrl":0,"Ang3\\Component\\Odoo\\Tests\\ConnectionTest::testGetIdentifierIsStable":0}} \ No newline at end of file From 8b21b6d4ed0e99a7e8a892ca69af827b9383702b Mon Sep 17 00:00:00 2001 From: Ang3 Date: Tue, 3 Feb 2026 17:54:05 +0100 Subject: [PATCH 78/80] Updates github ci --- .github/workflows/php_lint.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/php_lint.yml b/.github/workflows/php_lint.yml index 3875d99..024b45a 100644 --- a/.github/workflows/php_lint.yml +++ b/.github/workflows/php_lint.yml @@ -3,9 +3,11 @@ name: PHP Linting on: push: branches-ignore: + - 'master' - 'main' pull_request: branches-ignore: + - 'master' - 'main' jobs: From 8900c6ab7f38cbe8cf2d5079da85d08c5faa97b1 Mon Sep 17 00:00:00 2001 From: Ang3 Date: Tue, 3 Feb 2026 17:55:37 +0100 Subject: [PATCH 79/80] Updates composer dependencies --- composer.lock | 174 +++++++++++++++++++++++++++----------------------- 1 file changed, 93 insertions(+), 81 deletions(-) diff --git a/composer.lock b/composer.lock index dc1a598..1f05f5c 100644 --- a/composer.lock +++ b/composer.lock @@ -577,16 +577,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.92.5", + "version": "v3.93.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58" + "reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58", - "reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/b3546ab487c0762c39f308dc1ec0ea2c461fc21a", + "reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a", "shasum": "" }, "require": { @@ -618,14 +618,14 @@ }, "require-dev": { "facile-it/paraunit": "^1.3.1 || ^2.7", - "infection/infection": "^0.31", + "infection/infection": "^0.32", "justinrainbow/json-schema": "^6.6", "keradus/cli-executor": "^2.3", "mikey179/vfsstream": "^1.6.12", "php-coveralls/php-coveralls": "^2.9", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.46", + "phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.48", "symfony/polyfill-php85": "^1.33", "symfony/var-dumper": "^5.4.48 || ^6.4.26 || ^7.4.0 || ^8.0", "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0" @@ -669,7 +669,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.5" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.93.1" }, "funding": [ { @@ -677,7 +677,7 @@ "type": "github" } ], - "time": "2026-01-08T21:57:37+00:00" + "time": "2026-01-28T23:50:50+00:00" }, { "name": "masterminds/html5", @@ -984,11 +984,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.33", + "version": "2.1.38", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f", - "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dfaf1f530e1663aa167bc3e52197adb221582629", + "reference": "dfaf1f530e1663aa167bc3e52197adb221582629", "shasum": "" }, "require": { @@ -1033,7 +1033,7 @@ "type": "github" } ], - "time": "2025-12-05T10:24:31+00:00" + "time": "2026-01-30T17:12:46+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1127,28 +1127,28 @@ }, { "name": "phpunit/php-file-iterator", - "version": "5.1.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -1176,15 +1176,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" } ], - "time": "2024-08-27T05:02:59+00:00" + "time": "2026-02-02T13:52:54+00:00" }, { "name": "phpunit/php-invoker", @@ -1372,16 +1384,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.47", + "version": "11.5.50", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a8c3c540923f8a3d499659b927228059bb3809d8" + "reference": "fdfc727f0fcacfeb8fcb30c7e5da173125b58be3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a8c3c540923f8a3d499659b927228059bb3809d8", - "reference": "a8c3c540923f8a3d499659b927228059bb3809d8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fdfc727f0fcacfeb8fcb30c7e5da173125b58be3", + "reference": "fdfc727f0fcacfeb8fcb30c7e5da173125b58be3", "shasum": "" }, "require": { @@ -1402,7 +1414,7 @@ "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", "sebastian/code-unit": "^3.0.3", - "sebastian/comparator": "^6.3.2", + "sebastian/comparator": "^6.3.3", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.1", "sebastian/exporter": "^6.3.2", @@ -1453,7 +1465,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.47" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.50" }, "funding": [ { @@ -1477,7 +1489,7 @@ "type": "tidelift" } ], - "time": "2026-01-15T12:00:46+00:00" + "time": "2026-01-27T05:59:18+00:00" }, { "name": "psr/container", @@ -2280,16 +2292,16 @@ }, { "name": "sebastian/comparator", - "version": "6.3.2", + "version": "6.3.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", - "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", "shasum": "" }, "require": { @@ -2348,7 +2360,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" }, "funding": [ { @@ -2368,7 +2380,7 @@ "type": "tidelift" } ], - "time": "2025-08-10T08:07:46+00:00" + "time": "2026-01-24T09:26:40+00:00" }, { "name": "sebastian/complexity", @@ -3148,16 +3160,16 @@ }, { "name": "symfony/browser-kit", - "version": "v7.4.3", + "version": "v7.4.4", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "d5b5c731005f224fbc25289587a8538e4f62c762" + "reference": "bed167eadaaba641f51fc842c9227aa5e251309e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/d5b5c731005f224fbc25289587a8538e4f62c762", - "reference": "d5b5c731005f224fbc25289587a8538e4f62c762", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/bed167eadaaba641f51fc842c9227aa5e251309e", + "reference": "bed167eadaaba641f51fc842c9227aa5e251309e", "shasum": "" }, "require": { @@ -3197,7 +3209,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v7.4.3" + "source": "https://github.com/symfony/browser-kit/tree/v7.4.4" }, "funding": [ { @@ -3217,20 +3229,20 @@ "type": "tidelift" } ], - "time": "2025-12-16T08:02:06+00:00" + "time": "2026-01-13T10:40:19+00:00" }, { "name": "symfony/console", - "version": "v7.4.3", + "version": "v7.4.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6" + "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/732a9ca6cd9dfd940c639062d5edbde2f6727fb6", - "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6", + "url": "https://api.github.com/repos/symfony/console/zipball/41e38717ac1dd7a46b6bda7d6a82af2d98a78894", + "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894", "shasum": "" }, "require": { @@ -3295,7 +3307,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.3" + "source": "https://github.com/symfony/console/tree/v7.4.4" }, "funding": [ { @@ -3315,7 +3327,7 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:50:43+00:00" + "time": "2026-01-13T11:36:38+00:00" }, { "name": "symfony/css-selector", @@ -3455,16 +3467,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v7.4.1", + "version": "v7.4.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "0c5e8f20c74c78172a8ee72b125909b505033597" + "reference": "71fd6a82fc357c8b5de22f78b228acfc43dee965" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/0c5e8f20c74c78172a8ee72b125909b505033597", - "reference": "0c5e8f20c74c78172a8ee72b125909b505033597", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/71fd6a82fc357c8b5de22f78b228acfc43dee965", + "reference": "71fd6a82fc357c8b5de22f78b228acfc43dee965", "shasum": "" }, "require": { @@ -3503,7 +3515,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.4.1" + "source": "https://github.com/symfony/dom-crawler/tree/v7.4.4" }, "funding": [ { @@ -3523,20 +3535,20 @@ "type": "tidelift" } ], - "time": "2025-12-06T15:47:47+00:00" + "time": "2026-01-05T08:47:25+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.4.0", + "version": "v7.4.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d" + "reference": "dc2c0eba1af673e736bb851d747d266108aea746" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9dddcddff1ef974ad87b3708e4b442dc38b2261d", - "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/dc2c0eba1af673e736bb851d747d266108aea746", + "reference": "dc2c0eba1af673e736bb851d747d266108aea746", "shasum": "" }, "require": { @@ -3588,7 +3600,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.4" }, "funding": [ { @@ -3608,7 +3620,7 @@ "type": "tidelift" } ], - "time": "2025-10-28T09:38:46+00:00" + "time": "2026-01-05T11:45:34+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -3758,16 +3770,16 @@ }, { "name": "symfony/finder", - "version": "v7.4.3", + "version": "v7.4.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "fffe05569336549b20a1be64250b40516d6e8d06" + "reference": "ad4daa7c38668dcb031e63bc99ea9bd42196a2cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/fffe05569336549b20a1be64250b40516d6e8d06", - "reference": "fffe05569336549b20a1be64250b40516d6e8d06", + "url": "https://api.github.com/repos/symfony/finder/zipball/ad4daa7c38668dcb031e63bc99ea9bd42196a2cb", + "reference": "ad4daa7c38668dcb031e63bc99ea9bd42196a2cb", "shasum": "" }, "require": { @@ -3802,7 +3814,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.3" + "source": "https://github.com/symfony/finder/tree/v7.4.5" }, "funding": [ { @@ -3822,7 +3834,7 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:50:43+00:00" + "time": "2026-01-26T15:07:59+00:00" }, { "name": "symfony/options-resolver", @@ -4476,16 +4488,16 @@ }, { "name": "symfony/process", - "version": "v7.4.3", + "version": "v7.4.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f" + "reference": "608476f4604102976d687c483ac63a79ba18cc97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/2f8e1a6cdf590ca63715da4d3a7a3327404a523f", - "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f", + "url": "https://api.github.com/repos/symfony/process/zipball/608476f4604102976d687c483ac63a79ba18cc97", + "reference": "608476f4604102976d687c483ac63a79ba18cc97", "shasum": "" }, "require": { @@ -4517,7 +4529,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.3" + "source": "https://github.com/symfony/process/tree/v7.4.5" }, "funding": [ { @@ -4537,7 +4549,7 @@ "type": "tidelift" } ], - "time": "2025-12-19T10:00:43+00:00" + "time": "2026-01-26T15:07:59+00:00" }, { "name": "symfony/service-contracts", @@ -4694,16 +4706,16 @@ }, { "name": "symfony/string", - "version": "v7.4.0", + "version": "v7.4.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003" + "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d50e862cb0a0e0886f73ca1f31b865efbb795003", - "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003", + "url": "https://api.github.com/repos/symfony/string/zipball/1c4b10461bf2ec27537b5f36105337262f5f5d6f", + "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f", "shasum": "" }, "require": { @@ -4761,7 +4773,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.0" + "source": "https://github.com/symfony/string/tree/v7.4.4" }, "funding": [ { @@ -4781,7 +4793,7 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2026-01-12T10:54:30+00:00" }, { "name": "symfony/test-pack", @@ -4838,16 +4850,16 @@ }, { "name": "symfony/var-dumper", - "version": "v6.4.26", + "version": "v6.4.32", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a" + "reference": "131fc9915e0343052af5ed5040401b481ca192aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfae1497a2f1eaad78dbc0590311c599c7178d4a", - "reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/131fc9915e0343052af5ed5040401b481ca192aa", + "reference": "131fc9915e0343052af5ed5040401b481ca192aa", "shasum": "" }, "require": { @@ -4902,7 +4914,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.26" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.32" }, "funding": [ { @@ -4922,7 +4934,7 @@ "type": "tidelift" } ], - "time": "2025-09-25T15:37:27+00:00" + "time": "2026-01-01T13:34:06+00:00" }, { "name": "theseer/tokenizer", From ba48efb3941787456dfc2373ce79e12086df6625 Mon Sep 17 00:00:00 2001 From: Ang3 Date: Tue, 3 Feb 2026 17:56:32 +0100 Subject: [PATCH 80/80] Updates github ci --- .github/workflows/php_lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/php_lint.yml b/.github/workflows/php_lint.yml index 024b45a..3f46814 100644 --- a/.github/workflows/php_lint.yml +++ b/.github/workflows/php_lint.yml @@ -18,7 +18,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.1' + php-version: '8.2' - name: Install PHP-CS-Fixer run: | wget https://cs.symfony.com/download/php-cs-fixer-v3.phar -O php-cs-fixer