From 74604b409b85b4cf9cb77f16b91229098087f299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 23 Feb 2023 20:56:00 +0100 Subject: [PATCH 01/17] refactor: `AbstractPluginManager` now uses `ServiceManager` to provide plugins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the inheritance of the `ServiceManager` and marks the `ServiceManager` `final` in a soft state. Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- .laminas-ci.json | 6 - benchmarks/BenchAsset/AbstractFactoryFoo.php | 4 +- benchmarks/BenchAsset/FactoryFoo.php | 2 +- composer.json | 8 +- composer.lock | 853 ++++++++---------- psalm.xml.dist | 15 + src/AbstractFactory/ConfigAbstractFactory.php | 4 +- .../ReflectionBasedAbstractFactory.php | 4 +- src/AbstractPluginManager.php | 294 +++--- src/Config.php | 56 +- src/ConfigInterface.php | 39 +- src/Factory/AbstractFactoryInterface.php | 5 +- src/Factory/DelegatorFactoryInterface.php | 12 +- src/Factory/FactoryInterface.php | 13 +- src/Factory/InvokableFactory.php | 2 +- src/Initializer/InitializerInterface.php | 4 +- src/PluginManagerInterface.php | 42 +- src/Proxy/LazyServiceFactory.php | 11 +- src/ServiceLocatorInterface.php | 22 +- src/ServiceManager.php | 216 +++-- src/Test/CommonPluginManagerTrait.php | 29 +- src/Tool/FactoryCreator.php | 8 +- test/AbstractPluginManagerTest.php | 154 +--- test/CommonServiceLocatorBehaviorsTrait.php | 179 ++-- test/ConfigTest.php | 80 +- test/ExamplePluginManagerTest.php | 13 +- test/Exception/CyclicAliasExceptionTest.php | 4 +- test/ServiceManagerTest.php | 66 +- test/TestAsset/AbstractFactoryFoo.php | 7 +- test/TestAsset/CallTimesAbstractFactory.php | 4 +- test/TestAsset/ExtendedConfig.php | 22 - test/TestAsset/FailingAbstractFactory.php | 4 +- ...ailingExceptionWithStringAsCodeFactory.php | 2 +- test/TestAsset/FailingFactory.php | 2 +- .../InvokableObjectPluginManager.php | 39 + test/TestAsset/LenientPluginManager.php | 17 +- .../NonAutoInvokablePluginManager.php | 6 +- .../TestAsset/PassthroughDelegatorFactory.php | 10 +- test/TestAsset/PreDelegator.php | 8 +- test/TestAsset/SampleFactory.php | 8 +- test/TestAsset/SimpleAbstractFactory.php | 4 +- test/TestAsset/SimplePluginManager.php | 3 +- test/TestAsset/V2v3PluginManager.php | 74 -- .../factories/ComplexDependencyObject.php | 8 +- test/TestAsset/factories/InvokableObject.php | 8 +- .../factories/SimpleDependencyObject.php | 8 +- 46 files changed, 1011 insertions(+), 1368 deletions(-) delete mode 100644 .laminas-ci.json delete mode 100644 test/TestAsset/ExtendedConfig.php create mode 100644 test/TestAsset/InvokableObjectPluginManager.php delete mode 100644 test/TestAsset/V2v3PluginManager.php diff --git a/.laminas-ci.json b/.laminas-ci.json deleted file mode 100644 index 9143dca4..00000000 --- a/.laminas-ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "ignore_php_platform_requirements": { - "8.1": true, - "8.2": true - } -} diff --git a/benchmarks/BenchAsset/AbstractFactoryFoo.php b/benchmarks/BenchAsset/AbstractFactoryFoo.php index 42cee972..f7b2ecb7 100644 --- a/benchmarks/BenchAsset/AbstractFactoryFoo.php +++ b/benchmarks/BenchAsset/AbstractFactoryFoo.php @@ -10,7 +10,7 @@ class AbstractFactoryFoo implements AbstractFactoryInterface { /** {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null) + public function __invoke(ContainerInterface $container, string $requestedName, ?array $options = null): mixed { if ($requestedName === 'foo') { return new Foo($options); @@ -19,7 +19,7 @@ public function __invoke(ContainerInterface $container, $requestedName, ?array $ } /** {@inheritDoc} */ - public function canCreate(ContainerInterface $container, $requestedName) + public function canCreate(ContainerInterface $container, string $requestedName): bool { return $requestedName === 'foo'; } diff --git a/benchmarks/BenchAsset/FactoryFoo.php b/benchmarks/BenchAsset/FactoryFoo.php index 7afc5263..2aaf8b53 100644 --- a/benchmarks/BenchAsset/FactoryFoo.php +++ b/benchmarks/BenchAsset/FactoryFoo.php @@ -10,7 +10,7 @@ class FactoryFoo implements FactoryInterface { /** {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null) + public function __invoke(ContainerInterface $container, string $requestedName, ?array $options = null): mixed { return new Foo($options); } diff --git a/composer.json b/composer.json index d6a5cacc..cdee3423 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ }, "config": { "platform": { - "php": "8.0.99" + "php": "8.1.99" }, "sort-packages": true, "allow-plugins": { @@ -32,7 +32,7 @@ } }, "require": { - "php": "~8.0.0 || ~8.1.0 || ~8.2.0", + "php": "~8.1.0 || ~8.2.0", "brick/varexporter": "^0.3.8", "laminas/laminas-stdlib": "^3.2.1", "psr/container": "^1.0" @@ -52,7 +52,7 @@ "laminas/laminas-dependency-plugin": "^2.2", "lctrs/psalm-psr-container-plugin": "^1.9", "mikey179/vfsstream": "^1.6.11@alpha", - "ocramius/proxy-manager": "^2.14.1", + "friendsofphp/proxy-manager-lts": "^1", "phpbench/phpbench": "^1.2.7", "phpunit/phpunit": "^9.5.26", "psalm/plugin-phpunit": "^0.18.0", @@ -68,7 +68,7 @@ }, "suggest": { "laminas/laminas-cli": "To consume CLI commands provided by this component", - "ocramius/proxy-manager": "ProxyManager ^2.1.1 to handle lazy initialization of services" + "friendsofphp/proxy-manager-lts": "To handle lazy initialization of services" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 38db349e..02075f58 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": "b6ac4e51e97fa9ed48da4804d35b859c", + "content-hash": "b45b0010cee4181db031a2a43aaeea6e", "packages": [ { "name": "brick/varexporter", @@ -57,30 +57,30 @@ }, { "name": "laminas/laminas-stdlib", - "version": "3.16.1", + "version": "3.17.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-stdlib.git", - "reference": "f4f773641807c7ccee59b758bfe4ac4ba33ecb17" + "reference": "dd35c868075bad80b6718959740913e178eb4274" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/f4f773641807c7ccee59b758bfe4ac4ba33ecb17", - "reference": "f4f773641807c7ccee59b758bfe4ac4ba33ecb17", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/dd35c868075bad80b6718959740913e178eb4274", + "reference": "dd35c868075bad80b6718959740913e178eb4274", "shasum": "" }, "require": { - "php": "~8.0.0 || ~8.1.0 || ~8.2.0" + "php": "~8.1.0 || ~8.2.0" }, "conflict": { "zendframework/zend-stdlib": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "^2.4.0", - "phpbench/phpbench": "^1.2.7", - "phpunit/phpunit": "^9.5.26", - "psalm/plugin-phpunit": "^0.18.0", - "vimeo/psalm": "^5.0.0" + "laminas/laminas-coding-standard": "^2.5", + "phpbench/phpbench": "^1.2.9", + "phpunit/phpunit": "^10.0.16", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.8" }, "type": "library", "autoload": { @@ -112,20 +112,20 @@ "type": "community_bridge" } ], - "time": "2022-12-03T18:48:01+00:00" + "time": "2023-03-20T13:51:37+00:00" }, { "name": "nikic/php-parser", - "version": "v4.15.3", + "version": "v4.15.4", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" }, "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/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", "shasum": "" }, "require": { @@ -166,9 +166,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.15.4" }, - "time": "2023-01-16T22:05:37+00:00" + "time": "2023-03-05T19:49:14+00:00" }, { "name": "psr/container", @@ -849,30 +849,30 @@ }, { "name": "doctrine/annotations", - "version": "1.14.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "9e034d7a70032d422169f27d8759e8d84abb4f51" + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/9e034d7a70032d422169f27d8759e8d84abb4f51", - "reference": "9e034d7a70032d422169f27d8759e8d84abb4f51", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", "shasum": "" }, "require": { - "doctrine/lexer": "^1 || ^2", + "doctrine/lexer": "^2 || ^3", "ext-tokenizer": "*", - "php": "^7.1 || ^8.0", + "php": "^7.2 || ^8.0", "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "~1.4.10 || ^1.8.0", + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.0", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/cache": "^5.4 || ^6", "vimeo/psalm": "^4.10" }, "suggest": { @@ -919,79 +919,36 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.14.1" + "source": "https://github.com/doctrine/annotations/tree/2.0.1" }, - "time": "2022-12-12T12:46:12+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "shasum": "" - }, - "require": { - "php": "^7.1|^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5|^8.5|^9.5", - "psr/log": "^1|^2|^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" - }, - "time": "2022-05-02T15:47:09+00:00" + "time": "2023-02-02T22:02:53+00:00" }, { "name": "doctrine/instantiator", - "version": "1.5.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" + "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", "autoload": { @@ -1018,7 +975,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -1034,32 +991,31 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { "name": "doctrine/lexer", - "version": "2.1.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" + "reference": "84a527db05647743d50373e0ec53a152f2cde568" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", + "reference": "84a527db05647743d50373e0ec53a152f2cde568", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.5", "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^4.11 || ^5.0" + "vimeo/psalm": "^5.0" }, "type": "library", "autoload": { @@ -1096,7 +1052,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/2.1.0" + "source": "https://github.com/doctrine/lexer/tree/3.0.0" }, "funding": [ { @@ -1112,7 +1068,7 @@ "type": "tidelift" } ], - "time": "2022-12-14T08:49:07+00:00" + "time": "2022-12-15T16:57:16+00:00" }, { "name": "felixfbecker/advanced-json-rpc", @@ -1217,16 +1173,16 @@ }, { "name": "fidry/cpu-core-counter", - "version": "0.4.1", + "version": "0.5.1", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2" + "reference": "b58e5a3933e541dc286cc91fc4f3898bbc6f1623" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/79261cc280aded96d098e1b0e0ba0c4881b432c2", - "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/b58e5a3933e541dc286cc91fc4f3898bbc6f1623", + "reference": "b58e5a3933e541dc286cc91fc4f3898bbc6f1623", "shasum": "" }, "require": { @@ -1266,7 +1222,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/0.4.1" + "source": "https://github.com/theofidry/cpu-core-counter/tree/0.5.1" }, "funding": [ { @@ -1274,7 +1230,89 @@ "type": "github" } ], - "time": "2022-12-16T22:01:02+00:00" + "time": "2022-12-24T12:35:10+00:00" + }, + { + "name": "friendsofphp/proxy-manager-lts", + "version": "v1.0.14", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/proxy-manager-lts.git", + "reference": "a527c9d9d5348e012bd24482d83a5cd643bcbc9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/proxy-manager-lts/zipball/a527c9d9d5348e012bd24482d83a5cd643bcbc9e", + "reference": "a527c9d9d5348e012bd24482d83a5cd643bcbc9e", + "shasum": "" + }, + "require": { + "laminas/laminas-code": "~3.4.1|^4.0", + "php": ">=7.1", + "symfony/filesystem": "^4.4.17|^5.0|^6.0" + }, + "conflict": { + "laminas/laminas-stdlib": "<3.2.1", + "zendframework/zend-stdlib": "<3.2.1" + }, + "replace": { + "ocramius/proxy-manager": "^2.1" + }, + "require-dev": { + "ext-phar": "*", + "symfony/phpunit-bridge": "^5.4|^6.0" + }, + "type": "library", + "extra": { + "thanks": { + "name": "ocramius/proxy-manager", + "url": "https://github.com/Ocramius/ProxyManager" + } + }, + "autoload": { + "psr-4": { + "ProxyManager\\": "src/ProxyManager" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + } + ], + "description": "Adding support for a wider range of PHP versions to ocramius/proxy-manager", + "homepage": "https://github.com/FriendsOfPHP/proxy-manager-lts", + "keywords": [ + "aop", + "lazy loading", + "proxy", + "proxy pattern", + "service proxies" + ], + "support": { + "issues": "https://github.com/FriendsOfPHP/proxy-manager-lts/issues", + "source": "https://github.com/FriendsOfPHP/proxy-manager-lts/tree/v1.0.14" + }, + "funding": [ + { + "url": "https://github.com/Ocramius", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ocramius/proxy-manager", + "type": "tidelift" + } + ], + "time": "2023-01-30T10:40:19+00:00" }, { "name": "laminas/laminas-cli", @@ -1346,29 +1384,29 @@ }, { "name": "laminas/laminas-code", - "version": "4.7.1", + "version": "4.10.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-code.git", - "reference": "91aabc066d5620428120800c0eafc0411e441a62" + "reference": "ad8b36073f9ac792716478befadca0798cc15635" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-code/zipball/91aabc066d5620428120800c0eafc0411e441a62", - "reference": "91aabc066d5620428120800c0eafc0411e441a62", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/ad8b36073f9ac792716478befadca0798cc15635", + "reference": "ad8b36073f9ac792716478befadca0798cc15635", "shasum": "" }, "require": { - "php": ">=7.4, <8.2" + "php": "~8.1.0 || ~8.2.0" }, "require-dev": { - "doctrine/annotations": "^1.13.2", + "doctrine/annotations": "^2.0.0", "ext-phar": "*", "laminas/laminas-coding-standard": "^2.3.0", "laminas/laminas-stdlib": "^3.6.1", - "phpunit/phpunit": "^9.5.10", - "psalm/plugin-phpunit": "^0.17.0", - "vimeo/psalm": "^4.13.1" + "phpunit/phpunit": "^10.0.9", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.7.1" }, "suggest": { "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", @@ -1376,9 +1414,6 @@ }, "type": "library", "autoload": { - "files": [ - "polyfill/ReflectionEnumPolyfill.php" - ], "psr-4": { "Laminas\\Code\\": "src/" } @@ -1408,7 +1443,7 @@ "type": "community_bridge" } ], - "time": "2022-11-21T01:32:31+00:00" + "time": "2023-03-08T11:55:01+00:00" }, { "name": "laminas/laminas-coding-standard", @@ -1717,16 +1752,16 @@ }, { "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": { @@ -1764,7 +1799,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": [ { @@ -1772,20 +1807,20 @@ "type": "tidelift" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2023-03-08T13:26:56+00:00" }, { "name": "netresearch/jsonmapper", - "version": "v4.1.0", + "version": "v4.2.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/f60565f8c0566a31acf06884cdaa591867ecc956", + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956", "shasum": "" }, "require": { @@ -1821,143 +1856,9 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.2.0" }, - "time": "2022-12-08T20:46:14+00:00" - }, - { - "name": "ocramius/proxy-manager", - "version": "2.14.1", - "source": { - "type": "git", - "url": "https://github.com/Ocramius/ProxyManager.git", - "reference": "3990d60ef79001badbab4927a6a811682274a0d1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/3990d60ef79001badbab4927a6a811682274a0d1", - "reference": "3990d60ef79001badbab4927a6a811682274a0d1", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.1.0", - "laminas/laminas-code": "^4.4.2", - "php": "~8.0.0", - "webimpress/safe-writer": "^2.2.0" - }, - "conflict": { - "thecodingmachine/safe": "<1.3.3" - }, - "require-dev": { - "codelicia/xulieta": "^0.1.6", - "doctrine/coding-standard": "^9.0.0", - "ext-phar": "*", - "phpbench/phpbench": "^1.0.3", - "phpunit/phpunit": "^9.5.6", - "roave/infection-static-analysis-plugin": "^1.8", - "squizlabs/php_codesniffer": "^3.6.0", - "vimeo/psalm": "^4.8.1" - }, - "suggest": { - "laminas/laminas-json": "To have the JsonRpc adapter (Remote Object feature)", - "laminas/laminas-soap": "To have the Soap adapter (Remote Object feature)", - "laminas/laminas-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)", - "ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects" - }, - "type": "library", - "autoload": { - "psr-4": { - "ProxyManager\\": "src/ProxyManager" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A library providing utilities to generate, instantiate and generally operate with Object Proxies", - "homepage": "https://github.com/Ocramius/ProxyManager", - "keywords": [ - "aop", - "lazy loading", - "proxy", - "proxy pattern", - "service proxies" - ], - "support": { - "issues": "https://github.com/Ocramius/ProxyManager/issues", - "source": "https://github.com/Ocramius/ProxyManager/tree/2.14.1" - }, - "funding": [ - { - "url": "https://github.com/Ocramius", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ocramius/proxy-manager", - "type": "tidelift" - } - ], - "time": "2022-03-05T18:43:14+00:00" - }, - { - "name": "openlss/lib-array2xml", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/nullivex/lib-array2xml.git", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "autoload": { - "psr-0": { - "LSS": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Bryan Tong", - "email": "bryan@nullivex.com", - "homepage": "https://www.nullivex.com" - }, - { - "name": "Tony Butler", - "email": "spudz76@gmail.com", - "homepage": "https://www.nullivex.com" - } - ], - "description": "Array2XML conversion library credit to lalit.org", - "homepage": "https://www.nullivex.com", - "keywords": [ - "array", - "array conversion", - "xml", - "xml conversion" - ], - "support": { - "issues": "https://github.com/nullivex/lib-array2xml/issues", - "source": "https://github.com/nullivex/lib-array2xml/tree/master" - }, - "time": "2019-03-29T20:06:56+00:00" + "time": "2023-04-09T17:37:40+00:00" }, { "name": "phar-io/manifest", @@ -2123,25 +2024,25 @@ }, { "name": "phpbench/dom", - "version": "0.3.2", + "version": "0.3.3", "source": { "type": "git", "url": "https://github.com/phpbench/dom.git", - "reference": "b013b717832ddbaadf2a40984b04bc66af9a7110" + "reference": "786a96db538d0def931f5b19225233ec42ec7a72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/dom/zipball/b013b717832ddbaadf2a40984b04bc66af9a7110", - "reference": "b013b717832ddbaadf2a40984b04bc66af9a7110", + "url": "https://api.github.com/repos/phpbench/dom/zipball/786a96db538d0def931f5b19225233ec42ec7a72", + "reference": "786a96db538d0def931f5b19225233ec42ec7a72", "shasum": "" }, "require": { "ext-dom": "*", - "php": "^7.2||^8.0" + "php": "^7.3||^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.18", - "phpstan/phpstan": "^0.12.83", + "friendsofphp/php-cs-fixer": "^3.14", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^8.0||^9.0" }, "type": "library", @@ -2168,35 +2069,35 @@ "description": "DOM wrapper to simplify working with the PHP DOM implementation", "support": { "issues": "https://github.com/phpbench/dom/issues", - "source": "https://github.com/phpbench/dom/tree/0.3.2" + "source": "https://github.com/phpbench/dom/tree/0.3.3" }, - "time": "2021-09-24T15:26:07+00:00" + "time": "2023-03-06T23:46:57+00:00" }, { "name": "phpbench/phpbench", - "version": "1.2.7", + "version": "1.2.10", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "dce145304abbb16c8d9af69c19d96f47e9d0e670" + "reference": "95206f92479674599a75e02b74b9933e2d9883aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/dce145304abbb16c8d9af69c19d96f47e9d0e670", - "reference": "dce145304abbb16c8d9af69c19d96f47e9d0e670", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/95206f92479674599a75e02b74b9933e2d9883aa", + "reference": "95206f92479674599a75e02b74b9933e2d9883aa", "shasum": "" }, "require": { - "doctrine/annotations": "^1.13", + "doctrine/annotations": "^1.13 || ^2.0", "ext-dom": "*", "ext-json": "*", "ext-pcre": "*", "ext-reflection": "*", "ext-spl": "*", "ext-tokenizer": "*", - "php": "^7.3 || ^8.0", + "php": "^7.4 || ^8.0", "phpbench/container": "^2.1", - "phpbench/dom": "~0.3.1", + "phpbench/dom": "~0.3.3", "psr/log": "^1.1 || ^2.0 || ^3.0", "seld/jsonlint": "^1.1", "symfony/console": "^4.2 || ^5.0 || ^6.0", @@ -2214,7 +2115,7 @@ "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.0", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^8.5.8 || ^9.0", + "phpunit/phpunit": "^9.0", "symfony/error-handler": "^5.2 || ^6.0", "symfony/var-dumper": "^4.0 || ^5.0 || ^6.0" }, @@ -2252,7 +2153,7 @@ "description": "PHP Benchmarking Framework", "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.2.7" + "source": "https://github.com/phpbench/phpbench/tree/1.2.10" }, "funding": [ { @@ -2260,7 +2161,7 @@ "type": "github" } ], - "time": "2022-10-15T09:57:51+00:00" + "time": "2023-03-24T08:52:55+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -2473,23 +2374,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.21", + "version": "9.2.26", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "3f893e19712bb0c8bc86665d1562e9fd509c4ef0" + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/3f893e19712bb0c8bc86665d1562e9fd509c4ef0", - "reference": "3f893e19712bb0c8bc86665d1562e9fd509c4ef0", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", "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", @@ -2504,8 +2405,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": { @@ -2538,7 +2439,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.21" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" }, "funding": [ { @@ -2546,7 +2447,7 @@ "type": "github" } ], - "time": "2022-12-14T13:26:54+00:00" + "time": "2023-03-06T12:58:08+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2791,20 +2692,20 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.27", + "version": "9.6.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a2bc7ffdca99f92d959b3f2270529334030bba38" + "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a2bc7ffdca99f92d959b3f2270529334030bba38", - "reference": "a2bc7ffdca99f92d959b3f2270529334030bba38", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", + "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", + "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -2833,8 +2734,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" @@ -2842,7 +2743,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.5-dev" + "dev-master": "9.6-dev" } }, "autoload": { @@ -2873,7 +2774,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.27" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.7" }, "funding": [ { @@ -2889,7 +2791,7 @@ "type": "tidelift" } ], - "time": "2022-12-09T07:31:23+00:00" + "time": "2023-04-14T08:58:40+00:00" }, { "name": "psalm/plugin-phpunit", @@ -3466,16 +3368,16 @@ }, { "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": { @@ -3517,7 +3419,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": [ { @@ -3525,7 +3427,7 @@ "type": "github" } ], - "time": "2022-04-03T09:37:03+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", @@ -3957,16 +3859,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": { @@ -4001,7 +3903,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": [ { @@ -4009,7 +3911,7 @@ "type": "github" } ], - "time": "2022-09-12T14:47:03+00:00" + "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", @@ -4189,18 +4091,81 @@ ], "time": "2022-05-25T10:58:12+00:00" }, + { + "name": "spatie/array-to-xml", + "version": "3.1.5", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/13f76acef5362d15c71ae1ac6350cc3df5e25e43", + "reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/3.1.5" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-12-24T13:43:51+00:00" + }, { "name": "squizlabs/php_codesniffer", - "version": "3.7.1", + "version": "3.7.2", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", "shasum": "" }, "require": { @@ -4236,31 +4201,33 @@ "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", "keywords": [ "phpcs", - "standards" + "standards", + "static analysis" ], "support": { "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", "source": "https://github.com/squizlabs/PHP_CodeSniffer", "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" }, - "time": "2022-06-18T07:21:10+00:00" + "time": "2023-02-22T23:07:41+00:00" }, { "name": "symfony/console", - "version": "v6.0.19", + "version": "v6.2.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed" + "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c3ebc83d031b71c39da318ca8b7a07ecc67507ed", - "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed", + "url": "https://api.github.com/repos/symfony/console/zipball/3582d68a64a86ec25240aaa521ec8bc2342b369b", + "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^1.1|^2|^3", "symfony/string": "^5.4|^6.0" @@ -4317,12 +4284,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.19" + "source": "https://github.com/symfony/console/tree/v6.2.8" }, "funding": [ { @@ -4338,29 +4305,29 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2023-03-29T21:42:15+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.2", + "version": "v3.2.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -4389,7 +4356,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.0.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" }, "funding": [ { @@ -4405,24 +4372,24 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2023-03-01T10:25:55+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.0.19", + "version": "v6.2.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "2eaf8e63bc5b8cefabd4a800157f0d0c094f677a" + "reference": "04046f35fd7d72f9646e721fc2ecb8f9c67d3339" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2eaf8e63bc5b8cefabd4a800157f0d0c094f677a", - "reference": "2eaf8e63bc5b8cefabd4a800157f0d0c094f677a", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/04046f35fd7d72f9646e721fc2ecb8f9c67d3339", + "reference": "04046f35fd7d72f9646e721fc2ecb8f9c67d3339", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/event-dispatcher-contracts": "^2|^3" }, "conflict": { @@ -4472,7 +4439,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/v6.0.19" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.2.8" }, "funding": [ { @@ -4488,24 +4455,24 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2023-03-20T16:06:02+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.0.2", + "version": "v3.2.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "7bc61cc2db649b4637d331240c5346dcc7708051" + "reference": "0ad3b6f1e4e2da5690fefe075cd53a238646d8dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7bc61cc2db649b4637d331240c5346dcc7708051", - "reference": "7bc61cc2db649b4637d331240c5346dcc7708051", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0ad3b6f1e4e2da5690fefe075cd53a238646d8dd", + "reference": "0ad3b6f1e4e2da5690fefe075cd53a238646d8dd", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "psr/event-dispatcher": "^1" }, "suggest": { @@ -4514,7 +4481,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -4551,7 +4518,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.2.1" }, "funding": [ { @@ -4567,24 +4534,24 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2023-03-01T10:32:47+00:00" }, { "name": "symfony/filesystem", - "version": "v6.0.13", + "version": "v6.2.7", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "3adca49133bd055ebe6011ed1e012be3c908af79" + "reference": "82b6c62b959f642d000456f08c6d219d749215b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/3adca49133bd055ebe6011ed1e012be3c908af79", - "reference": "3adca49133bd055ebe6011ed1e012be3c908af79", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3", + "reference": "82b6c62b959f642d000456f08c6d219d749215b3", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, @@ -4614,7 +4581,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.0.13" + "source": "https://github.com/symfony/filesystem/tree/v6.2.7" }, "funding": [ { @@ -4630,24 +4597,27 @@ "type": "tidelift" } ], - "time": "2022-09-21T20:25:27+00:00" + "time": "2023-02-14T08:44:56+00:00" }, { "name": "symfony/finder", - "version": "v6.0.11", + "version": "v6.2.7", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "09cb683ba5720385ea6966e5e06be2a34f2568b1" + "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/09cb683ba5720385ea6966e5e06be2a34f2568b1", - "reference": "09cb683ba5720385ea6966e5e06be2a34f2568b1", + "url": "https://api.github.com/repos/symfony/finder/zipball/20808dc6631aecafbe67c186af5dcb370be3a0eb", + "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" }, "type": "library", "autoload": { @@ -4675,7 +4645,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.0.11" + "source": "https://github.com/symfony/finder/tree/v6.2.7" }, "funding": [ { @@ -4691,24 +4661,24 @@ "type": "tidelift" } ], - "time": "2022-07-29T07:39:48+00:00" + "time": "2023-02-16T09:57:23+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.0.3", + "version": "v6.2.7", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "51f7006670febe4cbcbae177cbffe93ff833250d" + "reference": "aa0e85b53bbb2b4951960efd61d295907eacd629" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/51f7006670febe4cbcbae177cbffe93ff833250d", - "reference": "51f7006670febe4cbcbae177cbffe93ff833250d", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/aa0e85b53bbb2b4951960efd61d295907eacd629", + "reference": "aa0e85b53bbb2b4951960efd61d295907eacd629", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/deprecation-contracts": "^2.1|^3" }, "type": "library", @@ -4742,7 +4712,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.0.3" + "source": "https://github.com/symfony/options-resolver/tree/v6.2.7" }, "funding": [ { @@ -4758,7 +4728,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2023-02-14T08:44:56+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5175,20 +5145,20 @@ }, { "name": "symfony/process", - "version": "v6.0.11", + "version": "v6.2.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "44270a08ccb664143dede554ff1c00aaa2247a43" + "reference": "75ed64103df4f6615e15a7fe38b8111099f47416" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/44270a08ccb664143dede554ff1c00aaa2247a43", - "reference": "44270a08ccb664143dede554ff1c00aaa2247a43", + "url": "https://api.github.com/repos/symfony/process/zipball/75ed64103df4f6615e15a7fe38b8111099f47416", + "reference": "75ed64103df4f6615e15a7fe38b8111099f47416", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -5216,7 +5186,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.0.11" + "source": "https://github.com/symfony/process/tree/v6.2.8" }, "funding": [ { @@ -5232,7 +5202,7 @@ "type": "tidelift" } ], - "time": "2022-06-27T17:10:44+00:00" + "time": "2023-03-09T16:20:02+00:00" }, { "name": "symfony/service-contracts", @@ -5319,20 +5289,20 @@ }, { "name": "symfony/string", - "version": "v6.0.19", + "version": "v6.2.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a" + "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d9e72497367c23e08bf94176d2be45b00a9d232a", - "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a", + "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef", + "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -5344,6 +5314,7 @@ "require-dev": { "symfony/error-handler": "^5.4|^6.0", "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", "symfony/translation-contracts": "^2.0|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, @@ -5384,7 +5355,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.0.19" + "source": "https://github.com/symfony/string/tree/v6.2.8" }, "funding": [ { @@ -5400,7 +5371,7 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2023-03-20T16:06:02+00:00" }, { "name": "theseer/tokenizer", @@ -5454,22 +5425,22 @@ }, { "name": "vimeo/psalm", - "version": "5.2.0", + "version": "5.9.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "fb685a16df3050d4c18d8a4100fe83abe6458cba" + "reference": "8b9ad1eb9e8b7d3101f949291da2b9f7767cd163" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/fb685a16df3050d4c18d8a4100fe83abe6458cba", - "reference": "fb685a16df3050d4c18d8a4100fe83abe6458cba", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/8b9ad1eb9e8b7d3101f949291da2b9f7767cd163", + "reference": "8b9ad1eb9e8b7d3101f949291da2b9f7767cd163", "shasum": "" }, "require": { "amphp/amp": "^2.4.2", "amphp/byte-stream": "^1.5", - "composer/package-versions-deprecated": "^1.10.0", + "composer-runtime-api": "^2", "composer/semver": "^1.4 || ^2.0 || ^3.0", "composer/xdebug-handler": "^2.0 || ^3.0", "dnoegel/php-xdg-base-dir": "^0.1.1", @@ -5482,28 +5453,28 @@ "ext-tokenizer": "*", "felixfbecker/advanced-json-rpc": "^3.1", "felixfbecker/language-server-protocol": "^1.5.2", - "fidry/cpu-core-counter": "^0.4.0", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1", "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.13", - "openlss/lib-array2xml": "^1.0", + "nikic/php-parser": "^4.14", "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0", - "sebastian/diff": "^4.0", + "sebastian/diff": "^4.0 || ^5.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", "symfony/console": "^4.1.6 || ^5.0 || ^6.0", - "symfony/filesystem": "^5.4 || ^6.0", - "symfony/polyfill-php80": "^1.25" + "symfony/filesystem": "^5.4 || ^6.0" }, "provide": { "psalm/psalm": "self.version" }, "require-dev": { + "amphp/phpunit-util": "^2.0", "bamarni/composer-bin-plugin": "^1.4", - "brianium/paratest": "^6.0", + "brianium/paratest": "^6.9", "ext-curl": "*", "mockery/mockery": "^1.5", "nunomaduro/mock-final-classes": "^1.1", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/phpdoc-parser": "^1.6", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^9.6", "psalm/plugin-mockery": "^1.1", "psalm/plugin-phpunit": "^0.18", "slevomat/coding-standard": "^8.4", @@ -5549,34 +5520,35 @@ "keywords": [ "code", "inspection", - "php" + "php", + "static analysis" ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/5.2.0" + "source": "https://github.com/vimeo/psalm/tree/5.9.0" }, - "time": "2022-12-12T08:18:56+00:00" + "time": "2023-03-29T21:38:21+00:00" }, { "name": "webimpress/coding-standard", - "version": "1.2.4", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/webimpress/coding-standard.git", - "reference": "cd0c4b0b97440c337c1f7da17b524674ca2f9ca9" + "reference": "b26557e2386711ecb74f22718f4b4bde5ddbc899" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webimpress/coding-standard/zipball/cd0c4b0b97440c337c1f7da17b524674ca2f9ca9", - "reference": "cd0c4b0b97440c337c1f7da17b524674ca2f9ca9", + "url": "https://api.github.com/repos/webimpress/coding-standard/zipball/b26557e2386711ecb74f22718f4b4bde5ddbc899", + "reference": "b26557e2386711ecb74f22718f4b4bde5ddbc899", "shasum": "" }, "require": { "php": "^7.3 || ^8.0", - "squizlabs/php_codesniffer": "^3.6.2" + "squizlabs/php_codesniffer": "^3.7.2" }, "require-dev": { - "phpunit/phpunit": "^9.5.13" + "phpunit/phpunit": "^9.6.4" }, "type": "phpcodesniffer-standard", "extra": { @@ -5602,66 +5574,7 @@ ], "support": { "issues": "https://github.com/webimpress/coding-standard/issues", - "source": "https://github.com/webimpress/coding-standard/tree/1.2.4" - }, - "funding": [ - { - "url": "https://github.com/michalbundyra", - "type": "github" - } - ], - "time": "2022-02-15T19:52:12+00:00" - }, - { - "name": "webimpress/safe-writer", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/webimpress/safe-writer.git", - "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webimpress/safe-writer/zipball/9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", - "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", - "shasum": "" - }, - "require": { - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.4", - "vimeo/psalm": "^4.7", - "webimpress/coding-standard": "^1.2.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.2.x-dev", - "dev-develop": "2.3.x-dev", - "dev-release-1.0": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Webimpress\\SafeWriter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "description": "Tool to write files safely, to avoid race conditions", - "keywords": [ - "concurrent write", - "file writer", - "race condition", - "safe writer", - "webimpress" - ], - "support": { - "issues": "https://github.com/webimpress/safe-writer/issues", - "source": "https://github.com/webimpress/safe-writer/tree/2.2.0" + "source": "https://github.com/webimpress/coding-standard/tree/1.3.1" }, "funding": [ { @@ -5669,7 +5582,7 @@ "type": "github" } ], - "time": "2021-04-19T16:34:45+00:00" + "time": "2023-03-09T15:05:18+00:00" }, { "name": "webmozart/assert", @@ -5787,11 +5700,11 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "~8.0.0 || ~8.1.0 || ~8.2.0" + "php": "~8.1.0 || ~8.2.0" }, "platform-dev": [], "platform-overrides": { - "php": "8.0.99" + "php": "8.1.99" }, "plugin-api-version": "2.3.0" } diff --git a/psalm.xml.dist b/psalm.xml.dist index 78f1b869..343017a4 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -6,6 +6,8 @@ xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" errorBaseline="psalm-baseline.xml" findUnusedPsalmSuppress="true" + findUnusedCode="false" + findUnusedBaselineEntry="true" > @@ -46,6 +48,19 @@ + + + + + + + + + + + + + diff --git a/src/AbstractFactory/ConfigAbstractFactory.php b/src/AbstractFactory/ConfigAbstractFactory.php index 3b58ad8f..4629a6d2 100644 --- a/src/AbstractFactory/ConfigAbstractFactory.php +++ b/src/AbstractFactory/ConfigAbstractFactory.php @@ -22,7 +22,7 @@ final class ConfigAbstractFactory implements AbstractFactoryInterface * * {@inheritdoc} */ - public function canCreate(ContainerInterface $container, $requestedName) + public function canCreate(ContainerInterface $container, string $requestedName): bool { if (! $container->has('config')) { return false; @@ -37,7 +37,7 @@ public function canCreate(ContainerInterface $container, $requestedName) } /** {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null) + public function __invoke(ContainerInterface $container, string $requestedName, ?array $options = null): mixed { if (! $container->has('config')) { throw new ServiceNotCreatedException('Cannot find a config array in the container'); diff --git a/src/AbstractFactory/ReflectionBasedAbstractFactory.php b/src/AbstractFactory/ReflectionBasedAbstractFactory.php index dbda3046..2f90314c 100644 --- a/src/AbstractFactory/ReflectionBasedAbstractFactory.php +++ b/src/AbstractFactory/ReflectionBasedAbstractFactory.php @@ -85,7 +85,7 @@ public function __construct( /** * {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null): object + public function __invoke(ContainerInterface $container, string $requestedName, ?array $options = null): object { if (! class_exists($requestedName)) { throw new InvalidArgumentException(sprintf('%s can only be used with class names.', self::class)); @@ -101,7 +101,7 @@ public function __invoke(ContainerInterface $container, $requestedName, ?array $ } /** {@inheritDoc} */ - public function canCreate(ContainerInterface $container, $requestedName): bool + public function canCreate(ContainerInterface $container, string $requestedName): bool { return class_exists($requestedName) && $this->canCallConstructor($requestedName); } diff --git a/src/AbstractPluginManager.php b/src/AbstractPluginManager.php index 1e113e12..8060fbc2 100644 --- a/src/AbstractPluginManager.php +++ b/src/AbstractPluginManager.php @@ -6,16 +6,15 @@ use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException; use Laminas\ServiceManager\Exception\InvalidServiceException; +use Laminas\ServiceManager\Factory\AbstractFactoryInterface; +use Laminas\ServiceManager\Factory\DelegatorFactoryInterface; +use Laminas\ServiceManager\Initializer\InitializerInterface; use Psr\Container\ContainerInterface; use function class_exists; use function gettype; use function is_object; -use function method_exists; use function sprintf; -use function trigger_error; - -use const E_USER_DEPRECATED; /** * Abstract plugin manager. @@ -28,93 +27,50 @@ * to indicate what class types constitute valid plugins, omitting the * requirement to define the `validate()` method. * - * The implementation extends `ServiceManager`, thus providing the same set - * of capabilities as found in that implementation. - * * @template InstanceType - * @implements PluginManagerInterface - * @psalm-import-type ServiceManagerConfiguration from ServiceManager - * @psalm-suppress PropertyNotSetInConstructor + * @template-implements PluginManagerInterface + * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface + * @psalm-import-type FactoryCallableType from ConfigInterface + * @psalm-import-type DelegatorCallableType from ConfigInterface + * @psalm-import-type InitializerCallableType from ConfigInterface */ -abstract class AbstractPluginManager extends ServiceManager implements PluginManagerInterface +abstract class AbstractPluginManager implements PluginManagerInterface { /** * Whether or not to auto-add a FQCN as an invokable if it exists. - * - * @var bool */ - protected $autoAddInvokableClass = true; + protected bool $autoAddInvokableClass = true; /** * An object type that the created instance must be instanced of * - * @var null|string - * @psalm-var null|class-string - */ - protected $instanceOf; - - /** - * Sets the provided $parentLocator as the creation context for all - * factories; for $config, {@see \Laminas\ServiceManager\ServiceManager::configure()} - * for details on its accepted structure. - * - * @param null|ConfigInterface|ContainerInterface $configInstanceOrParentLocator - * @param array $config - * @psalm-param ServiceManagerConfiguration $config + * @var null|class-string */ - public function __construct($configInstanceOrParentLocator = null, array $config = []) - { - /** @psalm-suppress DocblockTypeContradiction */ - if ( - null !== $configInstanceOrParentLocator - && ! $configInstanceOrParentLocator instanceof ConfigInterface - && ! $configInstanceOrParentLocator instanceof ContainerInterface - ) { - throw new Exception\InvalidArgumentException(sprintf( - '%s expects a ConfigInterface or ContainerInterface instance as the first argument; received %s', - self::class, - is_object($configInstanceOrParentLocator) - ? $configInstanceOrParentLocator::class - : gettype($configInstanceOrParentLocator) - )); - } + protected ?string $instanceOf = null; - if ($configInstanceOrParentLocator instanceof ConfigInterface) { - trigger_error(sprintf( - 'Usage of %s as a constructor argument for %s is now deprecated', - ConfigInterface::class, - static::class - ), E_USER_DEPRECATED); - $config = $configInstanceOrParentLocator->toArray(); - } + protected bool $sharedByDefault = true; - parent::__construct($config); + private ServiceManager $plugins; - if (! $configInstanceOrParentLocator instanceof ContainerInterface) { - trigger_error(sprintf( - '%s now expects a %s instance representing the parent container; please update your code', - __METHOD__, - ContainerInterface::class - ), E_USER_DEPRECATED); - } + /** + * @param ServiceManagerConfigurationType $config + */ + public function __construct( + ContainerInterface $creationContext, + array $config = [], + ) { + $this->plugins = new ServiceManager([ + 'shared_by_default' => $this->sharedByDefault, + ], $creationContext); - $this->creationContext = $configInstanceOrParentLocator instanceof ContainerInterface - ? $configInstanceOrParentLocator - : $this; + // TODO: support old, internal, servicemanager properties in constructor to register services from properties + $this->configure($config); } /** - * Override configure() to validate service instances. - * - * @param array $config - * @psalm-param ServiceManagerConfiguration $config - * @return self - * @throws InvalidServiceException If an instance passed in the `services` configuration is invalid for the - * plugin manager. - * @throws ContainerModificationsNotAllowedException If the allow override flag has been toggled off, and a - * service instanceexists for a given service. + * {@inheritDoc} */ - public function configure(array $config) + public function configure(array $config): static { if (isset($config['services'])) { /** @psalm-suppress MixedAssignment */ @@ -123,72 +79,54 @@ public function configure(array $config) } } - parent::configure($config); + /** @psalm-var ServiceManagerConfigurationType $config */ + $this->plugins->configure($config); return $this; } /** - * Override setService for additional plugin validation. - * - * {@inheritDoc} + * @deprecated Please use {@see PluginManagerInterface::configure()} instead. * * @param string|class-string $name * @param InstanceType $service */ - public function setService($name, $service) + public function setService(string $name, mixed $service): void { $this->validate($service); - parent::setService($name, $service); + $this->plugins->setService($name, $service); } /** - * @param class-string|string $name Service name of plugin to retrieve. - * @param null|array $options Options to use when creating the instance. - * @return mixed - * @psalm-return ($name is class-string ? InstanceType : mixed) - * @throws Exception\ServiceNotFoundException If the manager does not have - * a service definition for the instance, and the service is not - * auto-invokable. - * @throws InvalidServiceException If the plugin created is invalid for the - * plugin context. + * {@inheritDoc} */ - public function get($name, ?array $options = null) + public function get($id): mixed { - if (! $this->has($name)) { - if (! $this->autoAddInvokableClass || ! class_exists($name)) { + if (! $this->has($id)) { + if (! $this->autoAddInvokableClass || ! class_exists($id)) { throw new Exception\ServiceNotFoundException(sprintf( 'A plugin by the name "%s" was not found in the plugin manager %s', - $name, + $id, static::class )); } - $this->setFactory($name, Factory\InvokableFactory::class); + $this->plugins->setFactory($id, Factory\InvokableFactory::class); } - $instance = ! $options ? parent::get($name) : $this->build($name, $options); + /** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */ + $instance = $this->plugins->get($id); $this->validate($instance); + /** @psalm-suppress MixedReturnStatement Yes indeed, plugin managers can return mixed. */ return $instance; } /** * {@inheritDoc} - * - * @psalm-assert InstanceType $instance */ - public function validate(mixed $instance) + public function validate(mixed $instance): void { - if (method_exists($this, 'validatePlugin')) { - trigger_error(sprintf( - '%s::validatePlugin() has been deprecated as of 3.0; please define validate() instead', - static::class - ), E_USER_DEPRECATED); - $this->validatePlugin($instance); - return; - } - - if (empty($this->instanceOf) || $instance instanceof $this->instanceOf) { + if ($this->instanceOf === null || $instance instanceof $this->instanceOf) { return; } @@ -201,21 +139,143 @@ public function validate(mixed $instance) } /** - * Implemented for backwards compatibility only. + * {@inheritDoc} + */ + public function has(string $id): bool + { + return $this->plugins->has($id); + } + + /** + * {@inheritDoc} + */ + public function build(string $name, ?array $options = null): mixed + { + /** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */ + $plugin = $this->plugins->build($name, $options); + $this->validate($plugin); + + /** @psalm-suppress MixedReturnStatement Yes indeed, service managers can return mixed. */ + return $plugin; + } + + /** + * Add an alias. * - * Returns the creation context. + * @deprecated Please use {@see PluginManagerInterface::configure()} instead. * - * @deprecated since 3.0.0. The creation context should be passed during - * instantiation instead. + * @throws ContainerModificationsNotAllowedException If $alias already + * exists as a service and overrides are disallowed. + */ + public function setAlias(string $alias, string $target): void + { + $this->plugins->setAlias($alias, $target); + } + + /** + * Add an invokable class mapping. * - * @return void + * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * + * @param null|string $class Class to which to map; if omitted, $name is + * assumed. + * @throws ContainerModificationsNotAllowedException If $name already + * exists as a service and overrides are disallowed. */ - public function setServiceLocator(ContainerInterface $container) + public function setInvokableClass(string $name, string|null $class = null): void + { + $this->plugins->setInvokableClass($name, $class); + } + + /** + * Specify a factory for a given service name. + * + * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * + * @param class-string|FactoryCallableType|Factory\FactoryInterface $factory + * @throws ContainerModificationsNotAllowedException If $name already + * exists as a service and overrides are disallowed. + */ + public function setFactory(string $name, string|callable|Factory\FactoryInterface $factory): void + { + $this->plugins->setFactory($name, $factory); + } + + /** + * Create a lazy service mapping to a class. + * + * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * + * @param null|string $class Class to which to map; if not provided, $name + * will be used for the mapping. + */ + public function mapLazyService(string $name, string|null $class = null): void + { + $this->plugins->mapLazyService($name, $class); + } + + /** + * Add an abstract factory for resolving services. + * + * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * + * @param string|AbstractFactoryInterface $factory Abstract factory + * instance or class name. + * @psalm-param class-string|AbstractFactoryInterface $factory + */ + public function addAbstractFactory(string|AbstractFactoryInterface $factory): void + { + $this->plugins->addAbstractFactory($factory); + } + + /** + * Add a delegator for a given service. + * + * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * + * @param string $name Service name + * @param string|callable|DelegatorFactoryInterface $factory Delegator + * factory to assign. + * @psalm-param class-string|DelegatorCallableType $factory + */ + public function addDelegator(string $name, string|callable|DelegatorFactoryInterface $factory): void + { + $this->plugins->addDelegator($name, $factory); + } + + /** + * Add an initializer. + * + * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * + * @psalm-param class-string|InitializerCallableType|InitializerInterface $initializer + */ + public function addInitializer(string|callable|InitializerInterface $initializer): void + { + $this->plugins->addInitializer($initializer); + } + + /** + * Add a service sharing rule. + * + * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * + * @param bool $flag Whether or not the service should be shared. + * @throws ContainerModificationsNotAllowedException If $name already + * exists as a service and overrides are disallowed. + */ + public function setShared(string $name, bool $flag): void + { + $this->plugins->setShared($name, $flag); + } + + public function getAllowOverride(): bool + { + return $this->plugins->getAllowOverride(); + } + + public function setAllowOverride(bool $flag): void { - trigger_error(sprintf( - 'Usage of %s is deprecated since v3.0.0; please pass the container to the constructor instead', - __METHOD__ - ), E_USER_DEPRECATED); - $this->creationContext = $container; + $this->plugins->setAllowOverride($flag); } } diff --git a/src/Config.php b/src/Config.php index 8024bdc0..138fdf1c 100644 --- a/src/Config.php +++ b/src/Config.php @@ -4,10 +4,6 @@ namespace Laminas\ServiceManager; -use Laminas\Stdlib\ArrayUtils; - -use function array_keys; - /** * Object for defining configuration and configuring an existing service manager instance. * @@ -25,10 +21,9 @@ * * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface */ -class Config implements ConfigInterface +final class Config implements ConfigInterface { - /** @var array */ - private array $allowedKeys = [ + private const ALLOWED_CONFIGURATION_KEYS = [ 'abstract_factories' => true, 'aliases' => true, 'delegators' => true, @@ -40,63 +35,30 @@ class Config implements ConfigInterface 'shared' => true, ]; - /** - * @var array - * @psalm-var ServiceManagerConfigurationType - */ - protected $config = [ - 'abstract_factories' => [], - 'aliases' => [], - 'delegators' => [], - 'factories' => [], - 'initializers' => [], - 'invokables' => [], - 'lazy_services' => [], - 'services' => [], - 'shared' => [], - ]; + /** @var ServiceManagerConfigurationType */ + private array $config; /** - * @psalm-param ServiceManagerConfigurationType $config + * @param ServiceManagerConfigurationType $config */ public function __construct(array $config = []) { - // Only merge keys we're interested in - foreach (array_keys($config) as $key) { - if (! isset($this->allowedKeys[$key])) { - unset($config[$key]); - } - } - - /** @psalm-suppress ArgumentTypeCoercion */ - $this->config = $this->merge($this->config, $config); + $this->config = $config; } /** * @inheritDoc */ - public function configureServiceManager(ServiceManager $serviceManager) + public function configureServiceManager(ServiceLocatorInterface $serviceLocator) { - return $serviceManager->configure($this->config); + return $serviceLocator->configure($this->config); } /** * @inheritDoc */ - public function toArray() + public function toArray(): array { return $this->config; } - - /** - * @psalm-param ServiceManagerConfigurationType $a - * @psalm-param ServiceManagerConfigurationType $b - * @psalm-return ServiceManagerConfigurationType - * @psalm-suppress MixedReturnTypeCoercion - */ - private function merge(array $a, array $b) - { - /** @psalm-suppress MixedReturnTypeCoercion */ - return ArrayUtils::merge($a, $b); - } } diff --git a/src/ConfigInterface.php b/src/ConfigInterface.php index 7d199f10..9a80712e 100644 --- a/src/ConfigInterface.php +++ b/src/ConfigInterface.php @@ -4,34 +4,39 @@ namespace Laminas\ServiceManager; -use ArrayAccess; use Psr\Container\ContainerInterface; /** * @see ContainerInterface - * @see ArrayAccess * * @psalm-type AbstractFactoriesConfigurationType = array< * array-key, - * (class-string|Factory\AbstractFactoryInterface) + * class-string + * |Factory\AbstractFactoryInterface * > + * @psalm-type DelegatorCallableType = callable(ContainerInterface,string,callable():mixed,array|null):mixed * @psalm-type DelegatorsConfigurationType = array< * string, * array< * array-key, - * (class-string|Factory\DelegatorFactoryInterface) - * |callable(ContainerInterface,string,callable():object,array|null):object + * class-string + * |Factory\DelegatorFactoryInterface + * |DelegatorCallableType * > * > + * @psalm-type FactoryCallableType = callable(ContainerInterface,string,array|null):mixed * @psalm-type FactoriesConfigurationType = array< * string, - * (class-string|Factory\FactoryInterface) - * |callable(ContainerInterface,?string,?array|null):object + * class-string + * |Factory\FactoryInterface + * |FactoryCallableType * > + * @psalm-type InitializerCallableType = callable(ContainerInterface,mixed):void * @psalm-type InitializersConfigurationType = array< * array-key, - * (class-string|Initializer\InitializerInterface) - * |callable(ContainerInterface,object):void + * class-string + * |Initializer\InitializerInterface + * |InitializerCallableType * > * @psalm-type LazyServicesConfigurationType = array{ * class_map?:array, @@ -45,10 +50,11 @@ * delegators?: DelegatorsConfigurationType, * factories?: FactoriesConfigurationType, * initializers?: InitializersConfigurationType, - * invokables?: array, + * invokables?: array, * lazy_services?: LazyServicesConfigurationType, - * services?: array, + * services?: array, * shared?:array, + * shared_by_default?: bool, * ... * } */ @@ -61,9 +67,11 @@ interface ConfigInterface * local properties) and pass it to a ServiceManager's withConfig() method, * returning a new instance. * - * @return ServiceManager + * @template T of ServiceLocatorInterface + * @param T $serviceLocator + * @return T */ - public function configureServiceManager(ServiceManager $serviceManager); + public function configureServiceManager(ServiceLocatorInterface $serviceLocator); /** * Return configuration for a service manager instance as an array. @@ -84,8 +92,7 @@ public function configureServiceManager(ServiceManager $serviceManager); * In other words, this should return configuration that can be used to instantiate * a service manager or plugin manager, or pass to its `withConfig()` method. * - * @return array - * @psalm-return ServiceManagerConfigurationType + * @return ServiceManagerConfigurationType */ - public function toArray(); + public function toArray(): array; } diff --git a/src/Factory/AbstractFactoryInterface.php b/src/Factory/AbstractFactoryInterface.php index f73af4f7..0b4d8205 100644 --- a/src/Factory/AbstractFactoryInterface.php +++ b/src/Factory/AbstractFactoryInterface.php @@ -20,9 +20,6 @@ interface AbstractFactoryInterface extends FactoryInterface { /** * Can the factory create an instance for the service? - * - * @param string $requestedName - * @return bool */ - public function canCreate(ContainerInterface $container, $requestedName); + public function canCreate(ContainerInterface $container, string $requestedName): bool; } diff --git a/src/Factory/DelegatorFactoryInterface.php b/src/Factory/DelegatorFactoryInterface.php index 755cf8a4..523d603d 100644 --- a/src/Factory/DelegatorFactoryInterface.php +++ b/src/Factory/DelegatorFactoryInterface.php @@ -22,13 +22,15 @@ interface DelegatorFactoryInterface /** * A factory that creates delegates of a given service * - * @param string $name - * @psalm-param callable():mixed $callback - * @param null|array $options - * @return object + * @param callable():mixed $callback * @throws ServiceNotFoundException If unable to resolve the service. * @throws ServiceNotCreatedException If an exception is raised when creating a service. * @throws ContainerExceptionInterface If any other error occurs. */ - public function __invoke(ContainerInterface $container, $name, callable $callback, ?array $options = null); + public function __invoke( + ContainerInterface $container, + string $name, + callable $callback, + ?array $options = null + ): mixed; } diff --git a/src/Factory/FactoryInterface.php b/src/Factory/FactoryInterface.php index 68e61671..1043a211 100644 --- a/src/Factory/FactoryInterface.php +++ b/src/Factory/FactoryInterface.php @@ -12,22 +12,17 @@ /** * Interface for a factory * - * A factory is an callable object that is able to create an object. It is - * given the instance of the service locator, the requested name of the class + * A factory is an callable object that is able to create a service. It is + * given the instance of the service locator, the requested name of the service * you want to create, and any additional options that could be used to - * configure the instance state. + * configure the service state. */ interface FactoryInterface { /** - * Create an object - * - * @param string $requestedName - * @param null|array $options - * @return object * @throws ServiceNotFoundException If unable to resolve the service. * @throws ServiceNotCreatedException If an exception is raised when creating a service. * @throws ContainerExceptionInterface If any other error occurs. */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null); + public function __invoke(ContainerInterface $container, string $requestedName, ?array $options = null): mixed; } diff --git a/src/Factory/InvokableFactory.php b/src/Factory/InvokableFactory.php index 6f80c894..c1b96f08 100644 --- a/src/Factory/InvokableFactory.php +++ b/src/Factory/InvokableFactory.php @@ -20,7 +20,7 @@ final class InvokableFactory implements FactoryInterface { /** {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null) + public function __invoke(ContainerInterface $container, string $requestedName, ?array $options = null): mixed { return null === $options ? new $requestedName() : new $requestedName($options); } diff --git a/src/Initializer/InitializerInterface.php b/src/Initializer/InitializerInterface.php index 794f1dc5..e2dc9756 100644 --- a/src/Initializer/InitializerInterface.php +++ b/src/Initializer/InitializerInterface.php @@ -15,9 +15,9 @@ interface InitializerInterface { /** - * Initialize the given instance + * Initialize the given service * - * @param object $instance + * @param mixed $instance * @return void */ public function __invoke(ContainerInterface $container, $instance); diff --git a/src/PluginManagerInterface.php b/src/PluginManagerInterface.php index cb82d4e6..d7605321 100644 --- a/src/PluginManagerInterface.php +++ b/src/PluginManagerInterface.php @@ -4,6 +4,8 @@ namespace Laminas\ServiceManager; +use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException; +use Laminas\ServiceManager\Exception\CyclicAliasException; use Laminas\ServiceManager\Exception\InvalidServiceException; use Psr\Container\ContainerExceptionInterface; @@ -13,17 +15,53 @@ * A plugin manager is a specialized service locator used to create homogeneous objects * * @template InstanceType + * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface */ interface PluginManagerInterface extends ServiceLocatorInterface { /** * Validate an instance * - * @return void * @throws InvalidServiceException If created instance does not respect the * constraint on type imposed by the plugin manager. * @throws ContainerExceptionInterface If any other error occurs. * @psalm-assert InstanceType $instance */ - public function validate(mixed $instance); + public function validate(mixed $instance): void; + + public function has(string $id): bool; + + /** + * @param class-string|string $id Service name of plugin to retrieve. + * @psalm-return ($id is class-string ? InstanceType : mixed) + * @throws Exception\ServiceNotFoundException If the manager does not have + * a service definition for the instance, and the service is not + * auto-invokable. + * @throws InvalidServiceException If the plugin created is invalid for the + * plugin context. + */ + public function get(string $id): mixed; + + /** + * Build a service by its name, using optional options (such services are NEVER cached). + * + * @param string|class-string $name + * @psalm-return ($name is class-string ? InstanceType : mixed) + * @throws Exception\ServiceNotFoundException If no factory/abstract + * factory could be found to create the instance. + * @throws Exception\ServiceNotCreatedException If factory/delegator fails + * to create the instance. + * @throws ContainerExceptionInterface If any other error occurs. + */ + public function build(string $name, ?array $options = null): mixed; + + /** + * @param ServiceManagerConfigurationType $config + * @throws ContainerModificationsNotAllowedException If the allow override flag has been toggled off, and a + * service instanceexists for a given service. + * @throws InvalidServiceException If an instance passed in the `services` configuration is invalid for the + * plugin manager. + * @throws CyclicAliasException If the configuration contains aliases targeting themselves. + */ + public function configure(array $config): static; } diff --git a/src/Proxy/LazyServiceFactory.php b/src/Proxy/LazyServiceFactory.php index 65b04465..751c09dc 100644 --- a/src/Proxy/LazyServiceFactory.php +++ b/src/Proxy/LazyServiceFactory.php @@ -31,12 +31,13 @@ public function __construct(private LazyLoadingValueHolderFactory $proxyFactory, /** * {@inheritDoc} - * - * @param string $name - * @return VirtualProxyInterface */ - public function __invoke(ContainerInterface $container, $name, callable $callback, ?array $options = null) - { + public function __invoke( + ContainerInterface $container, + string $name, + callable $callback, + ?array $options = null + ): VirtualProxyInterface { if (isset($this->servicesMap[$name])) { $initializer = static function (&$wrappedInstance, LazyLoadingInterface $proxy) use ($callback): bool { $proxy->setProxyInitializer(null); diff --git a/src/ServiceLocatorInterface.php b/src/ServiceLocatorInterface.php index 3d4bd447..591ad66e 100644 --- a/src/ServiceLocatorInterface.php +++ b/src/ServiceLocatorInterface.php @@ -4,11 +4,17 @@ namespace Laminas\ServiceManager; +use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException; +use Laminas\ServiceManager\Exception\CyclicAliasException; +use Laminas\ServiceManager\Exception\ServiceNotCreatedException; +use Laminas\ServiceManager\Exception\ServiceNotFoundException; use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; /** * Interface for service locator + * + * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface */ interface ServiceLocatorInterface extends ContainerInterface { @@ -17,14 +23,20 @@ interface ServiceLocatorInterface extends ContainerInterface * * @template T of object * @param string|class-string $name - * @param null|array $options - * @return mixed * @psalm-return ($name is class-string ? T : mixed) - * @throws Exception\ServiceNotFoundException If no factory/abstract + * @throws ServiceNotFoundException If no factory/abstract * factory could be found to create the instance. - * @throws Exception\ServiceNotCreatedException If factory/delegator fails + * @throws ServiceNotCreatedException If factory/delegator fails * to create the instance. * @throws ContainerExceptionInterface If any other error occurs. */ - public function build($name, ?array $options = null); + public function build(string $name, ?array $options = null): mixed; + + /** + * @param ServiceManagerConfigurationType $config + * @throws ContainerModificationsNotAllowedException If the allow override flag has been toggled off, and a + * service instanceexists for a given service. + * @throws CyclicAliasException If the configuration contains aliases targeting themselves. + */ + public function configure(array $config): static; } diff --git a/src/ServiceManager.php b/src/ServiceManager.php index ee09cca8..47e8fbd6 100644 --- a/src/ServiceManager.php +++ b/src/ServiceManager.php @@ -10,6 +10,7 @@ use Laminas\ServiceManager\Exception\InvalidArgumentException; use Laminas\ServiceManager\Exception\ServiceNotCreatedException; use Laminas\ServiceManager\Exception\ServiceNotFoundException; +use Laminas\ServiceManager\Factory\DelegatorFactoryInterface; use Laminas\ServiceManager\Proxy\LazyServiceFactory; use Laminas\Stdlib\ArrayUtils; use ProxyManager\Configuration as ProxyConfiguration; @@ -23,6 +24,7 @@ use function array_intersect; use function array_key_exists; use function array_keys; +use function array_merge; use function class_exists; use function gettype; use function in_array; @@ -56,19 +58,12 @@ * @psalm-import-type FactoriesConfigurationType from ConfigInterface * @psalm-import-type InitializersConfigurationType from ConfigInterface * @psalm-import-type LazyServicesConfigurationType from ConfigInterface - * @psalm-type ServiceManagerConfiguration = array{ - * abstract_factories?: AbstractFactoriesConfigurationType, - * aliases?: array, - * delegators?: DelegatorsConfigurationType, - * factories?: FactoriesConfigurationType, - * initializers?: InitializersConfigurationType, - * invokables?: array, - * lazy_services?: LazyServicesConfigurationType, - * services?: array, - * shared?:array, - * shared_by_default?:bool, - * ... - * } + * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface + * @psalm-import-type InitializerCallableType from ConfigInterface + * @psalm-import-type DelegatorCallableType from ConfigInterface + * @psalm-import-type FactoryCallableType from ConfigInterface + * + * @final Will be marked as final with v5.0.0 */ class ServiceManager implements ServiceLocatorInterface { @@ -94,30 +89,20 @@ class ServiceManager implements ServiceLocatorInterface /** @var ContainerInterface */ protected $creationContext; - /** - * @var string[][]|Factory\DelegatorFactoryInterface[][] - * @psalm-var DelegatorsConfigurationType - */ + /** @var DelegatorsConfigurationType */ protected $delegators = []; /** * A list of factories (either as string name or callable) * - * @var string[]|callable[] - * @psalm-var FactoriesConfigurationType + * @var FactoriesConfigurationType */ protected $factories = []; - /** - * @var Initializer\InitializerInterface[]|callable[] - * @psalm-var InitializersConfigurationType - */ + /** @var InitializersConfigurationType */ protected $initializers = []; - /** - * @var array - * @psalm-var LazyServicesConfigurationType - */ + /** @var LazyServicesConfigurationType */ protected $lazyServices = []; private ?LazyServiceFactory $lazyServicesDelegator = null; @@ -125,7 +110,7 @@ class ServiceManager implements ServiceLocatorInterface /** * A list of already loaded services (this act as a local cache) * - * @var array + * @var array */ protected $services = []; @@ -159,6 +144,8 @@ class ServiceManager implements ServiceLocatorInterface /** * Cached abstract factories from string. + * + * @var array,Factory\AbstractFactoryInterface> */ private array $cachedAbstractFactories = []; @@ -166,11 +153,13 @@ class ServiceManager implements ServiceLocatorInterface * See {@see \Laminas\ServiceManager\ServiceManager::configure()} for details * on what $config accepts. * - * @psalm-param ServiceManagerConfiguration $config + * @param ServiceManagerConfigurationType $config */ - public function __construct(array $config = []) - { - $this->creationContext = $this; + public function __construct( + array $config = [], + ContainerInterface|null $creationContext = null + ) { + $this->creationContext = $creationContext ?? $this; $this->configure($config); } @@ -189,13 +178,14 @@ public function get($name) // We achieve better performance if we can let all alias // considerations out. if (! $this->aliases) { - $object = $this->doCreate($name); + /** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */ + $service = $this->doCreate($name); - // Cache the object for later, if it is supposed to be shared. + // Cache the service for later, if it is supposed to be shared. if ($sharedService) { - $this->services[$name] = $object; + $this->services[$name] = $service; } - return $object; + return $service; } // We now deal with requests which may be aliases. @@ -217,27 +207,29 @@ public function get($name) // At this point, we have to create the object. // We use the resolved name for that. - $object = $this->doCreate($resolvedName); + /** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */ + $service = $this->doCreate($resolvedName); // Cache the object for later, if it is supposed to be shared. if ($sharedService) { - $this->services[$resolvedName] = $object; + $this->services[$resolvedName] = $service; } // Also cache under the alias name; this allows sharing based on the // service name used. if ($sharedAlias) { - $this->services[$name] = $object; + $this->services[$name] = $service; } - return $object; + return $service; } /** {@inheritDoc} */ - public function build($name, ?array $options = null) + public function build(string $name, ?array $options = null): mixed { // We never cache when using "build". $name = $this->aliases[$name] ?? $name; + /** @psalm-suppress MixedReturnStatement Yes indeed, service managers can return mixed. */ return $this->doCreate($name, $options); } @@ -255,32 +247,24 @@ public function has($name) /** * Indicate whether or not the instance is immutable. - * - * @param bool $flag */ - public function setAllowOverride($flag) + public function setAllowOverride(bool $flag): void { - $this->allowOverride = (bool) $flag; + $this->allowOverride = $flag; } /** * Retrieve the flag indicating immutability status. - * - * @return bool */ - public function getAllowOverride() + public function getAllowOverride(): bool { return $this->allowOverride; } /** - * @psalm-param ServiceManagerConfiguration $config - * @return self - * @throws ContainerModificationsNotAllowedException If the allow - * override flag has been toggled off, and a service instance - * exists for a given service. + * {@inheritDoc} */ - public function configure(array $config) + public function configure(array $config): static { // This is a bulk update/initial configuration, // so we check all definitions up front. @@ -350,12 +334,10 @@ public function configure(array $config) /** * Add an alias. * - * @param string $alias - * @param string $target * @throws ContainerModificationsNotAllowedException If $alias already * exists as a service and overrides are disallowed. */ - public function setAlias($alias, $target) + public function setAlias(string $alias, string $target): void { if (isset($this->services[$alias]) && ! $this->allowOverride) { throw ContainerModificationsNotAllowedException::fromExistingService($alias); @@ -373,7 +355,7 @@ public function setAlias($alias, $target) * @throws ContainerModificationsNotAllowedException If $name already * exists as a service and overrides are disallowed. */ - public function setInvokableClass($name, $class = null) + public function setInvokableClass(string $name, ?string $class = null) { if (isset($this->services[$name]) && ! $this->allowOverride) { throw ContainerModificationsNotAllowedException::fromExistingService($name); @@ -387,14 +369,11 @@ public function setInvokableClass($name, $class = null) * * @param string $name Service name * @param string|callable|Factory\FactoryInterface $factory Factory to which to map. - * phpcs:disable Generic.Files.LineLength.TooLong - * @psalm-param class-string|callable(ContainerInterface,string,array|null):object|Factory\FactoryInterface $factory - * phpcs:enable Generic.Files.LineLength.TooLong - * @return void + * @psalm-param class-string|FactoryCallableType|Factory\FactoryInterface $factory * @throws ContainerModificationsNotAllowedException If $name already * exists as a service and overrides are disallowed. */ - public function setFactory($name, $factory) + public function setFactory(string $name, string|callable|Factory\FactoryInterface $factory): void { if (isset($this->services[$name]) && ! $this->allowOverride) { throw ContainerModificationsNotAllowedException::fromExistingService($name); @@ -410,7 +389,7 @@ public function setFactory($name, $factory) * @param null|string $class Class to which to map; if not provided, $name * will be used for the mapping. */ - public function mapLazyService($name, $class = null) + public function mapLazyService(string $name, ?string $class = null) { $this->configure(['lazy_services' => ['class_map' => [$name => $class ?: $name]]]); } @@ -431,7 +410,7 @@ public function addAbstractFactory($factory) * Add a delegator for a given service. * * @param string $name Service name - * @param string|callable|Factory\DelegatorFactoryInterface $factory Delegator + * @param string|callable|DelegatorFactoryInterface $factory Delegator * factory to assign. * @psalm-param class-string * |callable(ContainerInterface,string,callable,array|null) $factory @@ -458,11 +437,10 @@ public function addInitializer($initializer) * Map a service. * * @param string $name Service name - * @param array|object $service * @throws ContainerModificationsNotAllowedException If $name already * exists as a service and overrides are disallowed. */ - public function setService($name, $service) + public function setService(string $name, mixed $service): void { if (isset($this->services[$name]) && ! $this->allowOverride) { throw ContainerModificationsNotAllowedException::fromExistingService($name); @@ -478,41 +456,46 @@ public function setService($name, $service) * @throws ContainerModificationsNotAllowedException If $name already * exists as a service and overrides are disallowed. */ - public function setShared($name, $flag) + public function setShared(string $name, bool $flag): void { if (isset($this->services[$name]) && ! $this->allowOverride) { throw ContainerModificationsNotAllowedException::fromExistingService($name); } - $this->shared[$name] = (bool) $flag; + $this->shared[$name] = $flag; } /** * Instantiate initializers for to avoid checks during service construction. * - * @psalm-param InitializersConfigurationType $initializers + * @param InitializersConfigurationType $initializers */ private function resolveInitializers(array $initializers): void { + $resolved = []; foreach ($initializers as $initializer) { if (is_string($initializer) && class_exists($initializer)) { $initializer = new $initializer(); } if (is_callable($initializer)) { - $this->initializers[] = $initializer; + /** @psalm-var InitializerCallableType $initializer */ + $resolved[] = $initializer; continue; } throw InvalidArgumentException::fromInvalidInitializer($initializer); } + + $this->initializers = array_merge($this->initializers, $resolved); } /** * Get a factory for the given service name * - * @psalm-return (callable(ContainerInterface,string,array|null):object)|Factory\FactoryInterface - * @throws ServiceNotFoundException + * @return FactoryCallableType|Factory\FactoryInterface + * @throws ServiceNotFoundException In case that the service creation strategy based on factories + * did not find any capable factory. */ private function getFactory(string $name): callable { @@ -525,6 +508,7 @@ private function getFactory(string $name): callable } if (is_callable($factory)) { + /** @psalm-var FactoryCallableType $factory */ if ($lazyLoaded) { $this->factories[$name] = $factory; } @@ -545,10 +529,7 @@ private function getFactory(string $name): callable )); } - /** - * @return object - */ - private function createDelegatorFromName(string $name, ?array $options = null) + private function createDelegatorFromName(string $name, ?array $options = null): mixed { $creationCallback = function () use ($name, $options) { // Code is inlined for performance reason, instead of abstracting the creation @@ -558,24 +539,16 @@ private function createDelegatorFromName(string $name, ?array $options = null) $initialCreationContext = $this->creationContext; + $resolvedDelegators = []; foreach ($this->delegators[$name] as $index => $delegatorFactory) { - $delegatorFactory = $this->delegators[$name][$index]; - - if ($delegatorFactory === LazyServiceFactory::class) { - $delegatorFactory = $this->createLazyServiceDelegatorFactory(); - } elseif (is_string($delegatorFactory) && class_exists($delegatorFactory)) { - $delegatorFactory = new $delegatorFactory(); - } - - $this->assertCallableDelegatorFactory($delegatorFactory); - + $delegatorFactory = $this->resolveDelegatorFactory($this->delegators[$name][$index]); $this->delegators[$name][$index] = $delegatorFactory; - - $creationCallback = - /** @return object */ - static fn() => $delegatorFactory($initialCreationContext, $name, $creationCallback, $options); + $creationCallback = + static fn(): mixed => $delegatorFactory($initialCreationContext, $name, $creationCallback, $options); } + $this->delegators[$name] = $resolvedDelegators; + return $creationCallback(); } @@ -584,20 +557,21 @@ private function createDelegatorFromName(string $name, ?array $options = null) * * This is a highly performance sensitive method, do not modify if you have not benchmarked it carefully * - * @return object * @throws ServiceNotFoundException If unable to resolve the service. * @throws ServiceNotCreatedException If an exception is raised when creating a service. * @throws ContainerExceptionInterface If any other error occurs. */ - private function doCreate(string $resolvedName, ?array $options = null) + private function doCreate(string $resolvedName, ?array $options = null): mixed { try { if (! isset($this->delegators[$resolvedName])) { // Let's create the service by fetching the factory $factory = $this->getFactory($resolvedName); - $object = $factory($this->creationContext, $resolvedName, $options); + /** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */ + $service = $factory($this->creationContext, $resolvedName, $options); } else { - $object = $this->createDelegatorFromName($resolvedName, $options); + /** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */ + $service = $this->createDelegatorFromName($resolvedName, $options); } } catch (ContainerExceptionInterface $exception) { throw $exception; @@ -610,10 +584,10 @@ private function doCreate(string $resolvedName, ?array $options = null) } foreach ($this->initializers as $initializer) { - $initializer($this->creationContext, $object); + $initializer($this->creationContext, $service); } - return $object; + return $service; } /** @@ -667,8 +641,8 @@ private function createLazyServiceDelegatorFactory(): LazyServiceFactory * It works with strings and class instances. * It's not possible to de-duple anonymous functions * - * @psalm-param DelegatorsConfigurationType $config - * @psalm-return DelegatorsConfigurationType + * @param DelegatorsConfigurationType $config + * @return DelegatorsConfigurationType */ private function mergeDelegators(array $config): array { @@ -723,7 +697,7 @@ private function createAliasesAndFactoriesForInvokables(array $invokables): arra * a given service name we do not have a service instance * in the cache OR override is explicitly allowed. * - * @psalm-param ServiceManagerConfigurationType $config + * @param ServiceManagerConfigurationType $config * @throws ContainerModificationsNotAllowedException If any * service key is invalid. */ @@ -878,12 +852,11 @@ private function mapAliasesToTargets(): void /** * Instantiate abstract factories in order to avoid checks during service construction. * - * @param string|Factory\AbstractFactoryInterface $abstractFactory - * @psalm-param class-string|Factory\AbstractFactoryInterface $abstractFactory + * @param class-string|Factory\AbstractFactoryInterface $abstractFactory */ - private function resolveAbstractFactoryInstance($abstractFactory): void + private function resolveAbstractFactoryInstance(string|Factory\AbstractFactoryInterface $abstractFactory): void { - if (is_string($abstractFactory) && class_exists($abstractFactory)) { + if (is_string($abstractFactory)) { // Cached string factory name if (! isset($this->cachedAbstractFactories[$abstractFactory])) { $this->cachedAbstractFactories[$abstractFactory] = new $abstractFactory(); @@ -892,10 +865,6 @@ private function resolveAbstractFactoryInstance($abstractFactory): void $abstractFactory = $this->cachedAbstractFactories[$abstractFactory]; } - if (! $abstractFactory instanceof Factory\AbstractFactoryInterface) { - throw InvalidArgumentException::fromInvalidAbstractFactory($abstractFactory); - } - $abstractFactoryObjHash = spl_object_hash($abstractFactory); $this->abstractFactories[$abstractFactoryObjHash] = $abstractFactory; } @@ -937,10 +906,9 @@ private function abstractFactoryCanCreate(string $name): bool } /** - * @psalm-param mixed $delegatorFactory - * @psalm-assert callable(ContainerInterface,string,callable():object,array|null):object $delegatorFactory + * @psalm-assert Factory\DelegatorFactoryInterface|DelegatorCallableType $delegatorFactory */ - private function assertCallableDelegatorFactory($delegatorFactory): void + private function assertCallableDelegatorFactory(mixed $delegatorFactory): void { if ( $delegatorFactory instanceof Factory\DelegatorFactoryInterface @@ -954,13 +922,35 @@ private function assertCallableDelegatorFactory($delegatorFactory): void . ' which does not exist; please provide a valid function name or class name resolving' . ' to an implementation of %s', $delegatorFactory, - Factory\DelegatorFactoryInterface::class + DelegatorFactoryInterface::class )); } throw new ServiceNotCreatedException(sprintf( 'A non-callable delegator, "%s", was provided; expected a callable or instance of "%s"', is_object($delegatorFactory) ? $delegatorFactory::class : gettype($delegatorFactory), - Factory\DelegatorFactoryInterface::class + DelegatorFactoryInterface::class )); } + + /** + * @return DelegatorFactoryInterface|DelegatorCallableType + */ + private function resolveDelegatorFactory(mixed $delegatorFactory): callable|DelegatorFactoryInterface + { + if ($delegatorFactory instanceof DelegatorFactoryInterface) { + return $delegatorFactory; + } + + if ($delegatorFactory === LazyServiceFactory::class) { + return $this->createLazyServiceDelegatorFactory(); + } + + if (is_string($delegatorFactory) && class_exists($delegatorFactory)) { + /** @psalm-suppress MixedMethodCall We do assert that a delegator can be instantiated */ + $delegatorFactory = new $delegatorFactory(); + } + + $this->assertCallableDelegatorFactory($delegatorFactory); + return $delegatorFactory; + } } diff --git a/src/Test/CommonPluginManagerTrait.php b/src/Test/CommonPluginManagerTrait.php index fc16d7f2..b93677f6 100644 --- a/src/Test/CommonPluginManagerTrait.php +++ b/src/Test/CommonPluginManagerTrait.php @@ -6,7 +6,6 @@ use Laminas\ServiceManager\AbstractPluginManager; use Laminas\ServiceManager\Exception\InvalidServiceException; -use ReflectionClass; use ReflectionProperty; use function method_exists; @@ -24,34 +23,9 @@ public function testInstanceOfMatches() { $manager = $this->getPluginManager(); $reflection = new ReflectionProperty($manager, 'instanceOf'); - $reflection->setAccessible(true); $this->assertEquals($this->getInstanceOf(), $reflection->getValue($manager), 'instanceOf does not match'); } - public function testShareByDefaultAndSharedByDefault() - { - $manager = $this->getPluginManager(); - $reflection = new ReflectionClass($manager); - $shareByDefault = $sharedByDefault = true; - - foreach ($reflection->getProperties() as $prop) { - if ($prop->getName() === 'shareByDefault') { - $prop->setAccessible(true); - $shareByDefault = $prop->getValue($manager); - } - if ($prop->getName() === 'sharedByDefault') { - $prop->setAccessible(true); - $sharedByDefault = $prop->getValue($manager); - } - } - - $this->assertSame( - $shareByDefault, - $sharedByDefault, - 'Values of shareByDefault and sharedByDefault do not match' - ); - } - public function testRegisteringInvalidElementRaisesException() { $this->expectException($this->getServiceNotFoundException()); @@ -83,8 +57,7 @@ public function aliasProvider() { $manager = $this->getPluginManager(); $reflection = new ReflectionProperty($manager, 'aliases'); - $reflection->setAccessible(true); - $data = []; + $data = []; foreach ($reflection->getValue($manager) as $alias => $expected) { $data[] = [$alias, $expected]; } diff --git a/src/Tool/FactoryCreator.php b/src/Tool/FactoryCreator.php index 4832f0f0..df30d8b9 100644 --- a/src/Tool/FactoryCreator.php +++ b/src/Tool/FactoryCreator.php @@ -45,13 +45,7 @@ final class FactoryCreator implements FactoryCreatorInterface class %sFactory implements FactoryInterface { - /** - * @param ContainerInterface $container - * @param string $requestedName - * @param null|array $options - * @return %s - */ - public function __invoke(ContainerInterface $container, $requestedName, array $options = null) + public function __invoke(ContainerInterface $container, string $requestedName, array $options = null): %s { return new %s(%s); } diff --git a/test/AbstractPluginManagerTest.php b/test/AbstractPluginManagerTest.php index 448ebde5..198cf31c 100644 --- a/test/AbstractPluginManagerTest.php +++ b/test/AbstractPluginManagerTest.php @@ -5,8 +5,7 @@ namespace LaminasTest\ServiceManager; -use Laminas\ServiceManager\ConfigInterface; -use Laminas\ServiceManager\Exception\InvalidArgumentException; +use Laminas\ServiceManager\AbstractPluginManager; use Laminas\ServiceManager\Exception\InvalidServiceException; use Laminas\ServiceManager\Exception\ServiceNotFoundException; use Laminas\ServiceManager\Factory\AbstractFactoryInterface; @@ -14,17 +13,12 @@ use Laminas\ServiceManager\Factory\InvokableFactory; use Laminas\ServiceManager\ServiceManager; use LaminasTest\ServiceManager\TestAsset\InvokableObject; +use LaminasTest\ServiceManager\TestAsset\InvokableObjectPluginManager; use LaminasTest\ServiceManager\TestAsset\SimplePluginManager; -use LaminasTest\ServiceManager\TestAsset\V2v3PluginManager; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use stdClass; -use function restore_error_handler; -use function set_error_handler; - -use const E_USER_DEPRECATED; - /** * @covers \Laminas\ServiceManager\AbstractPluginManager */ @@ -32,7 +26,7 @@ final class AbstractPluginManagerTest extends TestCase { use CommonServiceLocatorBehaviorsTrait; - public function createContainer(array $config = []): ServiceManager + public function createContainer(array $config = []): AbstractPluginManager { $this->creationContext = new ServiceManager(); return new TestAsset\LenientPluginManager($this->creationContext, $config); @@ -126,8 +120,8 @@ public function testReturnsDiscreteInstancesIfOptionsAreProvidedRegardlessOfShar $container = $this->createMock(ContainerInterface::class); $pluginManager = new SimplePluginManager($container, $config); - $first = $pluginManager->get(InvokableObject::class, $options); - $second = $pluginManager->get(InvokableObject::class, $options); + $first = $pluginManager->build(InvokableObject::class, $options); + $second = $pluginManager->build(InvokableObject::class, $options); self::assertInstanceOf(InvokableObject::class, $first); self::assertInstanceOf(InvokableObject::class, $second); @@ -156,7 +150,7 @@ public function testCanWrapCreationInDelegators(): void 'delegators' => [ stdClass::class => [ TestAsset\PreDelegator::class, - static function ($container, $name, $callback) { + static function (ContainerInterface $container, string $name, callable $callback): mixed { $instance = $callback(); $instance->foo = 'bar'; @@ -191,110 +185,6 @@ public function testGetRaisesExceptionWhenNoFactoryIsResolved(): void $pluginManager->get('Some\Unknown\Service'); } - /** - * @group migration - */ - public function testCallingSetServiceLocatorSetsCreationContextWithDeprecationNotice(): void - { - set_error_handler(static function ($errno, $errstr): void { - self::assertEquals(E_USER_DEPRECATED, $errno); - }, E_USER_DEPRECATED); - $pluginManager = new TestAsset\LenientPluginManager(); - restore_error_handler(); - - self::assertSame($pluginManager, $pluginManager->getCreationContext()); - $serviceManager = new ServiceManager(); - - set_error_handler(static function ($errno, $errstr): void { - self::assertEquals(E_USER_DEPRECATED, $errno); - }, E_USER_DEPRECATED); - $pluginManager->setServiceLocator($serviceManager); - restore_error_handler(); - - self::assertSame($serviceManager, $pluginManager->getCreationContext()); - } - - /** - * @group migration - */ - public function testPassingNoInitialConstructorArgumentSetsPluginManagerAsCreationContextWithDeprecationNotice(): void - { - set_error_handler(static function ($errno, $errstr): void { - self::assertEquals(E_USER_DEPRECATED, $errno); - }, E_USER_DEPRECATED); - $pluginManager = new TestAsset\LenientPluginManager(); - restore_error_handler(); - - self::assertSame($pluginManager, $pluginManager->getCreationContext()); - } - - /** - * @group migration - */ - public function testCanPassConfigInterfaceAsFirstConstructorArgumentWithDeprecationNotice(): void - { - $config = $this->createMock(ConfigInterface::class); - $config - ->expects(self::once()) - ->method('toArray') - ->willReturn([]); - - set_error_handler(static function ($errno, $errstr): void { - self::assertEquals(E_USER_DEPRECATED, $errno); - }, E_USER_DEPRECATED); - $pluginManager = new TestAsset\LenientPluginManager($config); - restore_error_handler(); - - self::assertSame($pluginManager, $pluginManager->getCreationContext()); - } - - public function invalidConstructorArguments(): array - { - return [ - 'true' => [true], - 'false' => [false], - 'zero' => [0], - 'int' => [1], - 'zero-float' => [0.0], - 'float' => [1.1], - 'string' => ['invalid'], - 'array' => [['invokables' => []]], - 'object' => [(object) ['invokables' => []]], - ]; - } - - /** - * @group migration - * @dataProvider invalidConstructorArguments - */ - public function testPassingNonContainerNonConfigNonNullFirstConstructorArgumentRaisesException(mixed $arg): void - { - $this->expectException(InvalidArgumentException::class); - new TestAsset\LenientPluginManager($arg); - } - - /** - * @group migration - */ - public function testPassingConfigInstanceAsFirstConstructorArgumentSkipsSecondArgumentWithDeprecationNotice(): void - { - $config = $this->createMock(ConfigInterface::class); - $config - ->expects(self::once()) - ->method('toArray') - ->willReturn(['services' => [self::class => $this]]); - - set_error_handler(static function (int $errno, string $_): bool { // phpcs:ignore - self::assertEquals(E_USER_DEPRECATED, $errno); - - return true; - }, E_USER_DEPRECATED); - $pluginManager = new TestAsset\LenientPluginManager($config, ['services' => [self::class => []]]); - restore_error_handler(); - - self::assertSame($this, $pluginManager->get(self::class)); - } - /** * @group migration * @group autoinvokable @@ -330,36 +220,6 @@ public function testPluginManagersMayOptOutOfSupportingAutoInvokableServices(): $pluginManager->get(InvokableObject::class); } - /** - * @group migration - */ - public function testValidateWillFallBackToValidatePluginWhenDefinedAndEmitDeprecationNotice(): void - { - $assertionCalled = false; - $instance = (object) []; - $assertion = static function ($plugin) use ($instance, &$assertionCalled): void { - self::assertSame($instance, $plugin); - $assertionCalled = true; - }; - $pluginManager = new TestAsset\V2ValidationPluginManager(new ServiceManager()); - $pluginManager->assertion = $assertion; - - $errorHandlerCalled = false; - set_error_handler(static function (int $errno, string $errmsg) use (&$errorHandlerCalled): bool { - self::assertEquals(E_USER_DEPRECATED, $errno); - self::assertStringContainsString('3.0', $errmsg); - $errorHandlerCalled = true; - - return true; - }, E_USER_DEPRECATED); - - $pluginManager->validate($instance); - restore_error_handler(); - - self::assertTrue($assertionCalled, 'Assertion was not called by validatePlugin!'); - self::assertTrue($errorHandlerCalled, 'Error handler was not triggered by validatePlugin!'); - } - public function testSetServiceShouldRaiseExceptionForInvalidPlugin(): void { $pluginManager = new SimplePluginManager(new ServiceManager()); @@ -407,7 +267,7 @@ public function testAbstractFactoryGetsCreationContext(): void public function testAliasPropertyResolves(): void { - $pluginManager = new V2v3PluginManager(new ServiceManager()); + $pluginManager = new InvokableObjectPluginManager(new ServiceManager()); self::assertInstanceOf(InvokableObject::class, $pluginManager->get('foo')); } diff --git a/test/CommonServiceLocatorBehaviorsTrait.php b/test/CommonServiceLocatorBehaviorsTrait.php index 731a1dd6..c8b613be 100644 --- a/test/CommonServiceLocatorBehaviorsTrait.php +++ b/test/CommonServiceLocatorBehaviorsTrait.php @@ -5,6 +5,7 @@ namespace LaminasTest\ServiceManager; use DateTime; +use Laminas\ServiceManager\AbstractPluginManager; use Laminas\ServiceManager\ConfigInterface; use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException; use Laminas\ServiceManager\Exception\CyclicAliasException; @@ -14,7 +15,6 @@ use Laminas\ServiceManager\Factory\FactoryInterface; use Laminas\ServiceManager\Factory\InvokableFactory; use Laminas\ServiceManager\Initializer\InitializerInterface; -use Laminas\ServiceManager\ServiceLocatorInterface; use Laminas\ServiceManager\ServiceManager; use LaminasBench\ServiceManager\BenchAsset\AbstractFactoryFoo; use LaminasTest\ServiceManager\TestAsset\CallTimesAbstractFactory; @@ -28,14 +28,13 @@ use PHPUnit\Framework\TestCase; use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; -use ReflectionProperty; use stdClass; use function array_fill_keys; use function array_keys; use function array_merge; -use function PHPUnit\Framework\assertIsBool; -use function PHPUnit\Framework\assertIsString; +use function assert; +use function in_array; /** * @see ConfigInterface @@ -49,14 +48,13 @@ trait CommonServiceLocatorBehaviorsTrait /** * The creation context container; used in some mocks for comparisons; set during createContainer. */ - protected ServiceManager|null $creationContext; + protected ContainerInterface|null $creationContext; /** * @psalm-param ServiceManagerConfigurationType $config - * @return ServiceManager * @todo This will need to be static for future versions of PHPUnit */ - abstract public function createContainer(array $config = []); + abstract public function createContainer(array $config = []): AbstractPluginManager|ServiceManager; public function testIsSharedByDefault(): void { @@ -140,7 +138,7 @@ public function testCanCreateObjectWithClosureFactory(): void { $serviceManager = $this->createContainer([ 'factories' => [ - stdClass::class => static function (ServiceLocatorInterface $serviceLocator, $className): stdClass { + stdClass::class => static function (ContainerInterface $container, $className): stdClass { self::assertEquals(stdClass::class, $className); return new stdClass(); @@ -236,7 +234,7 @@ public function testCheckingServiceExistenceWithChecksAgainstAbstractFactories() public function testBuildNeverSharesInstances(): void { - $serviceManager = $this->createContainer([ + $container = $this->createContainer([ 'factories' => [ stdClass::class => InvokableFactory::class, ], @@ -245,10 +243,15 @@ public function testBuildNeverSharesInstances(): void ], ]); - $object1 = $serviceManager->build(stdClass::class); - $object2 = $serviceManager->build(stdClass::class, ['foo' => 'bar']); + $object1 = $container->build(stdClass::class); + self::assertInstanceOf(stdClass::class, $object1); + $object2 = $container->build(stdClass::class, ['foo' => 'bar']); + self::assertInstanceOf(stdClass::class, $object2); + $object3 = $container->get(stdClass::class); + self::assertInstanceOf(stdClass::class, $object3); self::assertNotSame($object1, $object2); + self::assertNotSame($object1, $object3); } public function testInitializersAreRunAfterCreation(): void @@ -562,32 +565,6 @@ public function invalidFactories(): array ]; } - public function invalidAbstractFactories(): array - { - $factories = $this->invalidFactories(); - $factories['non-class-string'] = ['non-callable-string', 'valid class name']; - - return $factories; - } - - /** - * @dataProvider invalidAbstractFactories - * @covers \Laminas\ServiceManager\ServiceManager::configure - */ - public function testPassingInvalidAbstractFactoryTypeViaConfigurationRaisesException( - mixed $factory, - string $contains = 'invalid abstract factory' - ): void { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage($contains); - /** @psalm-suppress InvalidArgument */ - $this->createContainer([ - 'abstract_factories' => [ - $factory, - ], - ]); - } - public function testCanSpecifyInitializerUsingStringViaConfiguration(): void { $serviceManager = $this->createContainer([ @@ -639,40 +616,6 @@ public function testGetRaisesExceptionWhenNoFactoryIsResolved(): void $serviceManager->get('Some\Unknown\Service'); } - public function invalidDelegators(): array - { - $invalidDelegators = $this->invalidFactories(); - $invalidDelegators['invalid-classname'] = ['not-a-class-name', 'invalid delegator']; - $invalidDelegators['non-invokable-class'] = [stdClass::class]; - - return $invalidDelegators; - } - - /** - * @dataProvider invalidDelegators - * @covers \Laminas\ServiceManager\ServiceManager::createDelegatorFromName - */ - public function testInvalidDelegatorShouldRaiseExceptionDuringCreation( - mixed $delegator, - string $contains = 'non-callable delegator' - ): void { - /** @psalm-suppress InvalidArgument */ - $serviceManager = $this->createContainer([ - 'factories' => [ - stdClass::class => InvokableFactory::class, - ], - 'delegators' => [ - stdClass::class => [ - $delegator, - ], - ], - ]); - - $this->expectException(ServiceNotCreatedException::class); - $this->expectExceptionMessage($contains); - $serviceManager->get(stdClass::class); - } - /** * @group mutation * @covers \Laminas\ServiceManager\ServiceManager::setAlias @@ -730,25 +673,6 @@ public function testCanInjectFactories(): void self::assertSame($instance, $foo); } - /** - * @group mutation - * @covers \Laminas\ServiceManager\ServiceManager::mapLazyService - */ - public function testCanMapLazyServices(): void - { - $container = $this->createContainer(); - $container->mapLazyService('foo', self::class); - $r = new ReflectionProperty($container, 'lazyServices'); - $r->setAccessible(true); - $lazyServices = $r->getValue($container); - - self::assertIsArray($lazyServices); - self::assertArrayHasKey('class_map', $lazyServices); - self::assertIsArray($lazyServices['class_map']); - self::assertArrayHasKey('foo', $lazyServices['class_map']); - self::assertEquals(self::class, $lazyServices['class_map']['foo']); - } - /** * @group mutation * @covers \Laminas\ServiceManager\ServiceManager::addAbstractFactory @@ -758,8 +682,7 @@ public function testCanInjectAbstractFactories(): void $container = $this->createContainer(); $container->addAbstractFactory(SimpleAbstractFactory::class); - // @todo Remove "true" flag once #49 is merged - self::assertTrue($container->has(stdClass::class, true)); + self::assertTrue($container->has(stdClass::class)); $instance = $container->get(stdClass::class); @@ -925,7 +848,7 @@ public function testAllowOverrideFlagIsFalseByDefault(): ContainerInterface * @group mutation * @depends testAllowOverrideFlagIsFalseByDefault */ - public function testAllowOverrideFlagIsMutable(ServiceManager $container): void + public function testAllowOverrideFlagIsMutable(ServiceManager|AbstractPluginManager $container): void { $container->setAllowOverride(true); @@ -982,6 +905,7 @@ public function testCoverageDepthFirstTaggingOnRecursiveAliasDefinitions(): void * all internal states, thereby verifying that build/get/has * remain stable through the internal states. * + * @param list $test * @dataProvider provideConsistencyOverInternalStatesTests */ public function testConsistencyOverInternalStates( @@ -990,22 +914,33 @@ public function testConsistencyOverInternalStates( array $test, bool $shared ): void { - $sm = clone $smTemplate; - $object['get'] = []; - $object['build'] = []; + $sm = clone $smTemplate; + $object = [ + 'get' => [], + 'build' => [], + ]; // call get()/build() and store the retrieved // objects in $object['get'] or $object['build'] // respectively foreach ($test as $method) { - $obj = $sm->$method($name); - $object[$shared ? $method : 'build'][] = $obj; + assert(in_array($method, ['get', 'build'], true)); + $obj = $sm->$method($name); self::assertNotNull($obj); self::assertTrue($sm->has($name)); + + $target = $method; + if (! $shared) { + $target = 'build'; + } + + /** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */ + $object[$target][] = $obj; } // compares the first to the first also, but ok + /** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */ foreach ($object['get'] as $sharedObj) { self::assertSame($object['get'][0], $sharedObj); } @@ -1027,7 +962,14 @@ public function testConsistencyOverInternalStates( * * @see testConsistencyOverInternalStates above * - * @return list, 3: bool}> + * @return array< + * array{ + * 0: ContainerInterface, + * 1: string, + * 2: list, + * 3: bool + * } + * > */ public function provideConsistencyOverInternalStatesTests(): array { @@ -1080,39 +1022,38 @@ public function provideConsistencyOverInternalStatesTests(): array } } - /** @psalm-var list> $callSequences */ - $tests = []; foreach ($configs as $config) { - $smTemplate = $this->createContainer($config); + $smTemplate = $this->createContainer($config); + $sharedByDefault = $config['shared_by_default'] ?? true; // setup sharing, services are always shared + /** @var array $names */ $names = array_fill_keys(array_keys($config['services']), true); // initialize the other keys with shared_by_default // and merge them - $names = array_merge(array_fill_keys(array_keys(array_merge( - $config['factories'], - $config['invokables'], - $config['aliases'], - $config['delegators'], - )), $config['shared_by_default'] ?? true), $names); - - // add the key resolved by the abstract factory - $names['foo'] = $config['shared_by_default'] ?? true; - - // adjust shared setting for individual keys from - // $shared array if present - if (! empty($config['shared'])) { - foreach ($config['shared'] as $name => $shared) { - $names[$name] = $shared; + foreach ( + array_keys(array_merge( + $config['factories'] ?? [], + $config['invokables'] ?? [], + $config['aliases'] ?? [], + $config['delegators'] ?? [], + )) as $name + ) { + // do not change shared setting for service placed in `services` as services are always shared + if (isset($config['services'][$name])) { + continue; } + + $names[$name] = $sharedByDefault; } + // add the key resolved by the abstract factory + $names['foo'] = $sharedByDefault; + foreach ($names as $name => $shared) { - assertIsString($name); - assertIsBool($shared); foreach ($callSequences as $callSequence) { $tests[] = [$smTemplate, $name, $callSequence, $shared]; } diff --git a/test/ConfigTest.php b/test/ConfigTest.php index 103c119f..77e3c069 100644 --- a/test/ConfigTest.php +++ b/test/ConfigTest.php @@ -4,59 +4,29 @@ namespace LaminasTest\ServiceManager; +use Laminas\ContainerConfigTest\TestAsset\Delegator1Factory; use Laminas\ServiceManager\Config; -use Laminas\ServiceManager\ServiceManager; +use Laminas\ServiceManager\ConfigInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; +use LaminasTest\ServiceManager\TestAsset\AbstractFactoryFoo; +use LaminasTest\ServiceManager\TestAsset\InvokableObject; +use LaminasTest\ServiceManager\TestAsset\SimpleInitializer; use PHPUnit\Framework\TestCase; /** * @covers \Laminas\ServiceManager\Config + * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface */ final class ConfigTest extends TestCase { - public function testMergeArrays(): void - { - $config = [ - 'invokables' => [ - 'foo' => TestAsset\InvokableObject::class, - ], - 'delegators' => [ - 'foo' => [ - TestAsset\PreDelegator::class, - ], - ], - 'factories' => [ - 'service' => TestAsset\FactoryObject::class, - ], - ]; - - $configuration = new TestAsset\ExtendedConfig($config); - $result = $configuration->toArray(); - - $expected = [ - 'invokables' => [ - 'foo' => TestAsset\InvokableObject::class, - TestAsset\InvokableObject::class => TestAsset\InvokableObject::class, - ], - 'delegators' => [ - 'foo' => [ - TestAsset\InvokableObject::class, - TestAsset\PreDelegator::class, - ], - ], - 'factories' => [ - 'service' => TestAsset\FactoryObject::class, - ], - ]; - - self::assertEquals($expected, $result); - } - + /** + * @return array{"array":ServiceManagerConfigurationType,config:ConfigInterface} + */ public function testPassesKnownServiceConfigKeysToServiceManagerWithConfigMethod(): array { $expected = [ 'abstract_factories' => [ - self::class, - __NAMESPACE__, + AbstractFactoryFoo::class, ], 'aliases' => [ 'foo' => self::class, @@ -64,26 +34,21 @@ public function testPassesKnownServiceConfigKeysToServiceManagerWithConfigMethod ], 'delegators' => [ 'foo' => [ - self::class, - __NAMESPACE__, + Delegator1Factory::class, ], ], 'factories' => [ - 'foo' => self::class, - 'bar' => __NAMESPACE__, + 'bar' => AbstractFactoryFoo::class, ], 'initializers' => [ - self::class, - __NAMESPACE__, + SimpleInitializer::class, ], 'invokables' => [ - 'foo' => self::class, - 'bar' => __NAMESPACE__, + 'foo' => InvokableObject::class, ], 'lazy_services' => [ 'class_map' => [ - self::class => self::class, - __NAMESPACE__ => __NAMESPACE__, + self::class => self::class, ], ], 'services' => [ @@ -95,21 +60,17 @@ public function testPassesKnownServiceConfigKeysToServiceManagerWithConfigMethod ], ]; - $config = $expected + [ - 'foo' => 'bar', - 'baz' => 'bat', - ]; + $config = $expected; - $services = $this->createMock(ServiceManager::class); + $services = $this->createMock(ServiceLocatorInterface::class); $services ->expects(self::once()) ->method('configure') ->with($expected) - ->willReturn('CALLED'); + ->willReturnSelf(); - /** @psalm-suppress InvalidArgument Keeping this invalid configuration to ensure BC compatibility. */ $configuration = new Config($config); - self::assertEquals('CALLED', $configuration->configureServiceManager($services)); + self::assertEquals($services, $configuration->configureServiceManager($services)); return [ 'array' => $expected, @@ -118,6 +79,7 @@ public function testPassesKnownServiceConfigKeysToServiceManagerWithConfigMethod } /** + * @param array{"array":ServiceManagerConfigurationType,config:ConfigInterface} $dependencies * @depends testPassesKnownServiceConfigKeysToServiceManagerWithConfigMethod */ public function testToArrayReturnsConfiguration(array $dependencies): void diff --git a/test/ExamplePluginManagerTest.php b/test/ExamplePluginManagerTest.php index 31e26291..7c8b9c62 100644 --- a/test/ExamplePluginManagerTest.php +++ b/test/ExamplePluginManagerTest.php @@ -4,23 +4,30 @@ namespace LaminasTest\ServiceManager; +use Laminas\ServiceManager\AbstractPluginManager; +use Laminas\ServiceManager\ConfigInterface; use Laminas\ServiceManager\ServiceManager; use Laminas\ServiceManager\Test\CommonPluginManagerTrait; use LaminasTest\ServiceManager\TestAsset\InvokableObject; -use LaminasTest\ServiceManager\TestAsset\V2v3PluginManager; +use LaminasTest\ServiceManager\TestAsset\InvokableObjectPluginManager; use PHPUnit\Framework\TestCase; use RuntimeException; /** * Example test of using CommonPluginManagerTrait + * + * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface */ final class ExamplePluginManagerTest extends TestCase { use CommonPluginManagerTrait; - protected function getPluginManager(): V2v3PluginManager + /** + * @param ServiceManagerConfigurationType $config + */ + protected function getPluginManager(array $config = []): AbstractPluginManager { - return new V2v3PluginManager(new ServiceManager()); + return new InvokableObjectPluginManager(new ServiceManager()); } protected function getV2InvalidPluginException(): string diff --git a/test/Exception/CyclicAliasExceptionTest.php b/test/Exception/CyclicAliasExceptionTest.php index 991ae4c5..db52e9f9 100644 --- a/test/Exception/CyclicAliasExceptionTest.php +++ b/test/Exception/CyclicAliasExceptionTest.php @@ -30,7 +30,7 @@ public function testFromCyclicAlias(string $alias, array $aliases, string $expec * * @return array< * non-empty-string, - * array{0:non-empty-string,1:array,non-empty-string} + * array{0:non-empty-string,1:array,2:non-empty-string} * > */ public function cyclicAliasProvider(): array @@ -108,7 +108,7 @@ public function testFromAliasesMap(array $aliases, string $expectedMessage): voi } /** - * @return array, string}> + * @return array, 1: string}> */ public function aliasesProvider(): array { diff --git a/test/ServiceManagerTest.php b/test/ServiceManagerTest.php index d63eb116..b644e388 100644 --- a/test/ServiceManagerTest.php +++ b/test/ServiceManagerTest.php @@ -15,6 +15,7 @@ use LaminasTest\ServiceManager\TestAsset\SimpleServiceManager; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use ReflectionProperty; use stdClass; /** @@ -218,19 +219,13 @@ public function testMapsOneToOneInvokablesAsInvokableFactoriesInternally(): void ], ]; - $serviceManager = new class ($config) extends ServiceManager - { - public function getFactories(): array - { - return $this->factories; - } - }; + $serviceManager = new ServiceManager($config); self::assertSame( [ InvokableObject::class => InvokableFactory::class, ], - $serviceManager->getFactories(), + $this->extractPrivateProperty($serviceManager, 'factories'), 'Invokable object factory not found' ); } @@ -243,24 +238,13 @@ public function testMapsNonSymmetricInvokablesAsAliasPlusInvokableFactory(): voi ], ]; - $serviceManager = new class ($config) extends ServiceManager - { - public function getFactories(): array - { - return $this->factories; - } - - public function getAliases(): array - { - return $this->aliases; - } - }; + $serviceManager = new ServiceManager($config); self::assertSame( [ 'Invokable' => InvokableObject::class, ], - $serviceManager->getAliases(), + $this->extractPrivateProperty($serviceManager, 'aliases'), 'Alias not found for non-symmetric invokable' ); @@ -268,7 +252,7 @@ public function getAliases(): array [ InvokableObject::class => InvokableFactory::class, ], - $serviceManager->getFactories() + $this->extractPrivateProperty($serviceManager, 'factories'), ); } @@ -378,7 +362,7 @@ public function testAbstractFactoryShouldBeCheckedForResolvedAliasesInsteadOfAli [self::anything(), self::equalTo('Alias')], [self::anything(), self::equalTo('ServiceName')] ) - ->willReturnCallback(static fn ($context, string $name): bool => $name === 'Alias'); + ->willReturnCallback(static fn (mixed $context, string $name): bool => $name === 'Alias'); self::assertTrue($serviceManager->has('Alias')); } @@ -420,7 +404,7 @@ public function testResolvedAliasFromAbstractFactory(): void [self::anything(), 'Alias'], [self::anything(), 'ServiceName'] ) - ->willReturnCallback(static fn ($context, string $name): bool => $name === 'ServiceName'); + ->willReturnCallback(static fn (mixed $context, string $name): bool => $name === 'ServiceName'); self::assertTrue($serviceManager->has('Alias')); } @@ -459,7 +443,7 @@ public function testConfigureMultipleTimesAvoidsDuplicates(): void { $delegatorFactory = static function ( ContainerInterface $container, - $name, + string $name, callable $callback ): InvokableObject { /** @var InvokableObject $instance */ @@ -530,8 +514,7 @@ static function (object $service) use (&$initializerTwoCalled): object { } /** - * @param array $config - * @psalm-param ServiceManagerConfigurationType $config + * @param ServiceManagerConfigurationType $config * @param non-empty-string $serviceName * @param non-empty-string $alias * @dataProvider aliasedServices @@ -592,10 +575,7 @@ public function aliasedServices(): array [ 'abstract_factories' => [ new class implements AbstractFactoryInterface { - /** - * @param string $requestedName - */ - public function canCreate(ContainerInterface $container, $requestedName): bool + public function canCreate(ContainerInterface $container, string $requestedName): bool { return $requestedName === stdClass::class; } @@ -649,4 +629,28 @@ public function testHasVerifiesAliasesBeforeUsingAbstractFactories(): void self::assertTrue($serviceManager->has('config')); } + + /** + * @group mutation + * @covers \Laminas\ServiceManager\ServiceManager::mapLazyService + */ + public function testCanMapLazyServices(): void + { + $container = $this->createContainer(); + + $container->mapLazyService('foo', self::class); + $r = new ReflectionProperty($container, 'lazyServices'); + $lazyServices = $r->getValue($container); + + self::assertIsArray($lazyServices); + self::assertArrayHasKey('class_map', $lazyServices); + self::assertIsArray($lazyServices['class_map']); + self::assertArrayHasKey('foo', $lazyServices['class_map']); + self::assertEquals(self::class, $lazyServices['class_map']['foo']); + } + + private function extractPrivateProperty(object $object, string $propertyName): mixed + { + return (new ReflectionProperty($object, $propertyName))->getValue($object); + } } diff --git a/test/TestAsset/AbstractFactoryFoo.php b/test/TestAsset/AbstractFactoryFoo.php index 803a8e2e..1b05c5f1 100644 --- a/test/TestAsset/AbstractFactoryFoo.php +++ b/test/TestAsset/AbstractFactoryFoo.php @@ -10,7 +10,7 @@ final class AbstractFactoryFoo implements AbstractFactoryInterface { /** {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null) + public function __invoke(ContainerInterface $container, string $requestedName, ?array $options = null): mixed { if ($requestedName === 'foo') { return new Foo($options); @@ -19,10 +19,7 @@ public function __invoke(ContainerInterface $container, $requestedName, ?array $ return false; } - /** - * @param string $requestedName - */ - public function canCreate(ContainerInterface $container, $requestedName): bool + public function canCreate(ContainerInterface $container, string $requestedName): bool { return $requestedName === 'foo'; } diff --git a/test/TestAsset/CallTimesAbstractFactory.php b/test/TestAsset/CallTimesAbstractFactory.php index b7b27da2..4ec72463 100644 --- a/test/TestAsset/CallTimesAbstractFactory.php +++ b/test/TestAsset/CallTimesAbstractFactory.php @@ -12,7 +12,7 @@ final class CallTimesAbstractFactory implements AbstractFactoryInterface protected static int $callTimes = 0; /** {@inheritDoc} */ - public function canCreate(ContainerInterface $container, $name) + public function canCreate(ContainerInterface $container, string $name): bool { self::$callTimes++; @@ -20,7 +20,7 @@ public function canCreate(ContainerInterface $container, $name) } /** {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $className, ?array $options = null) + public function __invoke(ContainerInterface $container, string $className, ?array $options = null): mixed { } diff --git a/test/TestAsset/ExtendedConfig.php b/test/TestAsset/ExtendedConfig.php deleted file mode 100644 index cbc6ab4f..00000000 --- a/test/TestAsset/ExtendedConfig.php +++ /dev/null @@ -1,22 +0,0 @@ - [ - InvokableObject::class => InvokableObject::class, - ], - 'delegators' => [ - 'foo' => [ - InvokableObject::class, - ], - ], - ]; -} diff --git a/test/TestAsset/FailingAbstractFactory.php b/test/TestAsset/FailingAbstractFactory.php index bd57c106..6dbc74f8 100644 --- a/test/TestAsset/FailingAbstractFactory.php +++ b/test/TestAsset/FailingAbstractFactory.php @@ -10,13 +10,13 @@ final class FailingAbstractFactory implements AbstractFactoryInterface { /** {@inheritDoc} */ - public function canCreate(ContainerInterface $container, $name) + public function canCreate(ContainerInterface $container, string $name): bool { return false; } /** {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $className, ?array $options = null) + public function __invoke(ContainerInterface $container, string $className, ?array $options = null): mixed { } } diff --git a/test/TestAsset/FailingExceptionWithStringAsCodeFactory.php b/test/TestAsset/FailingExceptionWithStringAsCodeFactory.php index a4de3ad7..bf79830d 100644 --- a/test/TestAsset/FailingExceptionWithStringAsCodeFactory.php +++ b/test/TestAsset/FailingExceptionWithStringAsCodeFactory.php @@ -10,7 +10,7 @@ final class FailingExceptionWithStringAsCodeFactory implements FactoryInterface { /** {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null) + public function __invoke(ContainerInterface $container, string $requestedName, ?array $options = null): mixed { throw new ExceptionWithStringAsCodeException('There is an error'); } diff --git a/test/TestAsset/FailingFactory.php b/test/TestAsset/FailingFactory.php index 9861c8d7..31b84c49 100644 --- a/test/TestAsset/FailingFactory.php +++ b/test/TestAsset/FailingFactory.php @@ -11,7 +11,7 @@ final class FailingFactory implements FactoryInterface { /** {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null) + public function __invoke(ContainerInterface $container, string $requestedName, ?array $options = null): mixed { throw new RuntimeException('There is an error'); } diff --git a/test/TestAsset/InvokableObjectPluginManager.php b/test/TestAsset/InvokableObjectPluginManager.php new file mode 100644 index 00000000..632aa6a7 --- /dev/null +++ b/test/TestAsset/InvokableObjectPluginManager.php @@ -0,0 +1,39 @@ + + */ +final class InvokableObjectPluginManager extends AbstractPluginManager +{ + /** @var array */ + protected $aliases = [ + 'foo' => InvokableObject::class, + + // v2 normalized FQCNs + 'laminastestservicemanagertestassetinvokableobject' => InvokableObject::class, + ]; + + /** @var array */ + protected $factories = [ + InvokableObject::class => InvokableFactory::class, + // Legacy (v2) due to alias resolution + 'laminastestservicemanagertestassetinvokableobject' => InvokableFactory::class, + ]; + + protected string|null $instanceOf = InvokableObject::class; + + protected bool $sharedByDefault = false; + + public function __construct(ContainerInterface $creationContext) + { + parent::__construct($creationContext, ['aliases' => $this->aliases, 'factories' => $this->factories]); + } +} diff --git a/test/TestAsset/LenientPluginManager.php b/test/TestAsset/LenientPluginManager.php index d7ad8383..8e1c5623 100644 --- a/test/TestAsset/LenientPluginManager.php +++ b/test/TestAsset/LenientPluginManager.php @@ -5,21 +5,10 @@ namespace LaminasTest\ServiceManager\TestAsset; use Laminas\ServiceManager\AbstractPluginManager; -use Psr\Container\ContainerInterface; +/** + * @template-extends AbstractPluginManager + */ final class LenientPluginManager extends AbstractPluginManager { - /** - * Allow anything to be considered valid. - * - * @param mixed $instance - */ - public function validate($instance): void - { - } - - public function getCreationContext(): ContainerInterface - { - return $this->creationContext; - } } diff --git a/test/TestAsset/NonAutoInvokablePluginManager.php b/test/TestAsset/NonAutoInvokablePluginManager.php index 12303f42..73fa35f3 100644 --- a/test/TestAsset/NonAutoInvokablePluginManager.php +++ b/test/TestAsset/NonAutoInvokablePluginManager.php @@ -8,9 +8,7 @@ final class NonAutoInvokablePluginManager extends AbstractPluginManager { - /** @var bool */ - protected $autoAddInvokableClass = false; + protected bool $autoAddInvokableClass = false; - /** @var string */ - protected $instanceOf = InvokableObject::class; + protected string|null $instanceOf = InvokableObject::class; } diff --git a/test/TestAsset/PassthroughDelegatorFactory.php b/test/TestAsset/PassthroughDelegatorFactory.php index 425e8dc8..e1021a62 100644 --- a/test/TestAsset/PassthroughDelegatorFactory.php +++ b/test/TestAsset/PassthroughDelegatorFactory.php @@ -11,11 +11,13 @@ final class PassthroughDelegatorFactory implements DelegatorFactoryInterface { /** * {@inheritDoc} - * - * @see \Laminas\ServiceManager\Factory\DelegatorFactoryInterface::__invoke() */ - public function __invoke(ContainerInterface $container, $name, callable $callback, ?array $options = null) - { + public function __invoke( + ContainerInterface $container, + string $name, + callable $callback, + ?array $options = null + ): mixed { return $callback(); } } diff --git a/test/TestAsset/PreDelegator.php b/test/TestAsset/PreDelegator.php index c88634b7..ea7bedc9 100644 --- a/test/TestAsset/PreDelegator.php +++ b/test/TestAsset/PreDelegator.php @@ -10,8 +10,12 @@ final class PreDelegator implements DelegatorFactoryInterface { /** {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $name, callable $callback, ?array $options = null) - { + public function __invoke( + ContainerInterface $container, + string $name, + callable $callback, + ?array $options = null + ): mixed { if (! $container->has('config')) { return $callback(); } diff --git a/test/TestAsset/SampleFactory.php b/test/TestAsset/SampleFactory.php index 5f5ff6e0..b57d83f5 100644 --- a/test/TestAsset/SampleFactory.php +++ b/test/TestAsset/SampleFactory.php @@ -10,11 +10,13 @@ final class SampleFactory implements FactoryInterface { /** - * @param string $requestedName * @param array|null $options */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null): InvokableObject - { + public function __invoke( + ContainerInterface $container, + string $requestedName, + ?array $options = null + ): InvokableObject { return new InvokableObject(); } } diff --git a/test/TestAsset/SimpleAbstractFactory.php b/test/TestAsset/SimpleAbstractFactory.php index 68079107..32f820fb 100644 --- a/test/TestAsset/SimpleAbstractFactory.php +++ b/test/TestAsset/SimpleAbstractFactory.php @@ -10,13 +10,13 @@ final class SimpleAbstractFactory implements AbstractFactoryInterface { /** {@inheritDoc} */ - public function canCreate(ContainerInterface $container, $name) + public function canCreate(ContainerInterface $container, string $name): bool { return true; } /** {@inheritDoc} */ - public function __invoke(ContainerInterface $container, $className, ?array $options = null) + public function __invoke(ContainerInterface $container, string $className, ?array $options = null): mixed { if (empty($options)) { return new $className(); diff --git a/test/TestAsset/SimplePluginManager.php b/test/TestAsset/SimplePluginManager.php index b0d8fa38..f03f58f8 100644 --- a/test/TestAsset/SimplePluginManager.php +++ b/test/TestAsset/SimplePluginManager.php @@ -8,6 +8,5 @@ final class SimplePluginManager extends AbstractPluginManager { - /** @var string */ - protected $instanceOf = InvokableObject::class; + protected string|null $instanceOf = InvokableObject::class; } diff --git a/test/TestAsset/V2v3PluginManager.php b/test/TestAsset/V2v3PluginManager.php deleted file mode 100644 index 8c68462b..00000000 --- a/test/TestAsset/V2v3PluginManager.php +++ /dev/null @@ -1,74 +0,0 @@ - */ - protected $aliases = [ - 'foo' => InvokableObject::class, - - // Legacy Zend Framework aliases - \ZendTest\ServiceManager\TestAsset\InvokableObject::class => InvokableObject::class, - - // v2 normalized FQCNs - 'zendtestservicemanagertestassetinvokableobject' => InvokableObject::class, - ]; - - /** @var array */ - protected $factories = [ - InvokableObject::class => InvokableFactory::class, - // Legacy (v2) due to alias resolution - 'laminastestservicemanagertestassetinvokableobject' => InvokableFactory::class, - ]; - - /** @var string */ - protected $instanceOf = InvokableObject::class; - - /** @var bool */ - protected $shareByDefault = false; - - /** @var bool */ - protected $sharedByDefault = false; - - /** - * @param mixed $plugin - * @return void - * @throws InvalidServiceException - */ - public function validate($plugin) - { - if ($plugin instanceof $this->instanceOf) { - return; - } - - throw new InvalidServiceException(sprintf( - "'%s' is not an instance of '%s'", - $plugin::class, - $this->instanceOf - )); - } - - /** - * @param mixed $plugin - * @return void - * @throws RuntimeException - */ - public function validatePlugin($plugin) - { - try { - $this->validate($plugin); - } catch (InvalidServiceException $e) { - throw new RuntimeException($e->getMessage(), $e->getCode(), $e); - } - } -} diff --git a/test/TestAsset/factories/ComplexDependencyObject.php b/test/TestAsset/factories/ComplexDependencyObject.php index 6b845bc2..c65d1ce7 100644 --- a/test/TestAsset/factories/ComplexDependencyObject.php +++ b/test/TestAsset/factories/ComplexDependencyObject.php @@ -9,13 +9,7 @@ class ComplexDependencyObjectFactory implements FactoryInterface { - /** - * @param ContainerInterface $container - * @param string $requestedName - * @param null|array $options - * @return ComplexDependencyObject - */ - public function __invoke(ContainerInterface $container, $requestedName, array $options = null) + public function __invoke(ContainerInterface $container, string $requestedName, array $options = null): ComplexDependencyObject { return new ComplexDependencyObject( $container->get(\LaminasTest\ServiceManager\TestAsset\SimpleDependencyObject::class), diff --git a/test/TestAsset/factories/InvokableObject.php b/test/TestAsset/factories/InvokableObject.php index a3d229ea..328e4f2b 100644 --- a/test/TestAsset/factories/InvokableObject.php +++ b/test/TestAsset/factories/InvokableObject.php @@ -9,13 +9,7 @@ class InvokableObjectFactory implements FactoryInterface { - /** - * @param ContainerInterface $container - * @param string $requestedName - * @param null|array $options - * @return InvokableObject - */ - public function __invoke(ContainerInterface $container, $requestedName, array $options = null) + public function __invoke(ContainerInterface $container, string $requestedName, array $options = null): InvokableObject { return new InvokableObject([]); } diff --git a/test/TestAsset/factories/SimpleDependencyObject.php b/test/TestAsset/factories/SimpleDependencyObject.php index ee543ec3..ce47f4fe 100644 --- a/test/TestAsset/factories/SimpleDependencyObject.php +++ b/test/TestAsset/factories/SimpleDependencyObject.php @@ -9,13 +9,7 @@ class SimpleDependencyObjectFactory implements FactoryInterface { - /** - * @param ContainerInterface $container - * @param string $requestedName - * @param null|array $options - * @return SimpleDependencyObject - */ - public function __invoke(ContainerInterface $container, $requestedName, array $options = null) + public function __invoke(ContainerInterface $container, string $requestedName, array $options = null): SimpleDependencyObject { return new SimpleDependencyObject($container->get(\LaminasTest\ServiceManager\TestAsset\InvokableObject::class)); } From beb8ae5d253ea2ef06c688644d76bcbb007ba565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 23 Feb 2023 21:16:06 +0100 Subject: [PATCH 02/17] qa: bump `psr/container` to a minimum of v1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- composer.json | 2 +- composer.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index cdee3423..bc71b676 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "php": "~8.1.0 || ~8.2.0", "brick/varexporter": "^0.3.8", "laminas/laminas-stdlib": "^3.2.1", - "psr/container": "^1.0" + "psr/container": "^1.1" }, "extra": { "laminas": { diff --git a/composer.lock b/composer.lock index 02075f58..0f5bae1b 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": "b45b0010cee4181db031a2a43aaeea6e", + "content-hash": "7e9ce5c985e93c6f56753b158acc8b22", "packages": [ { "name": "brick/varexporter", From eb9cfcede6ce7d57dd6dcd5b3f5d709b1b871b78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 23 Feb 2023 22:20:45 +0100 Subject: [PATCH 03/17] qa: remove `laminas/laminas-dependency-plugin` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index bc71b676..adfea6dc 100644 --- a/composer.json +++ b/composer.json @@ -27,8 +27,7 @@ "sort-packages": true, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, - "composer/package-versions-deprecated": true, - "laminas/laminas-dependency-plugin": true + "composer/package-versions-deprecated": true } }, "require": { From 1833d2e34513f08eeb5212480beb4124bebcbdb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 23 Feb 2023 22:40:57 +0100 Subject: [PATCH 04/17] qa: remove some unnecessary codepaths and add some more types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- src/ServiceManager.php | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/ServiceManager.php b/src/ServiceManager.php index 47e8fbd6..5653f448 100644 --- a/src/ServiceManager.php +++ b/src/ServiceManager.php @@ -213,12 +213,7 @@ public function get($name) // Cache the object for later, if it is supposed to be shared. if ($sharedService) { $this->services[$resolvedName] = $service; - } - - // Also cache under the alias name; this allows sharing based on the - // service name used. - if ($sharedAlias) { - $this->services[$name] = $service; + $this->services[$name] = $service; } return $service; @@ -389,7 +384,7 @@ public function setFactory(string $name, string|callable|Factory\FactoryInterfac * @param null|string $class Class to which to map; if not provided, $name * will be used for the mapping. */ - public function mapLazyService(string $name, ?string $class = null) + public function mapLazyService(string $name, ?string $class = null): void { $this->configure(['lazy_services' => ['class_map' => [$name => $class ?: $name]]]); } @@ -413,9 +408,10 @@ public function addAbstractFactory($factory) * @param string|callable|DelegatorFactoryInterface $factory Delegator * factory to assign. * @psalm-param class-string - * |callable(ContainerInterface,string,callable,array|null) $factory + * |DelegatorCallableType + * |Factory\DelegatorFactoryInterface $factory */ - public function addDelegator($name, $factory) + public function addDelegator(string $name, string|callable|Factory\DelegatorFactoryInterface $factory): void { $this->configure(['delegators' => [$name => [$factory]]]); } @@ -423,12 +419,11 @@ public function addDelegator($name, $factory) /** * Add an initializer. * - * @param string|callable|Initializer\InitializerInterface $initializer * @psalm-param class-string - * |callable(ContainerInterface,mixed):void + * |InitializerCallableType * |Initializer\InitializerInterface $initializer */ - public function addInitializer($initializer) + public function addInitializer(string|callable|Initializer\InitializerInterface $initializer) { $this->configure(['initializers' => [$initializer]]); } @@ -937,10 +932,6 @@ private function assertCallableDelegatorFactory(mixed $delegatorFactory): void */ private function resolveDelegatorFactory(mixed $delegatorFactory): callable|DelegatorFactoryInterface { - if ($delegatorFactory instanceof DelegatorFactoryInterface) { - return $delegatorFactory; - } - if ($delegatorFactory === LazyServiceFactory::class) { return $this->createLazyServiceDelegatorFactory(); } From 8730086373f0e35ed31df3ad688fbcffd6edbc7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 9 Mar 2023 11:34:49 +0100 Subject: [PATCH 05/17] qa: remove `PluginManagerInterface#configure` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interface should provide read-only methods and thus, keeping the `configure` method only in the abstract implementation. Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- src/PluginManagerInterface.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/PluginManagerInterface.php b/src/PluginManagerInterface.php index d7605321..ca9087c5 100644 --- a/src/PluginManagerInterface.php +++ b/src/PluginManagerInterface.php @@ -4,8 +4,6 @@ namespace Laminas\ServiceManager; -use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException; -use Laminas\ServiceManager\Exception\CyclicAliasException; use Laminas\ServiceManager\Exception\InvalidServiceException; use Psr\Container\ContainerExceptionInterface; @@ -54,14 +52,4 @@ public function get(string $id): mixed; * @throws ContainerExceptionInterface If any other error occurs. */ public function build(string $name, ?array $options = null): mixed; - - /** - * @param ServiceManagerConfigurationType $config - * @throws ContainerModificationsNotAllowedException If the allow override flag has been toggled off, and a - * service instanceexists for a given service. - * @throws InvalidServiceException If an instance passed in the `services` configuration is invalid for the - * plugin manager. - * @throws CyclicAliasException If the configuration contains aliases targeting themselves. - */ - public function configure(array $config): static; } From b02617699f1ff5edee75545d23db02b51f4ccf58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 9 Mar 2023 13:41:16 +0100 Subject: [PATCH 06/17] feature: move `AbstractPluginManager#validate` to `AbstractSingleInstancePluginManager` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will require implementations to implement `PluginManagerInterface#validate` in case it has to be more complex than verifying a single instance. Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- src/AbstractPluginManager.php | 24 ----------- src/AbstractSingleInstancePluginManager.php | 45 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 24 deletions(-) create mode 100644 src/AbstractSingleInstancePluginManager.php diff --git a/src/AbstractPluginManager.php b/src/AbstractPluginManager.php index 8060fbc2..c5dbba96 100644 --- a/src/AbstractPluginManager.php +++ b/src/AbstractPluginManager.php @@ -41,13 +41,6 @@ abstract class AbstractPluginManager implements PluginManagerInterface */ protected bool $autoAddInvokableClass = true; - /** - * An object type that the created instance must be instanced of - * - * @var null|class-string - */ - protected ?string $instanceOf = null; - protected bool $sharedByDefault = true; private ServiceManager $plugins; @@ -121,23 +114,6 @@ public function get($id): mixed return $instance; } - /** - * {@inheritDoc} - */ - public function validate(mixed $instance): void - { - if ($this->instanceOf === null || $instance instanceof $this->instanceOf) { - return; - } - - throw new InvalidServiceException(sprintf( - 'Plugin manager "%s" expected an instance of type "%s", but "%s" was received', - self::class, - $this->instanceOf, - is_object($instance) ? $instance::class : gettype($instance) - )); - } - /** * {@inheritDoc} */ diff --git a/src/AbstractSingleInstancePluginManager.php b/src/AbstractSingleInstancePluginManager.php new file mode 100644 index 00000000..e97b218d --- /dev/null +++ b/src/AbstractSingleInstancePluginManager.php @@ -0,0 +1,45 @@ + + */ +abstract class AbstractSingleInstancePluginManager extends AbstractPluginManager +{ + /** + * An object type that the created instance must be instanced of + * + * @var class-string + */ + protected string $instanceOf; + + /** + * {@inheritDoc} + */ + public function validate(mixed $instance): void + { + if ($instance instanceof $this->instanceOf) { + return; + } + + throw new InvalidServiceException(sprintf( + 'Plugin manager "%s" expected an instance of type "%s", but "%s" was received', + static::class, + $this->instanceOf, + get_debug_type($instance) + )); + } +} From ee7cfc593f860a7f03ef288cd969d6fe10c276ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 9 Mar 2023 13:43:23 +0100 Subject: [PATCH 07/17] qa: re-implement `ServiceManager` properties (and mark deprecated) to `AbstractPluginManager` which allowed implementations to configure themselves MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is mostly provided as a BC layer to keep the overall maintenance to migrate to the next major version low. Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- psalm.xml.dist | 12 ++ src/AbstractPluginManager.php | 131 +++++++++++++++--- .../TestAsset/ValidatorPluginManager.php | 3 + .../InvokableObjectPluginManager.php | 9 +- test/TestAsset/LenientPluginManager.php | 3 + .../NonAutoInvokablePluginManager.php | 6 +- test/TestAsset/SimplePluginManager.php | 6 +- test/TestAsset/V2ValidationPluginManager.php | 32 ----- 8 files changed, 138 insertions(+), 64 deletions(-) delete mode 100644 test/TestAsset/V2ValidationPluginManager.php diff --git a/psalm.xml.dist b/psalm.xml.dist index 343017a4..b31c127b 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -33,6 +33,18 @@ + + + + + + + + + + + + diff --git a/src/AbstractPluginManager.php b/src/AbstractPluginManager.php index c5dbba96..3fdc5183 100644 --- a/src/AbstractPluginManager.php +++ b/src/AbstractPluginManager.php @@ -5,27 +5,22 @@ namespace Laminas\ServiceManager; use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException; +use Laminas\ServiceManager\Exception\CyclicAliasException; use Laminas\ServiceManager\Exception\InvalidServiceException; use Laminas\ServiceManager\Factory\AbstractFactoryInterface; use Laminas\ServiceManager\Factory\DelegatorFactoryInterface; use Laminas\ServiceManager\Initializer\InitializerInterface; +use Laminas\Stdlib\ArrayUtils; use Psr\Container\ContainerInterface; use function class_exists; -use function gettype; -use function is_object; use function sprintf; /** * Abstract plugin manager. * - * Abstract PluginManagerInterface implementation providing: - * - * - creation context support. The constructor accepts the parent container - * instance, which is then used when creating instances. - * - plugin validation. Implementations may define the `$instanceOf` property - * to indicate what class types constitute valid plugins, omitting the - * requirement to define the `validate()` method. + * Abstract PluginManagerInterface implementation providing creation context support. + * The constructor accepts the parent container instance, which is then used when creating instances. * * @template InstanceType * @template-implements PluginManagerInterface @@ -33,6 +28,11 @@ * @psalm-import-type FactoryCallableType from ConfigInterface * @psalm-import-type DelegatorCallableType from ConfigInterface * @psalm-import-type InitializerCallableType from ConfigInterface + * @psalm-import-type AbstractFactoriesConfigurationType from ConfigInterface + * @psalm-import-type DelegatorsConfigurationType from ConfigInterface + * @psalm-import-type FactoriesConfigurationType from ConfigInterface + * @psalm-import-type InitializersConfigurationType from ConfigInterface + * @psalm-import-type LazyServicesConfigurationType from ConfigInterface */ abstract class AbstractPluginManager implements PluginManagerInterface { @@ -43,6 +43,79 @@ abstract class AbstractPluginManager implements PluginManagerInterface protected bool $sharedByDefault = true; + /** + * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. + * + * @var AbstractFactoryInterface[] + */ + protected array $abstractFactories = []; + + /** + * A list of aliases + * + * Should map one alias to a service name, or another alias (aliases are recursively resolved) + * + * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. + * + * @var string[] + */ + protected array $aliases = []; + + /** + * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. + * + * @var DelegatorsConfigurationType + */ + protected array $delegators = []; + + /** + * A list of factories (either as string name or callable) + * + * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. + * + * @var FactoriesConfigurationType + */ + protected array $factories = []; + + /** + * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. + * + * @var InitializersConfigurationType + */ + protected array $initializers = []; + + /** + * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. + * + * @var LazyServicesConfigurationType + */ + protected array $lazyServices = []; + + /** + * A list of already loaded services (this act as a local cache) + * + * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. + * + * @var array + */ + protected array $services = []; + + /** + * Enable/disable shared instances by service name. + * + * Example configuration: + * + * 'shared' => [ + * MyService::class => true, // will be shared, even if "sharedByDefault" is false + * MyOtherService::class => false // won't be shared, even if "sharedByDefault" is true + * ] + * + * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. + * + * @var array + */ + protected array $shared = []; + private ServiceManager $plugins; /** @@ -56,12 +129,28 @@ public function __construct( 'shared_by_default' => $this->sharedByDefault, ], $creationContext); - // TODO: support old, internal, servicemanager properties in constructor to register services from properties + /** @var ServiceManagerConfigurationType $config */ + $config = ArrayUtils::merge([ + 'factories' => $this->factories, + 'abstract_factories' => $this->abstractFactories, + 'aliases' => $this->aliases, + 'services' => $this->services, + 'lazy_services' => $this->lazyServices, + 'shared' => $this->shared, + 'delegators' => $this->delegators, + 'initializers' => $this->initializers, + ], $config); + $this->configure($config); } /** - * {@inheritDoc} + * @param ServiceManagerConfigurationType $config + * @throws ContainerModificationsNotAllowedException If the allow override flag has been toggled off, and a + * service instanceexists for a given service. + * @throws InvalidServiceException If an instance passed in the `services` configuration is invalid for the + * plugin manager. + * @throws CyclicAliasException If the configuration contains aliases targeting themselves. */ public function configure(array $config): static { @@ -79,7 +168,7 @@ public function configure(array $config): static } /** - * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * * @param string|class-string $name * @param InstanceType $service @@ -110,7 +199,6 @@ public function get($id): mixed /** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */ $instance = $this->plugins->get($id); $this->validate($instance); - /** @psalm-suppress MixedReturnStatement Yes indeed, plugin managers can return mixed. */ return $instance; } @@ -131,14 +219,13 @@ public function build(string $name, ?array $options = null): mixed $plugin = $this->plugins->build($name, $options); $this->validate($plugin); - /** @psalm-suppress MixedReturnStatement Yes indeed, service managers can return mixed. */ return $plugin; } /** * Add an alias. * - * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * * @throws ContainerModificationsNotAllowedException If $alias already * exists as a service and overrides are disallowed. @@ -151,7 +238,7 @@ public function setAlias(string $alias, string $target): void /** * Add an invokable class mapping. * - * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * * @param null|string $class Class to which to map; if omitted, $name is * assumed. @@ -166,7 +253,7 @@ public function setInvokableClass(string $name, string|null $class = null): void /** * Specify a factory for a given service name. * - * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * * @param class-string|FactoryCallableType|Factory\FactoryInterface $factory * @throws ContainerModificationsNotAllowedException If $name already @@ -180,7 +267,7 @@ public function setFactory(string $name, string|callable|Factory\FactoryInterfac /** * Create a lazy service mapping to a class. * - * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * * @param null|string $class Class to which to map; if not provided, $name * will be used for the mapping. @@ -193,7 +280,7 @@ public function mapLazyService(string $name, string|null $class = null): void /** * Add an abstract factory for resolving services. * - * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * * @param string|AbstractFactoryInterface $factory Abstract factory * instance or class name. @@ -207,7 +294,7 @@ public function addAbstractFactory(string|AbstractFactoryInterface $factory): vo /** * Add a delegator for a given service. * - * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * * @param string $name Service name * @param string|callable|DelegatorFactoryInterface $factory Delegator @@ -222,7 +309,7 @@ public function addDelegator(string $name, string|callable|DelegatorFactoryInter /** * Add an initializer. * - * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * * @psalm-param class-string|InitializerCallableType|InitializerInterface $initializer */ @@ -234,7 +321,7 @@ public function addInitializer(string|callable|InitializerInterface $initializer /** * Add a service sharing rule. * - * @deprecated Please use {@see PluginManagerInterface::configure()} instead. + * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * * @param bool $flag Whether or not the service should be shared. * @throws ContainerModificationsNotAllowedException If $name already diff --git a/test/AbstractFactory/TestAsset/ValidatorPluginManager.php b/test/AbstractFactory/TestAsset/ValidatorPluginManager.php index 05b68562..119c7912 100644 --- a/test/AbstractFactory/TestAsset/ValidatorPluginManager.php +++ b/test/AbstractFactory/TestAsset/ValidatorPluginManager.php @@ -8,4 +8,7 @@ class ValidatorPluginManager extends AbstractPluginManager { + public function validate(mixed $instance): void + { + } } diff --git a/test/TestAsset/InvokableObjectPluginManager.php b/test/TestAsset/InvokableObjectPluginManager.php index 632aa6a7..46f221cf 100644 --- a/test/TestAsset/InvokableObjectPluginManager.php +++ b/test/TestAsset/InvokableObjectPluginManager.php @@ -5,16 +5,17 @@ namespace LaminasTest\ServiceManager\TestAsset; use Laminas\ServiceManager\AbstractPluginManager; +use Laminas\ServiceManager\AbstractSingleInstancePluginManager; use Laminas\ServiceManager\Factory\InvokableFactory; use Psr\Container\ContainerInterface; /** * @template-extends AbstractPluginManager */ -final class InvokableObjectPluginManager extends AbstractPluginManager +final class InvokableObjectPluginManager extends AbstractSingleInstancePluginManager { /** @var array */ - protected $aliases = [ + protected array $aliases = [ 'foo' => InvokableObject::class, // v2 normalized FQCNs @@ -22,13 +23,13 @@ final class InvokableObjectPluginManager extends AbstractPluginManager ]; /** @var array */ - protected $factories = [ + protected array $factories = [ InvokableObject::class => InvokableFactory::class, // Legacy (v2) due to alias resolution 'laminastestservicemanagertestassetinvokableobject' => InvokableFactory::class, ]; - protected string|null $instanceOf = InvokableObject::class; + protected string $instanceOf = InvokableObject::class; protected bool $sharedByDefault = false; diff --git a/test/TestAsset/LenientPluginManager.php b/test/TestAsset/LenientPluginManager.php index 8e1c5623..ff2c520a 100644 --- a/test/TestAsset/LenientPluginManager.php +++ b/test/TestAsset/LenientPluginManager.php @@ -11,4 +11,7 @@ */ final class LenientPluginManager extends AbstractPluginManager { + public function validate(mixed $instance): void + { + } } diff --git a/test/TestAsset/NonAutoInvokablePluginManager.php b/test/TestAsset/NonAutoInvokablePluginManager.php index 73fa35f3..e3e650b9 100644 --- a/test/TestAsset/NonAutoInvokablePluginManager.php +++ b/test/TestAsset/NonAutoInvokablePluginManager.php @@ -4,11 +4,11 @@ namespace LaminasTest\ServiceManager\TestAsset; -use Laminas\ServiceManager\AbstractPluginManager; +use Laminas\ServiceManager\AbstractSingleInstancePluginManager; -final class NonAutoInvokablePluginManager extends AbstractPluginManager +final class NonAutoInvokablePluginManager extends AbstractSingleInstancePluginManager { protected bool $autoAddInvokableClass = false; - protected string|null $instanceOf = InvokableObject::class; + protected string $instanceOf = InvokableObject::class; } diff --git a/test/TestAsset/SimplePluginManager.php b/test/TestAsset/SimplePluginManager.php index f03f58f8..2e6fd6b9 100644 --- a/test/TestAsset/SimplePluginManager.php +++ b/test/TestAsset/SimplePluginManager.php @@ -4,9 +4,9 @@ namespace LaminasTest\ServiceManager\TestAsset; -use Laminas\ServiceManager\AbstractPluginManager; +use Laminas\ServiceManager\AbstractSingleInstancePluginManager; -final class SimplePluginManager extends AbstractPluginManager +final class SimplePluginManager extends AbstractSingleInstancePluginManager { - protected string|null $instanceOf = InvokableObject::class; + protected string $instanceOf = InvokableObject::class; } diff --git a/test/TestAsset/V2ValidationPluginManager.php b/test/TestAsset/V2ValidationPluginManager.php deleted file mode 100644 index 44a564fe..00000000 --- a/test/TestAsset/V2ValidationPluginManager.php +++ /dev/null @@ -1,32 +0,0 @@ -assertion)) { - throw new RuntimeException(sprintf( - '%s requires a callable $assertion property; not currently set', - self::class - )); - } - - ($this->assertion)($plugin); - } -} From 5fb1be366010d9d3eb8865af393750367f6c5e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 9 Mar 2023 13:54:13 +0100 Subject: [PATCH 08/17] qa: remove `ServiceLocatorInterface#configure` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- src/ServiceLocatorInterface.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/ServiceLocatorInterface.php b/src/ServiceLocatorInterface.php index 591ad66e..06ad281a 100644 --- a/src/ServiceLocatorInterface.php +++ b/src/ServiceLocatorInterface.php @@ -4,8 +4,6 @@ namespace Laminas\ServiceManager; -use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException; -use Laminas\ServiceManager\Exception\CyclicAliasException; use Laminas\ServiceManager\Exception\ServiceNotCreatedException; use Laminas\ServiceManager\Exception\ServiceNotFoundException; use Psr\Container\ContainerExceptionInterface; @@ -31,12 +29,4 @@ interface ServiceLocatorInterface extends ContainerInterface * @throws ContainerExceptionInterface If any other error occurs. */ public function build(string $name, ?array $options = null): mixed; - - /** - * @param ServiceManagerConfigurationType $config - * @throws ContainerModificationsNotAllowedException If the allow override flag has been toggled off, and a - * service instanceexists for a given service. - * @throws CyclicAliasException If the configuration contains aliases targeting themselves. - */ - public function configure(array $config): static; } From e0efcebd06732d9f2577ae79a030f24653402bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 9 Mar 2023 13:54:25 +0100 Subject: [PATCH 09/17] qa: remove obsolete psalm-type imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- src/PluginManagerInterface.php | 1 - src/ServiceLocatorInterface.php | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/PluginManagerInterface.php b/src/PluginManagerInterface.php index ca9087c5..5a32350f 100644 --- a/src/PluginManagerInterface.php +++ b/src/PluginManagerInterface.php @@ -13,7 +13,6 @@ * A plugin manager is a specialized service locator used to create homogeneous objects * * @template InstanceType - * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface */ interface PluginManagerInterface extends ServiceLocatorInterface { diff --git a/src/ServiceLocatorInterface.php b/src/ServiceLocatorInterface.php index 06ad281a..a7ecb161 100644 --- a/src/ServiceLocatorInterface.php +++ b/src/ServiceLocatorInterface.php @@ -11,8 +11,6 @@ /** * Interface for service locator - * - * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface */ interface ServiceLocatorInterface extends ContainerInterface { From 8a51b5fab1e47dc2508ca4eede18133c1532b13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 6 Apr 2023 04:51:35 +0200 Subject: [PATCH 10/17] refactor: remove `Config` and `ConfigInterface` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This moves all configuration types to `ServiceManager`. Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- psalm-baseline.xml | 3 +- psalm.xml.dist | 22 +-- src/AbstractPluginManager.php | 18 +-- src/Config.php | 64 --------- src/ConfigInterface.php | 98 ------------- src/ConfigProvider.php | 2 +- src/ServiceManager.php | 147 +++++++++++--------- test/CommonServiceLocatorBehaviorsTrait.php | 4 +- test/ConfigTest.php | 92 ------------ test/ExamplePluginManagerTest.php | 3 +- test/ServiceManagerTest.php | 5 +- 11 files changed, 110 insertions(+), 348 deletions(-) delete mode 100644 src/Config.php delete mode 100644 src/ConfigInterface.php delete mode 100644 test/ConfigTest.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index f80e1fcb..9ba175de 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -90,8 +90,7 @@ - - ['delegators' => [$name => [$factory]]] + ['initializers' => [$initializer]] ['lazy_services' => ['class_map' => [$name => $class ?: $name]]] diff --git a/psalm.xml.dist b/psalm.xml.dist index b31c127b..eec24b01 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -33,6 +33,13 @@ + + + + + + + @@ -45,16 +52,6 @@ - - - - - - - - - - @@ -73,6 +70,11 @@ + + + + + diff --git a/src/AbstractPluginManager.php b/src/AbstractPluginManager.php index 3fdc5183..6eee59b4 100644 --- a/src/AbstractPluginManager.php +++ b/src/AbstractPluginManager.php @@ -24,15 +24,15 @@ * * @template InstanceType * @template-implements PluginManagerInterface - * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface - * @psalm-import-type FactoryCallableType from ConfigInterface - * @psalm-import-type DelegatorCallableType from ConfigInterface - * @psalm-import-type InitializerCallableType from ConfigInterface - * @psalm-import-type AbstractFactoriesConfigurationType from ConfigInterface - * @psalm-import-type DelegatorsConfigurationType from ConfigInterface - * @psalm-import-type FactoriesConfigurationType from ConfigInterface - * @psalm-import-type InitializersConfigurationType from ConfigInterface - * @psalm-import-type LazyServicesConfigurationType from ConfigInterface + * @psalm-import-type ServiceManagerConfigurationType from ServiceManager + * @psalm-import-type FactoryCallableType from ServiceManager + * @psalm-import-type DelegatorCallableType from ServiceManager + * @psalm-import-type InitializerCallableType from ServiceManager + * @psalm-import-type AbstractFactoriesConfigurationType from ServiceManager + * @psalm-import-type DelegatorsConfigurationType from ServiceManager + * @psalm-import-type FactoriesConfigurationType from ServiceManager + * @psalm-import-type InitializersConfigurationType from ServiceManager + * @psalm-import-type LazyServicesConfigurationType from ServiceManager */ abstract class AbstractPluginManager implements PluginManagerInterface { diff --git a/src/Config.php b/src/Config.php deleted file mode 100644 index 138fdf1c..00000000 --- a/src/Config.php +++ /dev/null @@ -1,64 +0,0 @@ - true, - 'aliases' => true, - 'delegators' => true, - 'factories' => true, - 'initializers' => true, - 'invokables' => true, - 'lazy_services' => true, - 'services' => true, - 'shared' => true, - ]; - - /** @var ServiceManagerConfigurationType */ - private array $config; - - /** - * @param ServiceManagerConfigurationType $config - */ - public function __construct(array $config = []) - { - $this->config = $config; - } - - /** - * @inheritDoc - */ - public function configureServiceManager(ServiceLocatorInterface $serviceLocator) - { - return $serviceLocator->configure($this->config); - } - - /** - * @inheritDoc - */ - public function toArray(): array - { - return $this->config; - } -} diff --git a/src/ConfigInterface.php b/src/ConfigInterface.php deleted file mode 100644 index 9a80712e..00000000 --- a/src/ConfigInterface.php +++ /dev/null @@ -1,98 +0,0 @@ - - * |Factory\AbstractFactoryInterface - * > - * @psalm-type DelegatorCallableType = callable(ContainerInterface,string,callable():mixed,array|null):mixed - * @psalm-type DelegatorsConfigurationType = array< - * string, - * array< - * array-key, - * class-string - * |Factory\DelegatorFactoryInterface - * |DelegatorCallableType - * > - * > - * @psalm-type FactoryCallableType = callable(ContainerInterface,string,array|null):mixed - * @psalm-type FactoriesConfigurationType = array< - * string, - * class-string - * |Factory\FactoryInterface - * |FactoryCallableType - * > - * @psalm-type InitializerCallableType = callable(ContainerInterface,mixed):void - * @psalm-type InitializersConfigurationType = array< - * array-key, - * class-string - * |Initializer\InitializerInterface - * |InitializerCallableType - * > - * @psalm-type LazyServicesConfigurationType = array{ - * class_map?:array, - * proxies_namespace?:non-empty-string, - * proxies_target_dir?:non-empty-string, - * write_proxy_files?:bool - * } - * @psalm-type ServiceManagerConfigurationType = array{ - * abstract_factories?: AbstractFactoriesConfigurationType, - * aliases?: array, - * delegators?: DelegatorsConfigurationType, - * factories?: FactoriesConfigurationType, - * initializers?: InitializersConfigurationType, - * invokables?: array, - * lazy_services?: LazyServicesConfigurationType, - * services?: array, - * shared?:array, - * shared_by_default?: bool, - * ... - * } - */ -interface ConfigInterface -{ - /** - * Configure a service manager. - * - * Implementations should pull configuration from somewhere (typically - * local properties) and pass it to a ServiceManager's withConfig() method, - * returning a new instance. - * - * @template T of ServiceLocatorInterface - * @param T $serviceLocator - * @return T - */ - public function configureServiceManager(ServiceLocatorInterface $serviceLocator); - - /** - * Return configuration for a service manager instance as an array. - * - * Implementations MUST return an array compatible with ServiceManager::configure, - * containing one or more of the following keys: - * - * - abstract_factories - * - aliases - * - delegators - * - factories - * - initializers - * - invokables - * - lazy_services - * - services - * - shared - * - * In other words, this should return configuration that can be used to instantiate - * a service manager or plugin manager, or pass to its `withConfig()` method. - * - * @return ServiceManagerConfigurationType - */ - public function toArray(): array; -} diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index abc79e56..24a51c18 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -22,7 +22,7 @@ use function class_exists; /** - * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface + * @psalm-import-type ServiceManagerConfigurationType from ServiceManager */ final class ConfigProvider { diff --git a/src/ServiceManager.php b/src/ServiceManager.php index 5653f448..75b0d227 100644 --- a/src/ServiceManager.php +++ b/src/ServiceManager.php @@ -8,9 +8,14 @@ use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException; use Laminas\ServiceManager\Exception\CyclicAliasException; use Laminas\ServiceManager\Exception\InvalidArgumentException; +use Laminas\ServiceManager\Exception\InvalidServiceException; use Laminas\ServiceManager\Exception\ServiceNotCreatedException; use Laminas\ServiceManager\Exception\ServiceNotFoundException; +use Laminas\ServiceManager\Factory\AbstractFactoryInterface; use Laminas\ServiceManager\Factory\DelegatorFactoryInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; +use Laminas\ServiceManager\Factory\InvokableFactory; +use Laminas\ServiceManager\Initializer\InitializerInterface; use Laminas\ServiceManager\Proxy\LazyServiceFactory; use Laminas\Stdlib\ArrayUtils; use ProxyManager\Configuration as ProxyConfiguration; @@ -26,6 +31,7 @@ use function array_keys; use function array_merge; use function class_exists; +use function get_debug_type; use function gettype; use function in_array; use function is_callable; @@ -50,24 +56,60 @@ * It also provides the ability to inject specific service instances and to * define aliases. * - * @see ConfigInterface + * @see ContainerInterface + * @see DelegatorFactoryInterface + * @see AbstractFactoryInterface + * @see FactoryInterface * - * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface - * @psalm-import-type AbstractFactoriesConfigurationType from ConfigInterface - * @psalm-import-type DelegatorsConfigurationType from ConfigInterface - * @psalm-import-type FactoriesConfigurationType from ConfigInterface - * @psalm-import-type InitializersConfigurationType from ConfigInterface - * @psalm-import-type LazyServicesConfigurationType from ConfigInterface - * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface - * @psalm-import-type InitializerCallableType from ConfigInterface - * @psalm-import-type DelegatorCallableType from ConfigInterface - * @psalm-import-type FactoryCallableType from ConfigInterface + * @psalm-type AbstractFactoriesConfigurationType = array< + * array-key, + * class-string|AbstractFactoryInterface + * > + * @psalm-type DelegatorCallableType = callable(ContainerInterface,string,callable():mixed,array|null):mixed + * @psalm-type DelegatorsConfigurationType = array< + * string, + * array< + * array-key, + * class-string + * |DelegatorFactoryInterface + * |DelegatorCallableType + * > + * > + * @psalm-type FactoryCallableType = callable(ContainerInterface,string,array|null):mixed + * @psalm-type FactoriesConfigurationType = array< + * string, + * class-string|FactoryInterface|FactoryCallableType + * > + * @psalm-type InitializerCallableType = callable(ContainerInterface,mixed):void + * @psalm-type InitializersConfigurationType = array< + * array-key, + * class-string|InitializerInterface|InitializerCallableType + * > + * @psalm-type LazyServicesConfigurationType = array{ + * class_map?:array, + * proxies_namespace?:non-empty-string, + * proxies_target_dir?:non-empty-string, + * write_proxy_files?:bool + * } + * @psalm-type ServiceManagerConfigurationType = array{ + * abstract_factories?: AbstractFactoriesConfigurationType, + * aliases?: array, + * delegators?: DelegatorsConfigurationType, + * factories?: FactoriesConfigurationType, + * initializers?: InitializersConfigurationType, + * invokables?: array, + * lazy_services?: LazyServicesConfigurationType, + * services?: array, + * shared?:array, + * shared_by_default?: bool, + * ... + * } * * @final Will be marked as final with v5.0.0 */ class ServiceManager implements ServiceLocatorInterface { - /** @var Factory\AbstractFactoryInterface[] */ + /** @var AbstractFactoryInterface[] */ protected $abstractFactories = []; /** @@ -145,7 +187,7 @@ class ServiceManager implements ServiceLocatorInterface /** * Cached abstract factories from string. * - * @var array,Factory\AbstractFactoryInterface> + * @var array,AbstractFactoryInterface> */ private array $cachedAbstractFactories = []; @@ -257,7 +299,12 @@ public function getAllowOverride(): bool } /** - * {@inheritDoc} + * @param ServiceManagerConfigurationType $config + * @throws ContainerModificationsNotAllowedException If the allow override flag has been toggled off, and a + * service instanceexists for a given service. + * @throws InvalidServiceException If an instance passed in the `services` configuration is invalid for the + * plugin manager. + * @throws CyclicAliasException If the configuration contains aliases targeting themselves. */ public function configure(array $config): static { @@ -363,12 +410,12 @@ public function setInvokableClass(string $name, ?string $class = null) * Specify a factory for a given service name. * * @param string $name Service name - * @param string|callable|Factory\FactoryInterface $factory Factory to which to map. - * @psalm-param class-string|FactoryCallableType|Factory\FactoryInterface $factory + * @param string|callable|FactoryInterface $factory Factory to which to map. + * @psalm-param class-string|FactoryCallableType|FactoryInterface $factory * @throws ContainerModificationsNotAllowedException If $name already * exists as a service and overrides are disallowed. */ - public function setFactory(string $name, string|callable|Factory\FactoryInterface $factory): void + public function setFactory(string $name, string|callable|FactoryInterface $factory): void { if (isset($this->services[$name]) && ! $this->allowOverride) { throw ContainerModificationsNotAllowedException::fromExistingService($name); @@ -392,9 +439,9 @@ public function mapLazyService(string $name, ?string $class = null): void /** * Add an abstract factory for resolving services. * - * @param string|Factory\AbstractFactoryInterface $factory Abstract factory + * @param string|AbstractFactoryInterface $factory Abstract factory * instance or class name. - * @psalm-param class-string|Factory\AbstractFactoryInterface $factory + * @psalm-param class-string|AbstractFactoryInterface $factory */ public function addAbstractFactory($factory) { @@ -407,11 +454,9 @@ public function addAbstractFactory($factory) * @param string $name Service name * @param string|callable|DelegatorFactoryInterface $factory Delegator * factory to assign. - * @psalm-param class-string - * |DelegatorCallableType - * |Factory\DelegatorFactoryInterface $factory + * @psalm-param class-string|DelegatorCallableType|DelegatorFactoryInterface $factory */ - public function addDelegator(string $name, string|callable|Factory\DelegatorFactoryInterface $factory): void + public function addDelegator(string $name, string|callable|DelegatorFactoryInterface $factory): void { $this->configure(['delegators' => [$name => [$factory]]]); } @@ -419,11 +464,11 @@ public function addDelegator(string $name, string|callable|Factory\DelegatorFact /** * Add an initializer. * - * @psalm-param class-string + * @psalm-param class-string * |InitializerCallableType - * |Initializer\InitializerInterface $initializer + * |InitializerInterface $initializer */ - public function addInitializer(string|callable|Initializer\InitializerInterface $initializer) + public function addInitializer(string|callable|InitializerInterface $initializer) { $this->configure(['initializers' => [$initializer]]); } @@ -488,11 +533,11 @@ private function resolveInitializers(array $initializers): void /** * Get a factory for the given service name * - * @return FactoryCallableType|Factory\FactoryInterface + * @return FactoryCallableType|FactoryInterface * @throws ServiceNotFoundException In case that the service creation strategy based on factories * did not find any capable factory. */ - private function getFactory(string $name): callable + private function getFactory(string $name): callable|FactoryInterface { $factory = $this->factories[$name] ?? null; @@ -672,7 +717,7 @@ private function createAliasesAndFactoriesForInvokables(array $invokables): arra $newAliases = []; foreach ($invokables as $name => $class) { - $this->factories[$class] = Factory\InvokableFactory::class; + $this->factories[$class] = InvokableFactory::class; if ($name !== $class) { $this->aliases[$name] = $class; $newAliases[$name] = $class; @@ -847,9 +892,9 @@ private function mapAliasesToTargets(): void /** * Instantiate abstract factories in order to avoid checks during service construction. * - * @param class-string|Factory\AbstractFactoryInterface $abstractFactory + * @param class-string|AbstractFactoryInterface $abstractFactory */ - private function resolveAbstractFactoryInstance(string|Factory\AbstractFactoryInterface $abstractFactory): void + private function resolveAbstractFactoryInstance(string|AbstractFactoryInterface $abstractFactory): void { if (is_string($abstractFactory)) { // Cached string factory name @@ -901,47 +946,23 @@ private function abstractFactoryCanCreate(string $name): bool } /** - * @psalm-assert Factory\DelegatorFactoryInterface|DelegatorCallableType $delegatorFactory + * @param class-string|DelegatorCallableType|DelegatorFactoryInterface $delegatorFactory + * @return DelegatorCallableType */ - private function assertCallableDelegatorFactory(mixed $delegatorFactory): void - { - if ( - $delegatorFactory instanceof Factory\DelegatorFactoryInterface - || is_callable($delegatorFactory) - ) { - return; - } - if (is_string($delegatorFactory)) { - throw new ServiceNotCreatedException(sprintf( - 'An invalid delegator factory was registered; resolved to class or function "%s"' - . ' which does not exist; please provide a valid function name or class name resolving' - . ' to an implementation of %s', - $delegatorFactory, - DelegatorFactoryInterface::class - )); - } - throw new ServiceNotCreatedException(sprintf( - 'A non-callable delegator, "%s", was provided; expected a callable or instance of "%s"', - is_object($delegatorFactory) ? $delegatorFactory::class : gettype($delegatorFactory), - DelegatorFactoryInterface::class - )); - } - - /** - * @return DelegatorFactoryInterface|DelegatorCallableType - */ - private function resolveDelegatorFactory(mixed $delegatorFactory): callable|DelegatorFactoryInterface + private function resolveDelegatorFactory(DelegatorFactoryInterface|string|callable $delegatorFactory): callable { if ($delegatorFactory === LazyServiceFactory::class) { return $this->createLazyServiceDelegatorFactory(); } - if (is_string($delegatorFactory) && class_exists($delegatorFactory)) { - /** @psalm-suppress MixedMethodCall We do assert that a delegator can be instantiated */ + if (is_callable($delegatorFactory)) { + return $delegatorFactory; + } + + if (is_string($delegatorFactory)) { $delegatorFactory = new $delegatorFactory(); } - $this->assertCallableDelegatorFactory($delegatorFactory); return $delegatorFactory; } } diff --git a/test/CommonServiceLocatorBehaviorsTrait.php b/test/CommonServiceLocatorBehaviorsTrait.php index c8b613be..3cf05b3b 100644 --- a/test/CommonServiceLocatorBehaviorsTrait.php +++ b/test/CommonServiceLocatorBehaviorsTrait.php @@ -6,7 +6,6 @@ use DateTime; use Laminas\ServiceManager\AbstractPluginManager; -use Laminas\ServiceManager\ConfigInterface; use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException; use Laminas\ServiceManager\Exception\CyclicAliasException; use Laminas\ServiceManager\Exception\InvalidArgumentException; @@ -37,10 +36,9 @@ use function in_array; /** - * @see ConfigInterface * @see TestCase * - * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface + * @psalm-import-type ServiceManagerConfigurationType from ServiceManager * @psalm-require-extends TestCase */ trait CommonServiceLocatorBehaviorsTrait diff --git a/test/ConfigTest.php b/test/ConfigTest.php deleted file mode 100644 index 77e3c069..00000000 --- a/test/ConfigTest.php +++ /dev/null @@ -1,92 +0,0 @@ - [ - AbstractFactoryFoo::class, - ], - 'aliases' => [ - 'foo' => self::class, - 'bar' => __NAMESPACE__, - ], - 'delegators' => [ - 'foo' => [ - Delegator1Factory::class, - ], - ], - 'factories' => [ - 'bar' => AbstractFactoryFoo::class, - ], - 'initializers' => [ - SimpleInitializer::class, - ], - 'invokables' => [ - 'foo' => InvokableObject::class, - ], - 'lazy_services' => [ - 'class_map' => [ - self::class => self::class, - ], - ], - 'services' => [ - 'foo' => $this, - ], - 'shared' => [ - self::class => true, - __NAMESPACE__ => false, - ], - ]; - - $config = $expected; - - $services = $this->createMock(ServiceLocatorInterface::class); - $services - ->expects(self::once()) - ->method('configure') - ->with($expected) - ->willReturnSelf(); - - $configuration = new Config($config); - self::assertEquals($services, $configuration->configureServiceManager($services)); - - return [ - 'array' => $expected, - 'config' => $configuration, - ]; - } - - /** - * @param array{"array":ServiceManagerConfigurationType,config:ConfigInterface} $dependencies - * @depends testPassesKnownServiceConfigKeysToServiceManagerWithConfigMethod - */ - public function testToArrayReturnsConfiguration(array $dependencies): void - { - $configuration = $dependencies['array']; - $configInstance = $dependencies['config']; - - self::assertSame($configuration, $configInstance->toArray()); - } -} diff --git a/test/ExamplePluginManagerTest.php b/test/ExamplePluginManagerTest.php index 7c8b9c62..388f1c1d 100644 --- a/test/ExamplePluginManagerTest.php +++ b/test/ExamplePluginManagerTest.php @@ -5,7 +5,6 @@ namespace LaminasTest\ServiceManager; use Laminas\ServiceManager\AbstractPluginManager; -use Laminas\ServiceManager\ConfigInterface; use Laminas\ServiceManager\ServiceManager; use Laminas\ServiceManager\Test\CommonPluginManagerTrait; use LaminasTest\ServiceManager\TestAsset\InvokableObject; @@ -16,7 +15,7 @@ /** * Example test of using CommonPluginManagerTrait * - * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface + * @psalm-import-type ServiceManagerConfigurationType from ServiceManager */ final class ExamplePluginManagerTest extends TestCase { diff --git a/test/ServiceManagerTest.php b/test/ServiceManagerTest.php index b644e388..18aea5a6 100644 --- a/test/ServiceManagerTest.php +++ b/test/ServiceManagerTest.php @@ -5,7 +5,6 @@ namespace LaminasTest\ServiceManager; use DateTime; -use Laminas\ServiceManager\ConfigInterface; use Laminas\ServiceManager\Factory\AbstractFactoryInterface; use Laminas\ServiceManager\Factory\FactoryInterface; use Laminas\ServiceManager\Factory\InvokableFactory; @@ -19,10 +18,8 @@ use stdClass; /** - * @see ConfigInterface - * * @covers \Laminas\ServiceManager\ServiceManager - * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface + * @psalm-import-type ServiceManagerConfigurationType from ServiceManager */ final class ServiceManagerTest extends TestCase { From 4845306379edb80b459a2d52511a311b67ff764d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 20 Apr 2023 02:30:43 +0200 Subject: [PATCH 11/17] qa: optimize a whole bunch of type-inference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also removes plenty of invalid unit tests which verified stuff which should not work rather than verifying stuff what would work. Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- composer.json | 11 +- composer.lock | 434 ++++++++---------- psalm-baseline.xml | 382 ++++----------- src/AbstractPluginManager.php | 46 +- src/AbstractSingleInstancePluginManager.php | 2 +- .../AheadOfTimeFactoryCreatorCommand.php | 6 +- src/ConfigProvider.php | 6 +- src/PluginManagerInterface.php | 10 +- src/ServiceManager.php | 106 +++-- test/CommonServiceLocatorBehaviorsTrait.php | 48 +- test/ExamplePluginManagerTest.php | 4 +- test/ServiceManagerTest.php | 12 +- 12 files changed, 398 insertions(+), 669 deletions(-) diff --git a/composer.json b/composer.json index adfea6dc..d6176ffa 100644 --- a/composer.json +++ b/composer.json @@ -47,8 +47,7 @@ "composer/package-versions-deprecated": "^1.11.99.5", "laminas/laminas-cli": "^1.8", "laminas/laminas-coding-standard": "~2.5.0", - "laminas/laminas-container-config-test": "^0.8", - "laminas/laminas-dependency-plugin": "^2.2", + "laminas/laminas-container-config-test": "dev-qa/service-manager-v4", "lctrs/psalm-psr-container-plugin": "^1.9", "mikey179/vfsstream": "^1.6.11@alpha", "friendsofphp/proxy-manager-lts": "^1", @@ -56,7 +55,7 @@ "phpunit/phpunit": "^9.5.26", "psalm/plugin-phpunit": "^0.18.0", "symfony/console": "^6.0", - "vimeo/psalm": "^5.0.0" + "vimeo/psalm": "5.x-dev" }, "provide": { "psr/container-implementation": "^1.0" @@ -98,5 +97,11 @@ "test": "phpunit --colors=always", "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", "static-analysis" : "psalm --shepherd --stats" + }, + "repositories": { + "container-integration-tests": { + "type": "git", + "url": "https://github.com/boesing/laminas-container-config-test" + } } } diff --git a/composer.lock b/composer.lock index 0f5bae1b..5f465bd7 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": "7e9ce5c985e93c6f56753b158acc8b22", + "content-hash": "319541139bcb7286a5afa215143af372", "packages": [ { "name": "brick/varexporter", @@ -116,16 +116,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v4.15.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", + "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", "shasum": "" }, "require": { @@ -166,9 +166,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2022-11-12T15:38:23+00:00" }, { "name": "psr/container", @@ -925,32 +925,36 @@ }, { "name": "doctrine/instantiator", - "version": "2.0.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + "reference": "f350df0268e904597e3bd9c4685c53e0e333feea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea", + "reference": "f350df0268e904597e3bd9c4685c53e0e333feea", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11", + "doctrine/coding-standard": "^6.0", "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" + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" @@ -964,7 +968,7 @@ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" + "homepage": "http://ocramius.github.com/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", @@ -975,7 +979,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + "source": "https://github.com/doctrine/instantiator/tree/1.3.x" }, "funding": [ { @@ -991,7 +995,7 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:23:10+00:00" + "time": "2020-05-29T17:27:14+00:00" }, { "name": "doctrine/lexer", @@ -1503,17 +1507,11 @@ }, { "name": "laminas/laminas-container-config-test", - "version": "0.8.0", + "version": "dev-qa/service-manager-v4", "source": { "type": "git", - "url": "https://github.com/laminas/laminas-container-config-test.git", - "reference": "06474faed18a2732b21355297fa8c56f1aff2e91" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-container-config-test/zipball/06474faed18a2732b21355297fa8c56f1aff2e91", - "reference": "06474faed18a2732b21355297fa8c56f1aff2e91", - "shasum": "" + "url": "https://github.com/boesing/laminas-container-config-test", + "reference": "249c28ba37e05857ffab25a64ae6c9824626cf23" }, "require": { "php": "~8.0.0 || ~8.1.0 || ~8.2.0", @@ -1526,7 +1524,7 @@ "laminas/laminas-coding-standard": "^2.3", "phpunit/phpunit": "^9.5.26", "psalm/plugin-phpunit": "^0.18.0", - "vimeo/psalm": "^4.29.0" + "vimeo/psalm": "^5.0.0" }, "type": "library", "autoload": { @@ -1540,86 +1538,52 @@ "Laminas\\ContainerConfigTest\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "autoload-dev": { + "psr-4": { + "LaminasTest\\ContainerConfigTest\\": "test/" + } + }, + "scripts": { + "check": [ + "@cs-check", + "@test" + ], + "cs-check": [ + "phpcs" + ], + "cs-fix": [ + "phpcbf" + ], + "static-analysis": [ + "psalm --shepherd --stats" + ], + "test": [ + "phpunit --colors=always" + ], + "test-coverage": [ + "phpunit --colors=always --coverage-clover clover.xml" + ] + }, "license": [ "BSD-3-Clause" ], "description": "Mezzio PSR-11 container configuration tests", "homepage": "https://laminas.dev", "keywords": [ - "PSR-11", "container", "laminas", "mezzio", + "psr-11", "test" ], "support": { - "chat": "https://laminas.dev/chat", - "forum": "https://discourse.laminas.dev", "issues": "https://github.com/laminas/laminas-container-config-test/issues", + "source": "https://github.com/laminas/laminas-container-config-test", "rss": "https://github.com/laminas/laminas-container-config-test/releases.atom", - "source": "https://github.com/laminas/laminas-container-config-test" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2022-11-16T00:42:21+00:00" - }, - { - "name": "laminas/laminas-dependency-plugin", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-dependency-plugin.git", - "reference": "73cfb63ddca9d6bfedad5e0a038f6d55063975a3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-dependency-plugin/zipball/73cfb63ddca9d6bfedad5e0a038f6d55063975a3", - "reference": "73cfb63ddca9d6bfedad5e0a038f6d55063975a3", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1 || ^2.0", - "php": "^7.3 || ~8.0.0 || ~8.1.0" - }, - "require-dev": { - "composer/composer": "^1.9 || ^2.0", - "laminas/laminas-coding-standard": "^2.2.1", - "mikey179/vfsstream": "^1.6.10@alpha", - "phpunit/phpunit": "^9.5.5", - "psalm/plugin-phpunit": "^0.15.1", - "roave/security-advisories": "dev-master", - "vimeo/psalm": "^4.5" - }, - "type": "composer-plugin", - "extra": { - "class": "Laminas\\DependencyPlugin\\DependencyRewriterPluginDelegator" - }, - "autoload": { - "psr-4": { - "Laminas\\DependencyPlugin\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Replace zendframework and zfcampus packages with their Laminas Project equivalents.", - "support": { - "issues": "https://github.com/laminas/laminas-dependency-plugin/issues", - "source": "https://github.com/laminas/laminas-dependency-plugin/tree/2.2.0" + "chat": "https://laminas.dev/chat", + "forum": "https://discourse.laminas.dev" }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2021-09-08T17:51:35+00:00" + "time": "2023-04-19T23:38:19+00:00" }, { "name": "lctrs/psalm-psr-container-plugin", @@ -1752,29 +1716,25 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, "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" + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" }, "type": "library", "autoload": { @@ -1799,7 +1759,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.x" }, "funding": [ { @@ -1807,7 +1767,7 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2020-06-29T13:22:24+00:00" }, { "name": "netresearch/jsonmapper", @@ -1922,16 +1882,16 @@ }, { "name": "phar-io/version", - "version": "3.2.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "url": "https://api.github.com/repos/phar-io/version/zipball/c6bb6825def89e0a32220f88337f8ceaf1975fa0", + "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0", "shasum": "" }, "require": { @@ -1967,9 +1927,9 @@ "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" + "source": "https://github.com/phar-io/version/tree/master" }, - "time": "2022-02-21T01:04:05+00:00" + "time": "2020-06-27T14:39:04+00:00" }, { "name": "phpbench/container", @@ -2374,23 +2334,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.26", + "version": "9.2.13", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" + "reference": "deac8540cb7bd40b2b8cfa679b76202834fd04e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/deac8540cb7bd40b2b8cfa679b76202834fd04e8", + "reference": "deac8540cb7bd40b2b8cfa679b76202834fd04e8", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^4.13.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -2405,8 +2365,8 @@ "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" + "ext-pcov": "*", + "ext-xdebug": "*" }, "type": "library", "extra": { @@ -2439,7 +2399,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.13" }, "funding": [ { @@ -2447,20 +2407,20 @@ "type": "github" } ], - "time": "2023-03-06T12:58:08+00:00" + "time": "2022-02-23T17:02:38+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", "shasum": "" }, "require": { @@ -2499,7 +2459,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" }, "funding": [ { @@ -2507,7 +2467,7 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2020-09-28T05:57:25+00:00" }, { "name": "phpunit/php-invoker", @@ -2574,16 +2534,16 @@ }, { "name": "phpunit/php-text-template", - "version": "2.0.4", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "reference": "18c887016e60e52477e54534956d7b47bc52cd84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/18c887016e60e52477e54534956d7b47bc52cd84", + "reference": "18c887016e60e52477e54534956d7b47bc52cd84", "shasum": "" }, "require": { @@ -2621,7 +2581,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.3" }, "funding": [ { @@ -2629,20 +2589,20 @@ "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2020-09-28T06:03:05+00:00" }, { "name": "phpunit/php-timer", - "version": "5.0.3", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "reference": "c9ff14f493699e2f6adee9fd06a0245b276643b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/c9ff14f493699e2f6adee9fd06a0245b276643b7", + "reference": "c9ff14f493699e2f6adee9fd06a0245b276643b7", "shasum": "" }, "require": { @@ -2680,7 +2640,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.2" }, "funding": [ { @@ -2688,24 +2648,24 @@ "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2020-09-28T06:00:25+00:00" }, { "name": "phpunit/phpunit", - "version": "9.6.7", + "version": "9.5.26", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2" + "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2", + "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", + "doctrine/instantiator": "^1.3.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -2734,8 +2694,8 @@ "sebastian/version": "^3.0.2" }, "suggest": { - "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" + "ext-soap": "*", + "ext-xdebug": "*" }, "bin": [ "phpunit" @@ -2743,7 +2703,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.6-dev" + "dev-master": "9.5-dev" } }, "autoload": { @@ -2774,8 +2734,7 @@ ], "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.7" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26" }, "funding": [ { @@ -2791,7 +2750,7 @@ "type": "tidelift" } ], - "time": "2023-04-14T08:58:40+00:00" + "time": "2022-10-28T06:00:21+00:00" }, { "name": "psalm/plugin-phpunit", @@ -3060,16 +3019,16 @@ }, { "name": "sebastian/code-unit", - "version": "1.0.8", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "reference": "d3a241b6028ff9d8e97d2b6ebd4090d01f92fad8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/d3a241b6028ff9d8e97d2b6ebd4090d01f92fad8", + "reference": "d3a241b6028ff9d8e97d2b6ebd4090d01f92fad8", "shasum": "" }, "require": { @@ -3104,7 +3063,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.6" }, "funding": [ { @@ -3112,27 +3071,27 @@ "type": "github" } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2020-09-28T05:28:46+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "reference": "ee51f9bb0c6d8a43337055db3120829fa14da819" }, "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/code-unit-reverse-lookup/zipball/ee51f9bb0c6d8a43337055db3120829fa14da819", + "reference": "ee51f9bb0c6d8a43337055db3120829fa14da819", "shasum": "" }, "require": { - "php": ">=7.3" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { @@ -3159,7 +3118,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/master" }, "funding": [ { @@ -3167,7 +3126,7 @@ "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2020-06-26T12:04:00+00:00" }, { "name": "sebastian/comparator", @@ -3245,24 +3204,24 @@ }, { "name": "sebastian/complexity", - "version": "2.0.2", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "reference": "33fcd6a26656c6546f70871244ecba4b4dced097" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/33fcd6a26656c6546f70871244ecba4b4dced097", + "reference": "33fcd6a26656c6546f70871244ecba4b4dced097", "shasum": "" }, "require": { "nikic/php-parser": "^4.7", - "php": ">=7.3" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.2" }, "type": "library", "extra": { @@ -3290,7 +3249,7 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.0" }, "funding": [ { @@ -3298,20 +3257,20 @@ "type": "github" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2020-07-25T14:01:34+00:00" }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "ffc949a1a2aae270ea064453d7535b82e4c32092" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ffc949a1a2aae270ea064453d7535b82e4c32092", + "reference": "ffc949a1a2aae270ea064453d7535b82e4c32092", "shasum": "" }, "require": { @@ -3356,7 +3315,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.3" }, "funding": [ { @@ -3364,20 +3323,20 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2020-09-28T05:32:55+00:00" }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", "shasum": "" }, "require": { @@ -3419,7 +3378,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" }, "funding": [ { @@ -3427,7 +3386,7 @@ "type": "github" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2020-09-28T05:52:38+00:00" }, { "name": "sebastian/exporter", @@ -3508,16 +3467,16 @@ }, { "name": "sebastian/global-state", - "version": "5.0.5", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + "reference": "ea779cb749a478b22a2564ac41cd7bda79c78dc7" }, "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/ea779cb749a478b22a2564ac41cd7bda79c78dc7", + "reference": "ea779cb749a478b22a2564ac41cd7bda79c78dc7", "shasum": "" }, "require": { @@ -3560,7 +3519,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.1" }, "funding": [ { @@ -3568,7 +3527,7 @@ "type": "github" } ], - "time": "2022-02-14T08:28:10+00:00" + "time": "2020-09-28T05:54:06+00:00" }, { "name": "sebastian/lines-of-code", @@ -3629,16 +3588,16 @@ }, { "name": "sebastian/object-enumerator", - "version": "4.0.4", + "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "reference": "f6f5957013d84725427d361507e13513702888a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f6f5957013d84725427d361507e13513702888a4", + "reference": "f6f5957013d84725427d361507e13513702888a4", "shasum": "" }, "require": { @@ -3674,7 +3633,7 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.3" }, "funding": [ { @@ -3682,27 +3641,27 @@ "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2020-09-28T05:55:06+00:00" }, { "name": "sebastian/object-reflector", - "version": "2.0.4", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "reference": "127a46f6b057441b201253526f81d5406d6c7840" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/127a46f6b057441b201253526f81d5406d6c7840", + "reference": "127a46f6b057441b201253526f81d5406d6c7840", "shasum": "" }, "require": { - "php": ">=7.3" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { @@ -3729,7 +3688,7 @@ "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" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/master" }, "funding": [ { @@ -3737,27 +3696,27 @@ "type": "github" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2020-06-26T12:12:55+00:00" }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "062231bf61d2b9448c4fa5a7643b5e1829c11d63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/062231bf61d2b9448c4fa5a7643b5e1829c11d63", + "reference": "062231bf61d2b9448c4fa5a7643b5e1829c11d63", "shasum": "" }, "require": { - "php": ">=7.3" + "php": "^7.3 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { @@ -3789,10 +3748,10 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/master" }, "funding": [ { @@ -3800,7 +3759,7 @@ "type": "github" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2020-06-26T12:14:17+00:00" }, { "name": "sebastian/resource-operations", @@ -3859,16 +3818,16 @@ }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", "shasum": "" }, "require": { @@ -3903,7 +3862,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" }, "funding": [ { @@ -3911,7 +3870,7 @@ "type": "github" } ], - "time": "2023-02-03T06:13:03+00:00" + "time": "2022-09-12T14:47:03+00:00" }, { "name": "sebastian/version", @@ -5375,16 +5334,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "75a63c33a8577608444246075ea0af0d052e452a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", + "reference": "75a63c33a8577608444246075ea0af0d052e452a", "shasum": "" }, "require": { @@ -5413,7 +5372,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/master" }, "funding": [ { @@ -5421,20 +5380,20 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2020-07-12T23:59:07+00:00" }, { "name": "vimeo/psalm", - "version": "5.9.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "8b9ad1eb9e8b7d3101f949291da2b9f7767cd163" + "reference": "5efddb42013538f9e4a757dc2a83b0acc41c1a2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/8b9ad1eb9e8b7d3101f949291da2b9f7767cd163", - "reference": "8b9ad1eb9e8b7d3101f949291da2b9f7767cd163", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/5efddb42013538f9e4a757dc2a83b0acc41c1a2e", + "reference": "5efddb42013538f9e4a757dc2a83b0acc41c1a2e", "shasum": "" }, "require": { @@ -5485,6 +5444,7 @@ "ext-curl": "In order to send data to shepherd", "ext-igbinary": "^2.0.5 is required, used to serialize caching data" }, + "default-branch": true, "bin": [ "psalm", "psalm-language-server", @@ -5525,9 +5485,9 @@ ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/5.9.0" + "source": "https://github.com/vimeo/psalm/tree/master" }, - "time": "2023-03-29T21:38:21+00:00" + "time": "2023-04-19T19:12:29+00:00" }, { "name": "webimpress/coding-standard", @@ -5695,10 +5655,12 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "mikey179/vfsstream": 15 + "laminas/laminas-container-config-test": 20, + "mikey179/vfsstream": 15, + "vimeo/psalm": 20 }, "prefer-stable": false, - "prefer-lowest": false, + "prefer-lowest": true, "platform": { "php": "~8.1.0 || ~8.2.0" }, diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 9ba175de..b24ad71b 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,309 +1,166 @@ - + - + new $requestedName(...$arguments) - + $serviceDependencies $serviceDependencies - + $config[self::class] - + $config $dependencies - + $requestedName - + new $requestedName(...$parameters) - - - $config - $config - - - $service - - - setService - - - $service - - - $name - - - is_object($configInstanceOrParentLocator) - - - MixedAssignment - - - - - ArrayUtils::merge($a, $b) - - - $this->allowedKeys[$key] - - - ServiceManagerConfigurationType - - - MixedReturnTypeCoercion - MixedReturnTypeCoercion - - - + self::deDuplicateDetectedCycles($detectedCycles) - + $alias $detectedCycles - + $cycle - + new $requestedName($options) new $requestedName() - + $wrappedInstance - + $wrappedInstance - - ['initializers' => [$initializer]] - ['lazy_services' => ['class_map' => [$name => $class ?: $name]]] - - - $this->services[$service] - $this->services[$service] - $this->services[$service] - $this->services[$service] - $this->services[$service] - $this->services[$service] + + services[$service]]]> - - $service - $service - $service - $service - $service - $service - - - $this->factories - - + function () use ($name, $options) { - + addAbstractFactory - addDelegator addInitializer - mapLazyService - setAlias - setAllowOverride setInvokableClass - setService - setShared - + $abstractFactory - $config['aliases'] - $config['delegators'] - $config['delegators'] - $config['factories'] - $config['initializers'] - $config['invokables'] - $config['invokables'] - $config['lazy_services'] - $config['lazy_services']['class_map'] - $config['shared'] + + + + + + + + + + - + $alias $alias $service - $service - $service - $service - $service - $service - - $this->services[$service] - $this->services[$service] - $this->services[$service] - $this->services[$service] - $this->services[$service] - $this->services[$service] + + services[$service]]]> - + $abstractFactories $abstractFactory - $abstractFactory - $config['aliases'] + $key - $this->aliases - $this->factories - $this->shared - $this->sharedByDefault + aliases]]> + factories]]> + shared]]> + sharedByDefault]]> - - $config['aliases'] - $config['aliases'] ?? [] - $config['factories'] - $config['shared'] + + + + + - + $name $name - - (bool) $flag - (bool) $flag - - - $sharedAlias - $sharedAlias - - - $delegatorFactory - $service - $target - - - $key - - + $expected - $this->getServiceNotFoundException() - $this->getServiceNotFoundException() + getServiceNotFoundException()]]> + getServiceNotFoundException()]]> - + testInstanceOfMatches testLoadingInvalidElementRaisesException testPluginAliasesResolve testRegisteringInvalidElementRaisesException - testShareByDefaultAndSharedByDefault - + $alias $expected - $shareByDefault - $sharedByDefault - + array - - - enum_exists($service) - - - - $config['service_manager'] - $config['service_manager']['factories'] + + + $key - + $className - - $config['service_manager']['factories'] - $config['service_manager']['factories'][$className] + + + $config[ConfigAbstractFactory::class][$className] $config[ConfigAbstractFactory::class][$className] - + $dependency $key $value - + $key - - $dependency - - - - - 'Holistic' - - - setServiceLocator - - - - $callback - $container - $name - $plugin - - - static function ($container, $name, $callback) { - - - $arg - - + $instance - - $callback() - - - array + array - + $instance - - $container - $errstr - $errstr - $errstr - $errstr - $name - - - $object['get'][0] - - - - $config['shared'] - - + $callback $className $container @@ -313,112 +170,58 @@ $name $requestedName - + static function ($container, $name, $callback) { - + $creationContext $creationContext - - $object[$shared ? $method : 'build'][] - - - $names[$name] - $object[$shared ? $method : 'build'] - - + $first - $idx1 - $idx2 $instance - $method - $name - $names[$name] $nonSharedObj1 $nonSharedObj2 $obj - $object[$shared ? $method : 'build'][] $second - $shared - $shared - + $callback() - - array - array - array - - + $instance - - $object - - - has - - - $container - $container - $container - $container - $container - $name - $options - $requestedName - $serviceLocator - - - - - $configInstance - $configuration - - - toArray - - + array_filter(spl_autoload_functions(), $filter) - + AutoloaderInterface[] - + $autoload - + assertIsArray assertIsArray - - [$callback, 'callback'] - [$callback, 'callback'] + + + - + $className $initializer - + $initializer($wrappedInstance, $proxy) - - $wrappedInstance - - - $context - $context - $name - - + $inc $inc $instance1 @@ -429,41 +232,18 @@ $serviceFromAlias $serviceFromServiceNameAfterUsingAlias - + array - + $inc - - $container - $container - $context - $context - $name - $name - - - - - - array<non-empty-string,array{string}> - - - WhateverEnum - - - - - WhateverEnum - case - - + $test - + include $file diff --git a/src/AbstractPluginManager.php b/src/AbstractPluginManager.php index 6eee59b4..652327c0 100644 --- a/src/AbstractPluginManager.php +++ b/src/AbstractPluginManager.php @@ -9,6 +9,7 @@ use Laminas\ServiceManager\Exception\InvalidServiceException; use Laminas\ServiceManager\Factory\AbstractFactoryInterface; use Laminas\ServiceManager\Factory\DelegatorFactoryInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; use Laminas\ServiceManager\Initializer\InitializerInterface; use Laminas\Stdlib\ArrayUtils; use Psr\Container\ContainerInterface; @@ -24,15 +25,15 @@ * * @template InstanceType * @template-implements PluginManagerInterface - * @psalm-import-type ServiceManagerConfigurationType from ServiceManager - * @psalm-import-type FactoryCallableType from ServiceManager - * @psalm-import-type DelegatorCallableType from ServiceManager - * @psalm-import-type InitializerCallableType from ServiceManager - * @psalm-import-type AbstractFactoriesConfigurationType from ServiceManager - * @psalm-import-type DelegatorsConfigurationType from ServiceManager - * @psalm-import-type FactoriesConfigurationType from ServiceManager - * @psalm-import-type InitializersConfigurationType from ServiceManager - * @psalm-import-type LazyServicesConfigurationType from ServiceManager + * @psalm-import-type ServiceManagerConfiguration from ServiceManager + * @psalm-import-type FactoryCallable from ServiceManager + * @psalm-import-type DelegatorCallable from ServiceManager + * @psalm-import-type InitializerCallable from ServiceManager + * @psalm-import-type AbstractFactoriesConfiguration from ServiceManager + * @psalm-import-type DelegatorsConfiguration from ServiceManager + * @psalm-import-type FactoriesConfiguration from ServiceManager + * @psalm-import-type InitializersConfiguration from ServiceManager + * @psalm-import-type LazyServicesConfiguration from ServiceManager */ abstract class AbstractPluginManager implements PluginManagerInterface { @@ -64,7 +65,7 @@ abstract class AbstractPluginManager implements PluginManagerInterface /** * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. * - * @var DelegatorsConfigurationType + * @var DelegatorsConfiguration */ protected array $delegators = []; @@ -73,21 +74,21 @@ abstract class AbstractPluginManager implements PluginManagerInterface * * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. * - * @var FactoriesConfigurationType + * @var FactoriesConfiguration */ protected array $factories = []; /** * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. * - * @var InitializersConfigurationType + * @var InitializersConfiguration */ protected array $initializers = []; /** * @deprecated Please pass the plugin manager configuration via {@see AbstractPluginManager::__construct} instead. * - * @var LazyServicesConfigurationType + * @var LazyServicesConfiguration */ protected array $lazyServices = []; @@ -119,7 +120,7 @@ abstract class AbstractPluginManager implements PluginManagerInterface private ServiceManager $plugins; /** - * @param ServiceManagerConfigurationType $config + * @param ServiceManagerConfiguration $config */ public function __construct( ContainerInterface $creationContext, @@ -129,7 +130,7 @@ public function __construct( 'shared_by_default' => $this->sharedByDefault, ], $creationContext); - /** @var ServiceManagerConfigurationType $config */ + /** @var ServiceManagerConfiguration $config */ $config = ArrayUtils::merge([ 'factories' => $this->factories, 'abstract_factories' => $this->abstractFactories, @@ -145,7 +146,7 @@ public function __construct( } /** - * @param ServiceManagerConfigurationType $config + * @param ServiceManagerConfiguration $config * @throws ContainerModificationsNotAllowedException If the allow override flag has been toggled off, and a * service instanceexists for a given service. * @throws InvalidServiceException If an instance passed in the `services` configuration is invalid for the @@ -155,13 +156,12 @@ public function __construct( public function configure(array $config): static { if (isset($config['services'])) { - /** @psalm-suppress MixedAssignment */ foreach ($config['services'] as $service) { $this->validate($service); } } - /** @psalm-var ServiceManagerConfigurationType $config */ + /** @var ServiceManagerConfiguration $config */ $this->plugins->configure($config); return $this; @@ -215,7 +215,6 @@ public function has(string $id): bool */ public function build(string $name, ?array $options = null): mixed { - /** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */ $plugin = $this->plugins->build($name, $options); $this->validate($plugin); @@ -255,7 +254,7 @@ public function setInvokableClass(string $name, string|null $class = null): void * * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * - * @param class-string|FactoryCallableType|Factory\FactoryInterface $factory + * @param class-string|class-string|FactoryCallable|FactoryInterface $factory * @throws ContainerModificationsNotAllowedException If $name already * exists as a service and overrides are disallowed. */ @@ -269,7 +268,8 @@ public function setFactory(string $name, string|callable|Factory\FactoryInterfac * * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * - * @param null|string $class Class to which to map; if not provided, $name + * @param string|class-string $name Service name to map + * @param null|class-string $class Class to which to map; if not provided, $name * will be used for the mapping. */ public function mapLazyService(string $name, string|null $class = null): void @@ -299,7 +299,7 @@ public function addAbstractFactory(string|AbstractFactoryInterface $factory): vo * @param string $name Service name * @param string|callable|DelegatorFactoryInterface $factory Delegator * factory to assign. - * @psalm-param class-string|DelegatorCallableType $factory + * @psalm-param class-string|class-string|DelegatorCallable $factory */ public function addDelegator(string $name, string|callable|DelegatorFactoryInterface $factory): void { @@ -311,7 +311,7 @@ public function addDelegator(string $name, string|callable|DelegatorFactoryInter * * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * - * @psalm-param class-string|InitializerCallableType|InitializerInterface $initializer + * @psalm-param class-string|InitializerCallable|InitializerInterface $initializer */ public function addInitializer(string|callable|InitializerInterface $initializer): void { diff --git a/src/AbstractSingleInstancePluginManager.php b/src/AbstractSingleInstancePluginManager.php index e97b218d..e56ce5eb 100644 --- a/src/AbstractSingleInstancePluginManager.php +++ b/src/AbstractSingleInstancePluginManager.php @@ -14,7 +14,7 @@ * Implementations define the `$instanceOf` property to indicate what class types constitute valid plugins, omitting the * requirement to define the `validate()` method. * - * @template InstanceType + * @template InstanceType of object * @template-extends AbstractPluginManager */ abstract class AbstractSingleInstancePluginManager extends AbstractPluginManager diff --git a/src/Command/AheadOfTimeFactoryCreatorCommand.php b/src/Command/AheadOfTimeFactoryCreatorCommand.php index c054c309..a64b1358 100644 --- a/src/Command/AheadOfTimeFactoryCreatorCommand.php +++ b/src/Command/AheadOfTimeFactoryCreatorCommand.php @@ -5,10 +5,10 @@ namespace Laminas\ServiceManager\Command; use Brick\VarExporter\VarExporter; -use Laminas\ServiceManager\ConfigInterface; use Laminas\ServiceManager\ConfigProvider; use Laminas\ServiceManager\Exception\RuntimeException; use Laminas\ServiceManager\Factory\FactoryInterface; +use Laminas\ServiceManager\ServiceManager; use Laminas\ServiceManager\Tool\AheadOfTimeFactoryCompiler\AheadOfTimeFactoryCompilerInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -31,7 +31,7 @@ /** * @internal CLI commands are not meant to be used in any upstream projects other than via `laminas-cli`. * - * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface + * @psalm-import-type ServiceManagerConfiguration from ServiceManager */ final class AheadOfTimeFactoryCreatorCommand extends Command { @@ -146,7 +146,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } /** - * @param non-empty-array $containerConfigurations + * @param non-empty-array $containerConfigurations * @return non-empty-string */ private function createLocalAotContainerConfigContent(array $containerConfigurations): string diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 24a51c18..e15beddd 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -22,7 +22,7 @@ use function class_exists; /** - * @psalm-import-type ServiceManagerConfigurationType from ServiceManager + * @psalm-import-type ServiceManagerConfiguration from ServiceManager */ final class ConfigProvider { @@ -30,7 +30,7 @@ final class ConfigProvider /** * @return array{ - * dependencies: ServiceManagerConfigurationType, + * dependencies: ServiceManagerConfiguration, * ... * } */ @@ -43,7 +43,7 @@ public function __invoke(): array } /** - * @return ServiceManagerConfigurationType + * @return ServiceManagerConfiguration */ public function getServiceDependencies(): array { diff --git a/src/PluginManagerInterface.php b/src/PluginManagerInterface.php index 5a32350f..a0c0a1b1 100644 --- a/src/PluginManagerInterface.php +++ b/src/PluginManagerInterface.php @@ -29,8 +29,9 @@ public function validate(mixed $instance): void; public function has(string $id): bool; /** - * @param class-string|string $id Service name of plugin to retrieve. - * @psalm-return ($id is class-string ? InstanceType : mixed) + * @template TRequestedInstance extends InstanceType + * @psalm-param class-string|string $id Service name of plugin to retrieve. + * @psalm-return ($id is class-string ? TRequestedInstance : InstanceType) * @throws Exception\ServiceNotFoundException If the manager does not have * a service definition for the instance, and the service is not * auto-invokable. @@ -42,8 +43,9 @@ public function get(string $id): mixed; /** * Build a service by its name, using optional options (such services are NEVER cached). * - * @param string|class-string $name - * @psalm-return ($name is class-string ? InstanceType : mixed) + * @template TRequestedInstance extends InstanceType + * @psalm-param string|class-string $name + * @psalm-return ($name is class-string ? TRequestedInstance : InstanceType) * @throws Exception\ServiceNotFoundException If no factory/abstract * factory could be found to create the instance. * @throws Exception\ServiceNotCreatedException If factory/delegator fails diff --git a/src/ServiceManager.php b/src/ServiceManager.php index 75b0d227..1e5ac139 100644 --- a/src/ServiceManager.php +++ b/src/ServiceManager.php @@ -31,11 +31,8 @@ use function array_keys; use function array_merge; use function class_exists; -use function get_debug_type; -use function gettype; use function in_array; use function is_callable; -use function is_object; use function is_string; use function spl_autoload_register; use function spl_object_hash; @@ -61,44 +58,45 @@ * @see AbstractFactoryInterface * @see FactoryInterface * - * @psalm-type AbstractFactoriesConfigurationType = array< + * @psalm-type AbstractFactoriesConfiguration = array< * array-key, * class-string|AbstractFactoryInterface * > - * @psalm-type DelegatorCallableType = callable(ContainerInterface,string,callable():mixed,array|null):mixed - * @psalm-type DelegatorsConfigurationType = array< + * @psalm-type DelegatorCallable = callable(ContainerInterface,string,callable():mixed,array|null):mixed + * @psalm-type DelegatorsConfiguration = array< * string, * array< * array-key, * class-string + * |class-string * |DelegatorFactoryInterface - * |DelegatorCallableType + * |DelegatorCallable * > * > - * @psalm-type FactoryCallableType = callable(ContainerInterface,string,array|null):mixed - * @psalm-type FactoriesConfigurationType = array< + * @psalm-type FactoryCallable = callable(ContainerInterface,string,array|null):mixed + * @psalm-type FactoriesConfiguration = array< * string, - * class-string|FactoryInterface|FactoryCallableType + * class-string|class-string|FactoryInterface|FactoryCallable * > - * @psalm-type InitializerCallableType = callable(ContainerInterface,mixed):void - * @psalm-type InitializersConfigurationType = array< + * @psalm-type InitializerCallable = callable(ContainerInterface,mixed):void + * @psalm-type InitializersConfiguration = array< * array-key, - * class-string|InitializerInterface|InitializerCallableType + * class-string|class-string|InitializerInterface|InitializerCallable * > - * @psalm-type LazyServicesConfigurationType = array{ + * @psalm-type LazyServicesConfiguration = array{ * class_map?:array, * proxies_namespace?:non-empty-string, * proxies_target_dir?:non-empty-string, * write_proxy_files?:bool * } - * @psalm-type ServiceManagerConfigurationType = array{ - * abstract_factories?: AbstractFactoriesConfigurationType, + * @psalm-type ServiceManagerConfiguration = array{ + * abstract_factories?: AbstractFactoriesConfiguration, * aliases?: array, - * delegators?: DelegatorsConfigurationType, - * factories?: FactoriesConfigurationType, - * initializers?: InitializersConfigurationType, + * delegators?: DelegatorsConfiguration, + * factories?: FactoriesConfiguration, + * initializers?: InitializersConfiguration, * invokables?: array, - * lazy_services?: LazyServicesConfigurationType, + * lazy_services?: LazyServicesConfiguration, * services?: array, * shared?:array, * shared_by_default?: bool, @@ -131,20 +129,20 @@ class ServiceManager implements ServiceLocatorInterface /** @var ContainerInterface */ protected $creationContext; - /** @var DelegatorsConfigurationType */ + /** @var DelegatorsConfiguration */ protected $delegators = []; /** * A list of factories (either as string name or callable) * - * @var FactoriesConfigurationType + * @var FactoriesConfiguration */ protected $factories = []; - /** @var InitializersConfigurationType */ + /** @var list */ protected $initializers = []; - /** @var LazyServicesConfigurationType */ + /** @var LazyServicesConfiguration */ protected $lazyServices = []; private ?LazyServiceFactory $lazyServicesDelegator = null; @@ -195,7 +193,7 @@ class ServiceManager implements ServiceLocatorInterface * See {@see \Laminas\ServiceManager\ServiceManager::configure()} for details * on what $config accepts. * - * @param ServiceManagerConfigurationType $config + * @param ServiceManagerConfiguration $config */ public function __construct( array $config = [], @@ -299,7 +297,7 @@ public function getAllowOverride(): bool } /** - * @param ServiceManagerConfigurationType $config + * @param ServiceManagerConfiguration $config * @throws ContainerModificationsNotAllowedException If the allow override flag has been toggled off, and a * service instanceexists for a given service. * @throws InvalidServiceException If an instance passed in the `services` configuration is invalid for the @@ -411,7 +409,7 @@ public function setInvokableClass(string $name, ?string $class = null) * * @param string $name Service name * @param string|callable|FactoryInterface $factory Factory to which to map. - * @psalm-param class-string|FactoryCallableType|FactoryInterface $factory + * @psalm-param class-string|class-string|FactoryCallable|FactoryInterface $factory * @throws ContainerModificationsNotAllowedException If $name already * exists as a service and overrides are disallowed. */ @@ -427,13 +425,27 @@ public function setFactory(string $name, string|callable|FactoryInterface $facto /** * Create a lazy service mapping to a class. * - * @param string $name Service name to map - * @param null|string $class Class to which to map; if not provided, $name + * @param string|class-string $name Service name to map + * @param null|class-string $class Class to which to map; if not provided, $name * will be used for the mapping. */ public function mapLazyService(string $name, ?string $class = null): void { - $this->configure(['lazy_services' => ['class_map' => [$name => $class ?: $name]]]); + $targetClassName = $class ?? $name; + if (! class_exists($targetClassName)) { + $message = sprintf('Provided service name "%s" must be a `class-string`.', $name); + if ($class !== null) { + $message = sprintf( + 'Provided service name "%s" must target to a `class-string`. "%s" provided.', + $name, + $class, + ); + } + + throw new InvalidArgumentException($message); + } + + $this->configure(['lazy_services' => ['class_map' => [$name => $targetClassName]]]); } /** @@ -454,7 +466,7 @@ public function addAbstractFactory($factory) * @param string $name Service name * @param string|callable|DelegatorFactoryInterface $factory Delegator * factory to assign. - * @psalm-param class-string|DelegatorCallableType|DelegatorFactoryInterface $factory + * @psalm-param class-string|class-string|DelegatorCallable|DelegatorFactoryInterface $factory */ public function addDelegator(string $name, string|callable|DelegatorFactoryInterface $factory): void { @@ -464,9 +476,9 @@ public function addDelegator(string $name, string|callable|DelegatorFactoryInter /** * Add an initializer. * - * @psalm-param class-string - * |InitializerCallableType - * |InitializerInterface $initializer + * @phpcs:disable Generic.Files.LineLength.TooLong + * @psalm-param class-string|class-string|InitializerCallable|InitializerInterface $initializer + * @phpcs:enable Generic.Files.LineLength.TooLong */ public function addInitializer(string|callable|InitializerInterface $initializer) { @@ -508,18 +520,22 @@ public function setShared(string $name, bool $flag): void /** * Instantiate initializers for to avoid checks during service construction. * - * @param InitializersConfigurationType $initializers + * @param InitializersConfiguration $initializers */ private function resolveInitializers(array $initializers): void { $resolved = []; foreach ($initializers as $initializer) { if (is_string($initializer) && class_exists($initializer)) { + /** + * @psalm-suppress MixedMethodCall We are calling an unknown initializer. + * We have to trust that the class is instantiable. + */ $initializer = new $initializer(); } if (is_callable($initializer)) { - /** @psalm-var InitializerCallableType $initializer */ + /** @psalm-var InitializerCallable $initializer */ $resolved[] = $initializer; continue; } @@ -533,7 +549,7 @@ private function resolveInitializers(array $initializers): void /** * Get a factory for the given service name * - * @return FactoryCallableType|FactoryInterface + * @return FactoryCallable|FactoryInterface * @throws ServiceNotFoundException In case that the service creation strategy based on factories * did not find any capable factory. */ @@ -543,12 +559,13 @@ private function getFactory(string $name): callable|FactoryInterface $lazyLoaded = false; if (is_string($factory) && class_exists($factory)) { + /** @psalm-suppress MixedMethodCall We have to trust that the factory is instantiable. */ $factory = new $factory(); $lazyLoaded = true; } if (is_callable($factory)) { - /** @psalm-var FactoryCallableType $factory */ + /** @psalm-var FactoryCallable $factory */ if ($lazyLoaded) { $this->factories[$name] = $factory; } @@ -581,7 +598,8 @@ private function createDelegatorFromName(string $name, ?array $options = null): $resolvedDelegators = []; foreach ($this->delegators[$name] as $index => $delegatorFactory) { - $delegatorFactory = $this->resolveDelegatorFactory($this->delegators[$name][$index]); + /** @psalm-suppress ArgumentTypeCoercion https://github.com/vimeo/psalm/issues/9680 */ + $delegatorFactory = $this->resolveDelegatorFactory($delegatorFactory); $this->delegators[$name][$index] = $delegatorFactory; $creationCallback = static fn(): mixed => $delegatorFactory($initialCreationContext, $name, $creationCallback, $options); @@ -681,8 +699,8 @@ private function createLazyServiceDelegatorFactory(): LazyServiceFactory * It works with strings and class instances. * It's not possible to de-duple anonymous functions * - * @param DelegatorsConfigurationType $config - * @return DelegatorsConfigurationType + * @param DelegatorsConfiguration $config + * @return DelegatorsConfiguration */ private function mergeDelegators(array $config): array { @@ -737,7 +755,7 @@ private function createAliasesAndFactoriesForInvokables(array $invokables): arra * a given service name we do not have a service instance * in the cache OR override is explicitly allowed. * - * @param ServiceManagerConfigurationType $config + * @param ServiceManagerConfiguration $config * @throws ContainerModificationsNotAllowedException If any * service key is invalid. */ @@ -946,8 +964,8 @@ private function abstractFactoryCanCreate(string $name): bool } /** - * @param class-string|DelegatorCallableType|DelegatorFactoryInterface $delegatorFactory - * @return DelegatorCallableType + * @param class-string|DelegatorCallable|DelegatorFactoryInterface $delegatorFactory + * @return DelegatorCallable */ private function resolveDelegatorFactory(DelegatorFactoryInterface|string|callable $delegatorFactory): callable { diff --git a/test/CommonServiceLocatorBehaviorsTrait.php b/test/CommonServiceLocatorBehaviorsTrait.php index 3cf05b3b..d1feb3cf 100644 --- a/test/CommonServiceLocatorBehaviorsTrait.php +++ b/test/CommonServiceLocatorBehaviorsTrait.php @@ -8,12 +8,12 @@ use Laminas\ServiceManager\AbstractPluginManager; use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException; use Laminas\ServiceManager\Exception\CyclicAliasException; -use Laminas\ServiceManager\Exception\InvalidArgumentException; use Laminas\ServiceManager\Exception\ServiceNotCreatedException; use Laminas\ServiceManager\Factory\AbstractFactoryInterface; use Laminas\ServiceManager\Factory\FactoryInterface; use Laminas\ServiceManager\Factory\InvokableFactory; use Laminas\ServiceManager\Initializer\InitializerInterface; +use Laminas\ServiceManager\ServiceLocatorInterface; use Laminas\ServiceManager\ServiceManager; use LaminasBench\ServiceManager\BenchAsset\AbstractFactoryFoo; use LaminasTest\ServiceManager\TestAsset\CallTimesAbstractFactory; @@ -38,7 +38,7 @@ /** * @see TestCase * - * @psalm-import-type ServiceManagerConfigurationType from ServiceManager + * @psalm-import-type ServiceManagerConfiguration from ServiceManager * @psalm-require-extends TestCase */ trait CommonServiceLocatorBehaviorsTrait @@ -49,10 +49,9 @@ trait CommonServiceLocatorBehaviorsTrait protected ContainerInterface|null $creationContext; /** - * @psalm-param ServiceManagerConfigurationType $config - * @todo This will need to be static for future versions of PHPUnit + * @psalm-param ServiceManagerConfiguration $config */ - abstract public function createContainer(array $config = []): AbstractPluginManager|ServiceManager; + abstract public function createContainer(array $config = []): ServiceLocatorInterface; public function testIsSharedByDefault(): void { @@ -548,21 +547,6 @@ public function testCanSpecifyAbstractFactoryUsingStringViaConfiguration(): void self::assertInstanceOf(DateTime::class, $dateTime); } - public function invalidFactories(): array - { - return [ - 'null' => [null], - 'true' => [true], - 'false' => [false], - 'zero' => [0], - 'int' => [1], - 'zero-float' => [0.0], - 'float' => [1.1], - 'array' => [['foo', 'bar']], - 'non-invokable-object' => [(object) ['foo' => 'bar']], - ]; - } - public function testCanSpecifyInitializerUsingStringViaConfiguration(): void { $serviceManager = $this->createContainer([ @@ -581,28 +565,6 @@ public function testCanSpecifyInitializerUsingStringViaConfiguration(): void self::assertEquals('bar', $instance->foo, '"foo" property was not properly injected'); } - public function invalidInitializers(): array - { - $factories = $this->invalidFactories(); - $factories['non-class-string'] = ['non-callable-string', 'callable or an instance of']; - - return $factories; - } - - /** - * @dataProvider invalidInitializers - * @covers \Laminas\ServiceManager\ServiceManager::configure - */ - public function testPassingInvalidInitializerTypeViaConfigurationRaisesException( - mixed $initializer, - string $contains = 'invalid initializer' - ): void { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage($contains); - /** @psalm-suppress InvalidArgument */ - $this->createContainer(['initializers' => [$initializer]]); - } - /** * @covers \Laminas\ServiceManager\ServiceManager::getFactory */ @@ -725,7 +687,7 @@ public function testCanInjectInitializers(): void ]); $container->addInitializer(static function (ContainerInterface $container, $instance) { if (! $instance instanceof stdClass) { - return; + return $instance; } $instance->name = stdClass::class; diff --git a/test/ExamplePluginManagerTest.php b/test/ExamplePluginManagerTest.php index 388f1c1d..60d64d2d 100644 --- a/test/ExamplePluginManagerTest.php +++ b/test/ExamplePluginManagerTest.php @@ -15,14 +15,14 @@ /** * Example test of using CommonPluginManagerTrait * - * @psalm-import-type ServiceManagerConfigurationType from ServiceManager + * @psalm-import-type ServiceManagerConfiguration from ServiceManager */ final class ExamplePluginManagerTest extends TestCase { use CommonPluginManagerTrait; /** - * @param ServiceManagerConfigurationType $config + * @param ServiceManagerConfiguration $config */ protected function getPluginManager(array $config = []): AbstractPluginManager { diff --git a/test/ServiceManagerTest.php b/test/ServiceManagerTest.php index 18aea5a6..b41045dd 100644 --- a/test/ServiceManagerTest.php +++ b/test/ServiceManagerTest.php @@ -19,14 +19,14 @@ /** * @covers \Laminas\ServiceManager\ServiceManager - * @psalm-import-type ServiceManagerConfigurationType from ServiceManager + * @psalm-import-type ServiceManagerConfiguration from ServiceManager */ final class ServiceManagerTest extends TestCase { use CommonServiceLocatorBehaviorsTrait; /** - * @psalm-param ServiceManagerConfigurationType $config + * @psalm-param ServiceManagerConfiguration $config */ public function createContainer(array $config = []): ServiceManager { @@ -511,7 +511,7 @@ static function (object $service) use (&$initializerTwoCalled): object { } /** - * @param ServiceManagerConfigurationType $config + * @param ServiceManagerConfiguration $config * @param non-empty-string $serviceName * @param non-empty-string $alias * @dataProvider aliasedServices @@ -530,9 +530,9 @@ public function testWontShareServiceWhenRequestedByAlias(array $config, string $ /** * @psalm-return array */ public function aliasedServices(): array From cf04c17132018585ac62f942ecc689d54ff11cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 20 Apr 2023 03:00:16 +0200 Subject: [PATCH 12/17] qa: disable some codestandard checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- src/AbstractPluginManager.php | 9 +++++++-- src/ServiceManager.php | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/AbstractPluginManager.php b/src/AbstractPluginManager.php index 652327c0..39d80fee 100644 --- a/src/AbstractPluginManager.php +++ b/src/AbstractPluginManager.php @@ -161,8 +161,10 @@ public function configure(array $config): static } } + // phpcs:disable SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable /** @var ServiceManagerConfiguration $config */ $this->plugins->configure($config); + // phpcs:enable SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable return $this; } @@ -254,7 +256,9 @@ public function setInvokableClass(string $name, string|null $class = null): void * * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * + * @phpcs:disable Generic.Files.LineLength.TooLong * @param class-string|class-string|FactoryCallable|FactoryInterface $factory + * @phpcs:enable Generic.Files.LineLength.TooLong * @throws ContainerModificationsNotAllowedException If $name already * exists as a service and overrides are disallowed. */ @@ -297,9 +301,10 @@ public function addAbstractFactory(string|AbstractFactoryInterface $factory): vo * @deprecated Please use {@see AbstractPluginManager::configure()} instead. * * @param string $name Service name - * @param string|callable|DelegatorFactoryInterface $factory Delegator - * factory to assign. + * @param string|callable|DelegatorFactoryInterface $factory Delegator factory to assign. + * @phpcs:disable Generic.Files.LineLength.TooLong * @psalm-param class-string|class-string|DelegatorCallable $factory + * @phpcs:enable Generic.Files.LineLength.TooLong */ public function addDelegator(string $name, string|callable|DelegatorFactoryInterface $factory): void { diff --git a/src/ServiceManager.php b/src/ServiceManager.php index 1e5ac139..29b3beb6 100644 --- a/src/ServiceManager.php +++ b/src/ServiceManager.php @@ -409,7 +409,9 @@ public function setInvokableClass(string $name, ?string $class = null) * * @param string $name Service name * @param string|callable|FactoryInterface $factory Factory to which to map. + * @phpcs:disable Generic.Files.LineLength.TooLong * @psalm-param class-string|class-string|FactoryCallable|FactoryInterface $factory + * @phpcs:enable Generic.Files.LineLength.TooLong * @throws ContainerModificationsNotAllowedException If $name already * exists as a service and overrides are disallowed. */ @@ -466,7 +468,9 @@ public function addAbstractFactory($factory) * @param string $name Service name * @param string|callable|DelegatorFactoryInterface $factory Delegator * factory to assign. + * @phpcs:disable Generic.Files.LineLength.TooLong * @psalm-param class-string|class-string|DelegatorCallable|DelegatorFactoryInterface $factory + * @phpcs:enable Generic.Files.LineLength.TooLong */ public function addDelegator(string $name, string|callable|DelegatorFactoryInterface $factory): void { From 0ccd68beef43381f679923201b773f270990930d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 20 Apr 2023 03:04:46 +0200 Subject: [PATCH 13/17] qa: remove `laminas-code` shims for PHP 8.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- composer.json | 3 - .../laminas-code/ClassReflection.php | 231 --------- .../laminas-code/MethodReflection.php | 466 ------------------ .../laminas-code/ParameterReflection.php | 129 ----- test/autoload.php | 7 - 5 files changed, 836 deletions(-) delete mode 100644 test/TestAsset/laminas-code/ClassReflection.php delete mode 100644 test/TestAsset/laminas-code/MethodReflection.php delete mode 100644 test/TestAsset/laminas-code/ParameterReflection.php delete mode 100644 test/autoload.php diff --git a/composer.json b/composer.json index d6176ffa..4414f0e5 100644 --- a/composer.json +++ b/composer.json @@ -74,9 +74,6 @@ } }, "autoload-dev": { - "files": [ - "test/autoload.php" - ], "psr-4": { "LaminasTest\\ServiceManager\\": "test/", "LaminasBench\\ServiceManager\\": "benchmarks/" diff --git a/test/TestAsset/laminas-code/ClassReflection.php b/test/TestAsset/laminas-code/ClassReflection.php deleted file mode 100644 index 7ad47694..00000000 --- a/test/TestAsset/laminas-code/ClassReflection.php +++ /dev/null @@ -1,231 +0,0 @@ -docBlock)) { - return $this->docBlock; - } - - if ('' == $this->getDocComment()) { - return false; - } - - $this->docBlock = new DocBlockReflection($this); - - return $this->docBlock; - } - - /** - * Return the start line of the class - * - * @param bool $includeDocComment - * @return int - */ - #[ReturnTypeWillChange] - public function getStartLine($includeDocComment = false) - { - if ($includeDocComment && $this->getDocComment() != '') { - return $this->getDocBlock()->getStartLine(); - } - - return parent::getStartLine(); - } - - /** - * Return the contents of the class - * - * @param bool $includeDocBlock - * @return string - */ - public function getContents($includeDocBlock = true) - { - $fileName = $this->getFileName(); - - if (false === $fileName || ! file_exists($fileName)) { - return ''; - } - - $filelines = file($fileName); - $startnum = $this->getStartLine($includeDocBlock); - $endnum = $this->getEndLine() - $this->getStartLine(); - - // Ensure we get between the open and close braces - $lines = array_slice($filelines, $startnum, $endnum); - array_unshift($lines, $filelines[$startnum - 1]); - - return strstr(implode('', $lines), '{'); - } - - /** - * Get all reflection objects of implemented interfaces - * - * @return ClassReflection[] - */ - #[ReturnTypeWillChange] - public function getInterfaces() - { - $phpReflections = parent::getInterfaces(); - $laminasReflections = []; - while ($phpReflections && ($phpReflection = array_shift($phpReflections))) { - $instance = new ClassReflection($phpReflection->getName()); - $laminasReflections[] = $instance; - unset($phpReflection); - } - unset($phpReflections); - - return $laminasReflections; - } - - /** - * Return method reflection by name - * - * @param string $name - * @return MethodReflection - */ - #[ReturnTypeWillChange] - public function getMethod($name) - { - return new MethodReflection($this->getName(), parent::getMethod($name)->getName()); - } - - /** - * Get reflection objects of all methods - * - * @param int $filter - * @return MethodReflection[] - */ - #[ReturnTypeWillChange] - public function getMethods($filter = -1) - { - $methods = []; - foreach (parent::getMethods($filter) as $method) { - $instance = new MethodReflection($this->getName(), $method->getName()); - $methods[] = $instance; - } - - return $methods; - } - - /** - * Returns an array of reflection classes of traits used by this class. - * - * @return null|array - */ - #[ReturnTypeWillChange] - public function getTraits() - { - $vals = []; - $traits = parent::getTraits(); - if ($traits === null) { - return; - } - - foreach ($traits as $trait) { - $vals[] = new ClassReflection($trait->getName()); - } - - return $vals; - } - - /** - * Get parent reflection class of reflected class - * - * @return ClassReflection|bool - */ - #[ReturnTypeWillChange] - public function getParentClass() - { - $phpReflection = parent::getParentClass(); - if ($phpReflection) { - $laminasReflection = new ClassReflection($phpReflection->getName()); - unset($phpReflection); - - return $laminasReflection; - } - - return false; - } - - /** - * Return reflection property of this class by name - * - * @param string $name - * @return PropertyReflection - */ - #[ReturnTypeWillChange] - public function getProperty($name) - { - $phpReflection = parent::getProperty($name); - $laminasReflection = new PropertyReflection($this->getName(), $phpReflection->getName()); - unset($phpReflection); - - return $laminasReflection; - } - - /** - * Return reflection properties of this class - * - * @param int $filter - * @return PropertyReflection[] - */ - #[ReturnTypeWillChange] - public function getProperties($filter = -1) - { - $phpReflections = parent::getProperties($filter); - $laminasReflections = []; - while ($phpReflections && ($phpReflection = array_shift($phpReflections))) { - $instance = new PropertyReflection($this->getName(), $phpReflection->getName()); - $laminasReflections[] = $instance; - unset($phpReflection); - } - unset($phpReflections); - - return $laminasReflections; - } - - /** - * @return string - */ - public function toString() - { - return parent::__toString(); - } - - /** - * @return string - */ - public function __toString() - { - return parent::__toString(); - } -} diff --git a/test/TestAsset/laminas-code/MethodReflection.php b/test/TestAsset/laminas-code/MethodReflection.php deleted file mode 100644 index 65d7fa50..00000000 --- a/test/TestAsset/laminas-code/MethodReflection.php +++ /dev/null @@ -1,466 +0,0 @@ -getDocComment()) { - return false; - } - - return new DocBlockReflection($this); - } - - /** - * Get start line (position) of method - * - * @param bool $includeDocComment - * @return int - */ - #[ReturnTypeWillChange] - public function getStartLine($includeDocComment = false) - { - if ($includeDocComment) { - if ($this->getDocComment() != '') { - return $this->getDocBlock()->getStartLine(); - } - } - - return parent::getStartLine(); - } - - /** - * Get reflection of declaring class - * - * @return ClassReflection - */ - #[ReturnTypeWillChange] - public function getDeclaringClass() - { - $phpReflection = parent::getDeclaringClass(); - $laminasReflection = new ClassReflection($phpReflection->getName()); - unset($phpReflection); - - return $laminasReflection; - } - - /** - * Get method prototype - * - * @param string $format - * @return array|string - */ - #[ReturnTypeWillChange] - public function getPrototype($format = self::PROTOTYPE_AS_ARRAY) - { - $returnType = 'mixed'; - $docBlock = $this->getDocBlock(); - if ($docBlock) { - $return = $docBlock->getTag('return'); - $returnTypes = $return->getTypes(); - $returnType = count($returnTypes) > 1 ? implode('|', $returnTypes) : $returnTypes[0]; - } - - $declaringClass = $this->getDeclaringClass(); - $prototype = [ - 'namespace' => $declaringClass->getNamespaceName(), - 'class' => substr($declaringClass->getName(), strlen($declaringClass->getNamespaceName()) + 1), - 'name' => $this->getName(), - 'visibility' => $this->isPublic() ? 'public' : ($this->isPrivate() ? 'private' : 'protected'), - 'return' => $returnType, - 'arguments' => [], - ]; - - $parameters = $this->getParameters(); - foreach ($parameters as $parameter) { - $prototype['arguments'][$parameter->getName()] = [ - 'type' => $parameter->detectType(), - 'required' => ! $parameter->isOptional(), - 'by_ref' => $parameter->isPassedByReference(), - 'default' => $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null, - ]; - } - - if ($format == self::PROTOTYPE_AS_STRING) { - $line = $prototype['visibility'] . ' ' . $prototype['return'] . ' ' . $prototype['name'] . '('; - $args = []; - foreach ($prototype['arguments'] as $name => $argument) { - $argsLine = ($argument['type'] ? - $argument['type'] . ' ' - : '') . ($argument['by_ref'] ? '&' : '') . '$' . $name; - if (! $argument['required']) { - $argsLine .= ' = ' . var_export($argument['default'], true); - } - $args[] = $argsLine; - } - $line .= implode(', ', $args); - $line .= ')'; - - return $line; - } - - return $prototype; - } - - /** - * Get all method parameter reflection objects - * - * @return ParameterReflection[] - */ - #[ReturnTypeWillChange] - public function getParameters() - { - $phpReflections = parent::getParameters(); - $laminasReflections = []; - while ($phpReflections && ($phpReflection = array_shift($phpReflections))) { - $instance = new ParameterReflection( - [$this->getDeclaringClass()->getName(), $this->getName()], - $phpReflection->getName() - ); - $laminasReflections[] = $instance; - unset($phpReflection); - } - unset($phpReflections); - - return $laminasReflections; - } - - /** - * Get method contents - * - * @param bool $includeDocBlock - * @return string - */ - public function getContents($includeDocBlock = true) - { - $docComment = $this->getDocComment(); - $content = $includeDocBlock && ! empty($docComment) ? $docComment . "\n" : ''; - $content .= $this->extractMethodContents(); - - return $content; - } - - /** - * Get method body - * - * @return string - */ - public function getBody() - { - return $this->extractMethodContents(true); - } - - /** - * Tokenize method string and return concatenated body - * - * @param bool $bodyOnly - * @return string - */ - protected function extractMethodContents($bodyOnly = false) - { - $fileName = $this->getFileName(); - - if ((class_exists($this->class) && false === $fileName) || ! file_exists($fileName)) { - return ''; - } - - $lines = array_slice( - file($fileName, FILE_IGNORE_NEW_LINES), - $this->getStartLine() - 1, - $this->getEndLine() - ($this->getStartLine() - 1), - true - ); - - $functionLine = implode("\n", $lines); - $tokens = token_get_all(' $token) { - $tokenType = is_array($token) ? token_name($token[0]) : $token; - $tokenValue = is_array($token) ? $token[1] : $token; - - switch ($tokenType) { - case 'T_FINAL': - case 'T_ABSTRACT': - case 'T_PUBLIC': - case 'T_PROTECTED': - case 'T_PRIVATE': - case 'T_STATIC': - case 'T_FUNCTION': - // check to see if we have a valid function - // then check if we are inside function and have a closure - if ($this->isValidFunction($tokens, $key, $this->getName())) { - if ($bodyOnly === false) { - //if first instance of tokenType grab prefixed whitespace - //and append to body - if ($capture === false) { - $body .= $this->extractPrefixedWhitespace($tokens, $key); - } - $body .= $tokenValue; - } - - $capture = true; - } else { - //closure test - if ($firstBrace && $tokenType == 'T_FUNCTION') { - $body .= $tokenValue; - break; - } - $capture = false; - break; - } - break; - - case '{': - if ($capture === false) { - break; - } - - if ($firstBrace === false) { - $firstBrace = true; - if ($bodyOnly === true) { - break; - } - } - - $body .= $tokenValue; - break; - - case '}': - if ($capture === false) { - break; - } - - //check to see if this is the last brace - if ($this->isEndingBrace($tokens, $key)) { - //capture the end brace if not bodyOnly - if ($bodyOnly === false) { - $body .= $tokenValue; - } - - break 2; - } - - $body .= $tokenValue; - break; - - default: - if ($capture === false) { - break; - } - - // if returning body only wait for first brace before capturing - if ($bodyOnly === true && $firstBrace !== true) { - break; - } - - $body .= $tokenValue; - break; - } - } - - //remove ending whitespace and return - return rtrim($body); - } - - /** - * Take current position and find any whitespace - * - * @param array $haystack - * @param int $position - * @return string - */ - protected function extractPrefixedWhitespace($haystack, $position) - { - $content = ''; - $count = count($haystack); - if ($position + 1 == $count) { - return $content; - } - - for ($i = $position - 1; $i >= 0; $i--) { - $tokenType = is_array($haystack[$i]) ? token_name($haystack[$i][0]) : $haystack[$i]; - $tokenValue = is_array($haystack[$i]) ? $haystack[$i][1] : $haystack[$i]; - - //search only for whitespace - if ($tokenType == 'T_WHITESPACE') { - $content .= $tokenValue; - } else { - break; - } - } - - return $content; - } - - /** - * Test for ending brace - * - * @param array $haystack - * @param int $position - * @return bool - */ - protected function isEndingBrace($haystack, $position) - { - $count = count($haystack); - - //advance one position - $position += 1; - - if ($position == $count) { - return true; - } - - for ($i = $position; $i < $count; $i++) { - $tokenType = is_array($haystack[$i]) ? token_name($haystack[$i][0]) : $haystack[$i]; - switch ($tokenType) { - case 'T_FINAL': - case 'T_ABSTRACT': - case 'T_PUBLIC': - case 'T_PROTECTED': - case 'T_PRIVATE': - case 'T_STATIC': - return true; - - case 'T_FUNCTION': - // If a function is encountered and that function is not a closure - // then return true. otherwise the function is a closure, return false - if ($this->isValidFunction($haystack, $i)) { - return true; - } - return false; - - case '}': - case ';': - case 'T_BREAK': - case 'T_CATCH': - case 'T_DO': - case 'T_ECHO': - case 'T_ELSE': - case 'T_ELSEIF': - case 'T_EVAL': - case 'T_EXIT': - case 'T_FINALLY': - case 'T_FOR': - case 'T_FOREACH': - case 'T_GOTO': - case 'T_IF': - case 'T_INCLUDE': - case 'T_INCLUDE_ONCE': - case 'T_PRINT': - case 'T_STRING': - case 'T_STRING_VARNAME': - case 'T_THROW': - case 'T_USE': - case 'T_VARIABLE': - case 'T_WHILE': - case 'T_YIELD': - return false; - } - } - } - - /** - * Test to see if current position is valid function or - * closure. Returns true if it's a function and NOT a closure - * - * @param array $haystack - * @param int $position - * @param string $functionName - * @return bool - */ - protected function isValidFunction($haystack, $position, $functionName = null) - { - $isValid = false; - $count = count($haystack); - for ($i = $position + 1; $i < $count; $i++) { - $tokenType = is_array($haystack[$i]) ? token_name($haystack[$i][0]) : $haystack[$i]; - $tokenValue = is_array($haystack[$i]) ? $haystack[$i][1] : $haystack[$i]; - - //check for occurrence of ( or - if ($tokenType == 'T_STRING') { - //check to see if function name is passed, if so validate against that - if ($functionName !== null && $tokenValue != $functionName) { - $isValid = false; - break; - } - - $isValid = true; - break; - } elseif ($tokenValue == '(') { - break; - } - } - - return $isValid; - } - - /** - * @return string - */ - public function toString() - { - return parent::__toString(); - } - - /** - * @return string - */ - public function __toString() - { - return parent::__toString(); - } -} diff --git a/test/TestAsset/laminas-code/ParameterReflection.php b/test/TestAsset/laminas-code/ParameterReflection.php deleted file mode 100644 index d1ba559d..00000000 --- a/test/TestAsset/laminas-code/ParameterReflection.php +++ /dev/null @@ -1,129 +0,0 @@ -getName()); - unset($phpReflection); - - return $laminasReflection; - } - - /** - * Get class reflection object - * - * @return null|ClassReflection - */ - #[ReturnTypeWillChange] - public function getClass() - { - $phpReflectionType = parent::getType(); - if ($phpReflectionType === null) { - return null; - } - - $laminasReflection = new ClassReflection($phpReflectionType->getName()); - unset($phpReflectionType); - - return $laminasReflection; - } - - /** - * Get declaring function reflection object - * - * @return FunctionReflection|MethodReflection - */ - #[ReturnTypeWillChange] - public function getDeclaringFunction() - { - $phpReflection = parent::getDeclaringFunction(); - if ($phpReflection instanceof ReflectionMethod) { - $laminasReflection = new MethodReflection($this->getDeclaringClass()->getName(), $phpReflection->getName()); - } else { - $laminasReflection = new FunctionReflection($phpReflection->getName()); - } - unset($phpReflection); - - return $laminasReflection; - } - - /** - * Get parameter type - * - * @return string|null - */ - public function detectType() - { - if ( - method_exists($this, 'getType') - && null !== ($type = $this->getType()) - && $type->isBuiltin() - ) { - return $type->getName(); - } - - if (null !== $type && $type->getName() === 'self') { - return $this->getDeclaringClass()->getName(); - } - - if (($class = $this->getClass()) instanceof ReflectionClass) { - return $class->getName(); - } - - $docBlock = $this->getDeclaringFunction()->getDocBlock(); - - if (! $docBlock instanceof DocBlockReflection) { - return null; - } - - $params = $docBlock->getTags('param'); - - if (isset($params[$this->getPosition()])) { - return $params[$this->getPosition()]->getType(); - } - - return null; - } - - /** - * @return string - */ - public function toString() - { - return parent::__toString(); - } - - /** - * @return string - */ - public function __toString() - { - return parent::__toString(); - } -} diff --git a/test/autoload.php b/test/autoload.php deleted file mode 100644 index 8e5af1e3..00000000 --- a/test/autoload.php +++ /dev/null @@ -1,7 +0,0 @@ -= 80100) { - require __DIR__ . '/TestAsset/laminas-code/ParameterReflection.php'; - require __DIR__ . '/TestAsset/laminas-code/MethodReflection.php'; - require __DIR__ . '/TestAsset/laminas-code/ClassReflection.php'; -} From 827be21101110fb062fcd917a047ce9a21cebc3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Wed, 26 Apr 2023 00:08:20 +0200 Subject: [PATCH 14/17] qa: bump psalm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- composer.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/composer.lock b/composer.lock index 5f465bd7..fc750110 100644 --- a/composer.lock +++ b/composer.lock @@ -116,16 +116,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.2", + "version": "v4.15.4", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc" + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", - "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", "shasum": "" }, "require": { @@ -166,9 +166,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" }, - "time": "2022-11-12T15:38:23+00:00" + "time": "2023-03-05T19:49:14+00:00" }, { "name": "psr/container", @@ -3261,16 +3261,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "ffc949a1a2aae270ea064453d7535b82e4c32092" + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ffc949a1a2aae270ea064453d7535b82e4c32092", - "reference": "ffc949a1a2aae270ea064453d7535b82e4c32092", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", "shasum": "" }, "require": { @@ -3315,7 +3315,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" }, "funding": [ { @@ -3323,7 +3323,7 @@ "type": "github" } ], - "time": "2020-09-28T05:32:55+00:00" + "time": "2020-10-26T13:10:38+00:00" }, { "name": "sebastian/environment", @@ -5388,12 +5388,12 @@ "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "5efddb42013538f9e4a757dc2a83b0acc41c1a2e" + "reference": "c059388274a641b0c577c11b845050891fbfbe2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/5efddb42013538f9e4a757dc2a83b0acc41c1a2e", - "reference": "5efddb42013538f9e4a757dc2a83b0acc41c1a2e", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/c059388274a641b0c577c11b845050891fbfbe2c", + "reference": "c059388274a641b0c577c11b845050891fbfbe2c", "shasum": "" }, "require": { @@ -5487,7 +5487,7 @@ "issues": "https://github.com/vimeo/psalm/issues", "source": "https://github.com/vimeo/psalm/tree/master" }, - "time": "2023-04-19T19:12:29+00:00" + "time": "2023-04-25T18:47:59+00:00" }, { "name": "webimpress/coding-standard", @@ -5660,7 +5660,7 @@ "vimeo/psalm": 20 }, "prefer-stable": false, - "prefer-lowest": true, + "prefer-lowest": false, "platform": { "php": "~8.1.0 || ~8.2.0" }, From 9f7d6d7ba0cdd75a792c4d2b95cd75eb495577f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 27 Apr 2023 20:58:16 +0200 Subject: [PATCH 15/17] qa: remove redundant `has` method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- src/PluginManagerInterface.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/PluginManagerInterface.php b/src/PluginManagerInterface.php index a0c0a1b1..483dece7 100644 --- a/src/PluginManagerInterface.php +++ b/src/PluginManagerInterface.php @@ -26,8 +26,6 @@ interface PluginManagerInterface extends ServiceLocatorInterface */ public function validate(mixed $instance): void; - public function has(string $id): bool; - /** * @template TRequestedInstance extends InstanceType * @psalm-param class-string|string $id Service name of plugin to retrieve. From 8f14d753b296bd59722e1b9dd07f30c46b84f694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Tue, 2 May 2023 19:28:54 +0200 Subject: [PATCH 16/17] qa: adapt changes to `CommonPluginManagerTrait` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- psalm-baseline.xml | 15 +---- src/Test/CommonPluginManagerTrait.php | 57 +++++++++---------- test/ExamplePluginManagerTest.php | 15 +---- .../InvokableObjectPluginManager.php | 11 ++-- 4 files changed, 35 insertions(+), 63 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index b24ad71b..e8be8867 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + new $requestedName(...$arguments) @@ -110,19 +110,6 @@ getServiceNotFoundException()]]> getServiceNotFoundException()]]> - - testInstanceOfMatches - testLoadingInvalidElementRaisesException - testPluginAliasesResolve - testRegisteringInvalidElementRaisesException - - - $alias - $expected - - - array - diff --git a/src/Test/CommonPluginManagerTrait.php b/src/Test/CommonPluginManagerTrait.php index b93677f6..e5e0dd10 100644 --- a/src/Test/CommonPluginManagerTrait.php +++ b/src/Test/CommonPluginManagerTrait.php @@ -5,34 +5,38 @@ namespace Laminas\ServiceManager\Test; use Laminas\ServiceManager\AbstractPluginManager; +use Laminas\ServiceManager\AbstractSingleInstancePluginManager; use Laminas\ServiceManager\Exception\InvalidServiceException; +use Laminas\ServiceManager\ServiceManager; use ReflectionProperty; -use function method_exists; +use function assert; +use function is_string; /** - * Trait for testing plugin managers for v2-v3 compatibility + * Trait for testing plugin managers for compatibility * * To use this trait: * * implement the `getPluginManager()` method to return your plugin manager - * * implement the `getV2InvalidPluginException()` method to return the class `validatePlugin()` throws under v2 + * + * @psalm-import-type ServiceManagerConfiguration from ServiceManager */ trait CommonPluginManagerTrait { - public function testInstanceOfMatches() + public function testInstanceOfMatches(): void { $manager = $this->getPluginManager(); $reflection = new ReflectionProperty($manager, 'instanceOf'); $this->assertEquals($this->getInstanceOf(), $reflection->getValue($manager), 'instanceOf does not match'); } - public function testRegisteringInvalidElementRaisesException() + public function testRegisteringInvalidElementRaisesException(): void { $this->expectException($this->getServiceNotFoundException()); $this->getPluginManager()->setService('test', $this); } - public function testLoadingInvalidElementRaisesException() + public function testLoadingInvalidElementRaisesException(): void { $manager = $this->getPluginManager(); $manager->setInvokableClass('test', static::class); @@ -42,55 +46,48 @@ public function testLoadingInvalidElementRaisesException() /** * @dataProvider aliasProvider - * @param string $alias - * @param string $expected */ - public function testPluginAliasesResolve($alias, $expected) + public function testPluginAliasesResolve(string $alias, string $expected): void { $this->assertInstanceOf($expected, $this->getPluginManager()->get($alias), "Alias '$alias' does not resolve'"); } /** - * @return array + * @return list */ - public function aliasProvider() + public function aliasProvider(): array { - $manager = $this->getPluginManager(); - $reflection = new ReflectionProperty($manager, 'aliases'); + $manager = $this->getPluginManager(); + $pluginContainerProperty = new ReflectionProperty(AbstractPluginManager::class, 'plugins'); + $pluginContainer = $pluginContainerProperty->getValue($manager); + self::assertInstanceOf(ServiceManager::class, $pluginContainer); + + $reflection = new ReflectionProperty($pluginContainer, 'aliases'); $data = []; - foreach ($reflection->getValue($manager) as $alias => $expected) { + foreach ($reflection->getValue($pluginContainer) as $alias => $expected) { + assert(is_string($alias) && is_string($expected)); $data[] = [$alias, $expected]; } + return $data; } protected function getServiceNotFoundException(): string { - $manager = $this->getPluginManager(); - if (method_exists($manager, 'configure')) { - return InvalidServiceException::class; - } - return $this->getV2InvalidPluginException(); + return InvalidServiceException::class; } /** * Returns the plugin manager to test * - * @return AbstractPluginManager - */ - abstract protected function getPluginManager(); - - /** - * Returns the FQCN of the exception thrown under v2 by `validatePlugin()` - * - * @return mixed + * @param ServiceManagerConfiguration $config */ - abstract protected function getV2InvalidPluginException(); + abstract protected function getPluginManager(array $config = []): AbstractSingleInstancePluginManager; /** * Returns the value the instanceOf property has been set to * - * @return string + * @return class-string */ - abstract protected function getInstanceOf(); + abstract protected function getInstanceOf(): string; } diff --git a/test/ExamplePluginManagerTest.php b/test/ExamplePluginManagerTest.php index 60d64d2d..0dce6128 100644 --- a/test/ExamplePluginManagerTest.php +++ b/test/ExamplePluginManagerTest.php @@ -4,13 +4,12 @@ namespace LaminasTest\ServiceManager; -use Laminas\ServiceManager\AbstractPluginManager; +use Laminas\ServiceManager\AbstractSingleInstancePluginManager; use Laminas\ServiceManager\ServiceManager; use Laminas\ServiceManager\Test\CommonPluginManagerTrait; use LaminasTest\ServiceManager\TestAsset\InvokableObject; use LaminasTest\ServiceManager\TestAsset\InvokableObjectPluginManager; use PHPUnit\Framework\TestCase; -use RuntimeException; /** * Example test of using CommonPluginManagerTrait @@ -21,17 +20,9 @@ final class ExamplePluginManagerTest extends TestCase { use CommonPluginManagerTrait; - /** - * @param ServiceManagerConfiguration $config - */ - protected function getPluginManager(array $config = []): AbstractPluginManager + protected function getPluginManager(array $config = []): AbstractSingleInstancePluginManager { - return new InvokableObjectPluginManager(new ServiceManager()); - } - - protected function getV2InvalidPluginException(): string - { - return RuntimeException::class; + return new InvokableObjectPluginManager(new ServiceManager(), $config); } protected function getInstanceOf(): string diff --git a/test/TestAsset/InvokableObjectPluginManager.php b/test/TestAsset/InvokableObjectPluginManager.php index 46f221cf..1e03e0e1 100644 --- a/test/TestAsset/InvokableObjectPluginManager.php +++ b/test/TestAsset/InvokableObjectPluginManager.php @@ -7,9 +7,11 @@ use Laminas\ServiceManager\AbstractPluginManager; use Laminas\ServiceManager\AbstractSingleInstancePluginManager; use Laminas\ServiceManager\Factory\InvokableFactory; -use Psr\Container\ContainerInterface; +use Laminas\ServiceManager\ServiceManager; /** + * @psalm-import-type ServiceManagerConfiguration from ServiceManager + * @psalm-import-type FactoriesConfiguration from ServiceManager * @template-extends AbstractPluginManager */ final class InvokableObjectPluginManager extends AbstractSingleInstancePluginManager @@ -22,7 +24,7 @@ final class InvokableObjectPluginManager extends AbstractSingleInstancePluginMan 'laminastestservicemanagertestassetinvokableobject' => InvokableObject::class, ]; - /** @var array */ + /** @var FactoriesConfiguration */ protected array $factories = [ InvokableObject::class => InvokableFactory::class, // Legacy (v2) due to alias resolution @@ -32,9 +34,4 @@ final class InvokableObjectPluginManager extends AbstractSingleInstancePluginMan protected string $instanceOf = InvokableObject::class; protected bool $sharedByDefault = false; - - public function __construct(ContainerInterface $creationContext) - { - parent::__construct($creationContext, ['aliases' => $this->aliases, 'factories' => $this->factories]); - } } From 8469e6d9bd8c174a1936b76e4833417a8fcc605d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Tue, 2 May 2023 20:50:39 +0200 Subject: [PATCH 17/17] qa: bump psalm to stable release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- composer.json | 2 +- composer.lock | 30 ++++++++++++++---------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/composer.json b/composer.json index 4414f0e5..2118272c 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,7 @@ "phpunit/phpunit": "^9.5.26", "psalm/plugin-phpunit": "^0.18.0", "symfony/console": "^6.0", - "vimeo/psalm": "5.x-dev" + "vimeo/psalm": "^5.10" }, "provide": { "psr/container-implementation": "^1.0" diff --git a/composer.lock b/composer.lock index fc750110..59830e24 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": "319541139bcb7286a5afa215143af372", + "content-hash": "6639b46f0ff002d522b4eb0e7c619b0c", "packages": [ { "name": "brick/varexporter", @@ -4497,16 +4497,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.2.7", + "version": "v6.2.10", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3" + "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894", + "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894", "shasum": "" }, "require": { @@ -4540,7 +4540,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.2.7" + "source": "https://github.com/symfony/filesystem/tree/v6.2.10" }, "funding": [ { @@ -4556,7 +4556,7 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2023-04-18T13:46:08+00:00" }, { "name": "symfony/finder", @@ -5384,16 +5384,16 @@ }, { "name": "vimeo/psalm", - "version": "dev-master", + "version": "5.10.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "c059388274a641b0c577c11b845050891fbfbe2c" + "reference": "a5effd2d2dddd1a7ea7a0f6a051ce63ff979e356" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/c059388274a641b0c577c11b845050891fbfbe2c", - "reference": "c059388274a641b0c577c11b845050891fbfbe2c", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/a5effd2d2dddd1a7ea7a0f6a051ce63ff979e356", + "reference": "a5effd2d2dddd1a7ea7a0f6a051ce63ff979e356", "shasum": "" }, "require": { @@ -5444,7 +5444,6 @@ "ext-curl": "In order to send data to shepherd", "ext-igbinary": "^2.0.5 is required, used to serialize caching data" }, - "default-branch": true, "bin": [ "psalm", "psalm-language-server", @@ -5485,9 +5484,9 @@ ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/master" + "source": "https://github.com/vimeo/psalm/tree/5.10.0" }, - "time": "2023-04-25T18:47:59+00:00" + "time": "2023-05-02T17:20:34+00:00" }, { "name": "webimpress/coding-standard", @@ -5656,8 +5655,7 @@ "minimum-stability": "stable", "stability-flags": { "laminas/laminas-container-config-test": 20, - "mikey179/vfsstream": 15, - "vimeo/psalm": 20 + "mikey179/vfsstream": 15 }, "prefer-stable": false, "prefer-lowest": false,