diff --git a/.travis.yml b/.travis.yml index 92908014..3d632872 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,40 +8,20 @@ cache: env: global: - - COMPOSER_ARGS="--no-interaction --no-plugins" - - COVERAGE_DEPS="satooshi/php-coveralls" + - COMPOSER_ARGS="--no-interaction" + - COVERAGE_DEPS="php-coveralls/php-coveralls" - PHPSTAN_DEPS="phpstan/phpstan:^0.10.3" matrix: include: - - php: 5.6 - env: - - DEPS=lowest - - php: 5.6 - env: - - DEPS=locked - - LEGACY_DEPS="ocramius/proxy-manager phpbench/phpbench phpunit/phpunit" - - php: 5.6 - env: - - DEPS=latest - - php: 7 - env: - - DEPS=lowest - - php: 7 - env: - - DEPS=locked - - LEGACY_DEPS="ocramius/proxy-manager phpbench/phpbench phpunit/phpunit" - - php: 7 - env: - - DEPS=latest - php: 7.1 env: - DEPS=lowest - php: 7.1 env: - DEPS=locked - - BENCHMARKS=true - CS_CHECK=true + - BENCHMARKS=true - TEST_COVERAGE=true - PHPSTAN_TEST=true - LEGACY_DEPS="ocramius/proxy-manager phpbench/phpbench phpunit/phpunit" @@ -62,10 +42,9 @@ before_install: - if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi install: - - travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs - - if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update --with-dependencies $COMPOSER_ARGS $LEGACY_DEPS ; fi - - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi + - travis_retry composer install $COMPOSER_ARGS - if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi + - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi - if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi - if [[ $PHPSTAN_TEST == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $PHPSTAN_DEPS ; fi - stty cols 120 && composer show @@ -77,7 +56,7 @@ script: - if [[ $PHPSTAN_TEST == 'true' ]]; then ./vendor/bin/phpstan analyse --no-progress . ; fi after_script: - - if [[ $TEST_COVERAGE == 'true' ]]; then composer upload-coverage ; fi + - if [[ $TEST_COVERAGE == 'true' ]]; then vendor/bin/php-coveralls -v ; fi notifications: email: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 14c896c1..292b17e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,36 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 4.0.0 - TBD + +### Added + +- Nothing. + +### Changed + +- [#221](https://github.com/zendframework/zend-servicemanager/pull/221) provides + enormous performance improvements for each of the various mutator methods + (`setAlias()`, `setFactory()`, etc.), `has()` lookups, and initial + container configuration. + +### Deprecated + +- Nothing. + +### Removed + +- [#197](https://github.com/zendframework/zend-servicemanager/pull/197) drops + support for PHP versions prior to 7.1. + +- [#193](https://github.com/zendframework/zend-servicemanager/pull/193) drops + support for HHVM. + +### Fixed + +- [#230](https://github.com/zendframework/zend-servicemanager/pull/230) fixes a + problem in detecting cyclic aliases, ensuring they are detected correctly. + ## 3.3.2 - 2018-01-29 ### Added diff --git a/benchmarks/BenchAsset/AbstractFactoryFoo.php b/benchmarks/BenchAsset/AbstractFactoryFoo.php index 68879f68..4d61801f 100644 --- a/benchmarks/BenchAsset/AbstractFactoryFoo.php +++ b/benchmarks/BenchAsset/AbstractFactoryFoo.php @@ -7,8 +7,8 @@ namespace ZendBench\ServiceManager\BenchAsset; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Factory\AbstractFactoryInterface; -use Interop\Container\ContainerInterface; class AbstractFactoryFoo implements AbstractFactoryInterface { diff --git a/benchmarks/BenchAsset/FactoryFoo.php b/benchmarks/BenchAsset/FactoryFoo.php index dba627ea..474ea283 100644 --- a/benchmarks/BenchAsset/FactoryFoo.php +++ b/benchmarks/BenchAsset/FactoryFoo.php @@ -7,8 +7,8 @@ namespace ZendBench\ServiceManager\BenchAsset; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Factory\FactoryInterface; -use Interop\Container\ContainerInterface; class FactoryFoo implements FactoryInterface { diff --git a/benchmarks/HasBench.php b/benchmarks/HasBench.php new file mode 100644 index 00000000..6e696d77 --- /dev/null +++ b/benchmarks/HasBench.php @@ -0,0 +1,113 @@ +sm = new ServiceManager([ + 'factories' => [ + 'factory1' => BenchAsset\FactoryFoo::class, + ], + 'invokables' => [ + 'invokable1' => BenchAsset\Foo::class, + ], + 'services' => [ + 'service1' => new \stdClass(), + ], + 'aliases' => [ + 'alias1' => 'service1', + 'recursiveAlias1' => 'alias1', + 'recursiveAlias2' => 'recursiveAlias1', + ], + 'abstract_factories' => [ + BenchAsset\AbstractFactoryFoo::class + ] + ]); + } + + public function benchHasFactory1() + { + // @todo @link https://github.com/phpbench/phpbench/issues/304 + $sm = clone $this->sm; + + $sm->has('factory1'); + } + + public function benchHasInvokable1() + { + // @todo @link https://github.com/phpbench/phpbench/issues/304 + $sm = clone $this->sm; + + $sm->has('invokable1'); + } + + public function benchHasService1() + { + // @todo @link https://github.com/phpbench/phpbench/issues/304 + $sm = clone $this->sm; + + $sm->has('service1'); + } + + public function benchHasAlias1() + { + // @todo @link https://github.com/phpbench/phpbench/issues/304 + $sm = clone $this->sm; + + $sm->has('alias1'); + } + + public function benchHasRecursiveAlias1() + { + // @todo @link https://github.com/phpbench/phpbench/issues/304 + $sm = clone $this->sm; + + $sm->has('recursiveAlias1'); + } + + public function benchHasRecursiveAlias2() + { + // @todo @link https://github.com/phpbench/phpbench/issues/304 + $sm = clone $this->sm; + + $sm->has('recursiveAlias2'); + } + + public function benchHasAbstractFactory() + { + // @todo @link https://github.com/phpbench/phpbench/issues/304 + $sm = clone $this->sm; + + $sm->has('foo'); + } + + public function benchHasNot() + { + // @todo @link https://github.com/phpbench/phpbench/issues/304 + $sm = clone $this->sm; + + $sm->has('42'); + } +} diff --git a/benchmarks/SetNewServicesBench.php b/benchmarks/SetNewServicesBench.php index 9e351060..460f850f 100644 --- a/benchmarks/SetNewServicesBench.php +++ b/benchmarks/SetNewServicesBench.php @@ -56,6 +56,14 @@ public function __construct() $this->sm = new ServiceManager($config); } + public function benchSetService() + { + // @todo @link https://github.com/phpbench/phpbench/issues/304 + $sm = clone $this->sm; + + $sm->setService('service2', new \stdClass()); + } + public function benchSetFactory() { // @todo @link https://github.com/phpbench/phpbench/issues/304 diff --git a/composer.json b/composer.json index 8b181871..ee66c947 100644 --- a/composer.json +++ b/composer.json @@ -21,25 +21,24 @@ "forum": "https://discourse.zendframework.com/c/questions/components" }, "require": { - "php": "^5.6 || ^7.0", - "container-interop/container-interop": "^1.2", + "php": "^7.1", "psr/container": "^1.0", "zendframework/zend-stdlib": "^3.1" }, "require-dev": { - "mikey179/vfsStream": "^1.6.5", - "ocramius/proxy-manager": "^1.0 || ^2.0", + "mikey179/vfsStream": "^1.6.4", + "ocramius/proxy-manager": "^2.1.1", "phpbench/phpbench": "^0.13.0", - "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0" + "phpunit/phpunit": "^6.4.4", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-container-config-test": "^0.2.1" }, "provide": { - "container-interop/container-interop-implementation": "^1.2", "psr/container-implementation": "^1.0" }, "suggest": { - "ocramius/proxy-manager": "ProxyManager 1.* to handle lazy initialization of services", - "zendframework/zend-stdlib": "zend-stdlib ^2.5 if you wish to use the MergeReplaceKey or MergeRemoveKey features in Config instances" + "ocramius/proxy-manager": "ProxyManager ^2.1.1 to handle lazy initialization of services", + "zendframework/zend-stdlib": "zend-stdlib ^2.7.7 | ^3.1 if you wish to use the MergeReplaceKey or MergeRemoveKey features in Config instances" }, "autoload": { "psr-4": { @@ -55,6 +54,9 @@ "config": { "sort-packages": true }, + "conflict": { + "container-interop/container-interop": "<1.2.0" + }, "extra": { "branch-alias": { "dev-master": "3.3-dev", @@ -73,7 +75,6 @@ "cs-check": "phpcs", "cs-fix": "phpcbf", "test": "phpunit --colors=always", - "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", - "upload-coverage": "coveralls -v" + "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" } } diff --git a/composer.lock b/composer.lock index 0fb5a582..986b5688 100644 --- a/composer.lock +++ b/composer.lock @@ -4,39 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "f6e58eb326f8ae66f3f32ffff6b24338", + "content-hash": "f280e96d0b05401d973f1fdd15dc0c90", "packages": [ - { - "name": "container-interop/container-interop", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/container-interop/container-interop.git", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "shasum": "" - }, - "require": { - "psr/container": "^1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Interop\\Container\\": "src/Interop/Container/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", - "homepage": "https://github.com/container-interop/container-interop", - "time": "2017-02-14T19:40:03+00:00" - }, { "name": "psr/container", "version": "1.0.0", @@ -188,6 +157,37 @@ ], "time": "2017-11-13T18:35:09+00:00" }, + { + "name": "container-interop/container-interop", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/container-interop/container-interop.git", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "shasum": "" + }, + "require": { + "psr/container": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Interop\\Container\\": "src/Interop/Container/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", + "homepage": "https://github.com/container-interop/container-interop", + "time": "2017-02-14T19:40:03+00:00" + }, { "name": "doctrine/annotations", "version": "v1.5.0", @@ -2863,6 +2863,61 @@ ], "time": "2016-11-09T21:30:43+00:00" }, + { + "name": "zendframework/zend-container-config-test", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-container-config-test.git", + "reference": "ebc45b7b6b2a8e382fe8a4a4d6c74a196fe53554" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-container-config-test/zipball/ebc45b7b6b2a8e382fe8a4a4d6c74a196fe53554", + "reference": "ebc45b7b6b2a8e382fe8a4a4d6c74a196fe53554", + "shasum": "" + }, + "require": { + "php": "^7.1", + "psr/container": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0.2", + "zendframework/zend-auradi-config": "^1.0.1", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-pimple-config": "^1.1", + "zendframework/zend-servicemanager": "^3.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "files": [ + "src/TestAsset/function-factory.php", + "src/TestAsset/function-factory-with-name.php" + ], + "psr-4": { + "Zend\\ContainerConfigTest\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Expressive PSR-11 container configuration tests", + "keywords": [ + "PSR-11", + "ZendFramework", + "container", + "expressive", + "test", + "zf" + ], + "time": "2018-04-12T18:58:34+00:00" + }, { "name": "zendframework/zend-eventmanager", "version": "3.2.0", @@ -2924,7 +2979,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^5.6 || ^7.0" + "php": "^7.1" }, "platform-dev": [] } diff --git a/docs/book/configuring-the-service-manager.md b/docs/book/configuring-the-service-manager.md index df924fef..2c2a49d1 100644 --- a/docs/book/configuring-the-service-manager.md +++ b/docs/book/configuring-the-service-manager.md @@ -68,7 +68,7 @@ $serviceManager = new ServiceManager([ As said before, a factory can also be a callable, to create more complex objects: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Factory\InvokableFactory; use Zend\ServiceManager\ServiceManager; use stdClass; @@ -295,7 +295,7 @@ For instance, if we'd want to automatically inject the dependency `EventManagerAwareInterface`, we could create the following initializer: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use stdClass; use Zend\ServiceManager\ServiceManager; @@ -331,7 +331,7 @@ class MyInitializer implements InitializerInterface // When creating the service manager: -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use stdClass; use Zend\ServiceManager\ServiceManager; @@ -451,7 +451,7 @@ So far, we have covered examples where services are created through factories Occasionally you may need to pass additional options that act as a "context". For instance, we could have a `StringLengthValidator` service registered. However, this validator can have multiple options, such as `min` and `max`. -Because this is dependant on the caller context (or might even be retrieved +Because this is dependent on the caller context (or might even be retrieved from a database, for instance), the factory cannot know what options to give when constructing the validator. diff --git a/docs/book/cookbook/factories-vs-abstract-factories.md b/docs/book/cookbook/factories-vs-abstract-factories.md index 65c80191..4d0c70ce 100644 --- a/docs/book/cookbook/factories-vs-abstract-factories.md +++ b/docs/book/cookbook/factories-vs-abstract-factories.md @@ -57,7 +57,7 @@ This means, internally: - a hash table lookup (for the abstract factory) - invocation of 1:N methods for discovery - which may contain additional lookups and/or retrievals in the container -- invocation of a factory method (assuming succesful lookup) +- invocation of a factory method (assuming successful lookup) As such, having an explicit map can aid performance dramatically. diff --git a/docs/book/delegators.md b/docs/book/delegators.md index 93991cd0..5ac0ae09 100644 --- a/docs/book/delegators.md +++ b/docs/book/delegators.md @@ -14,7 +14,7 @@ intercept actions being performed on the delegate in an A delegator factory has the following signature: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; public function __invoke( ContainerInterface $container, @@ -106,7 +106,7 @@ A simple delegator factory for the `buzzer` service can be implemented as following: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Factory\DelegatorFactoryInterface; class BuzzerDelegatorFactory implements DelegatorFactoryInterface diff --git a/docs/book/migration-v4.md b/docs/book/migration-v4.md new file mode 100644 index 00000000..4bb25864 --- /dev/null +++ b/docs/book/migration-v4.md @@ -0,0 +1,43 @@ +# Migration Guide + +Migration guide for Zend Service version 4.0.0. + +## PSR-11: Container Interface + +[`container-interop/container-interop`](https://github.com/container-interop/container-interop) +was officially deprecated in favor of [PSR-11](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md) +on February 13, 2017. As such, all uses of the interop-container interfaces +have been replaced with PSR-11 containers interfaces as follows: + - `Interop\Container\ContainerInterface` to `Psr\Container\ContainerInterface`, + - `Interop\Container\Exception\ContainerException` to `Psr\Container\ContainerExceptionInterface`, + - `Interop\Container\Exception\NotFoundException` to `Psr\Container\NotFoundExceptionInterface`. + +Further, installs of `container-interop/container-interop` below version `1.2.0` +is prohibited via Composer's `conflicts` configuration. Version `1.2.0` _does_ +extend the PSR-11 interfaces, and is thus still usable. + +If your project typehints any `Interop\ContainerInterop\*` interfaces where any +`Zend\ServiceManager\*` classes are expected, you _**must**_ update your code to +expect `Zend\ServiceManager\*` or `Psr\Container\*` classes or interfaces instead. +The latter is preferred, unless your code utilizes any additional functionality +provided by the `Zend\ServiceManager\*` classes that are not declared in the +PSR-11 interfaces. + +To do this, use your favorite find-and-replace tool to update the following: + - `use Interop\Container\ContainerInterface;` -> `use Psr\Container\ContainerInterface;` + - `use Interop\Container\Exception\ContainerException;` -> `use Psr\Container\ContainerExceptionInterface;` + - `use Interop\Container\Exception\NotFoundException;` -> `use Psr\Container\NotFoundExceptionInterface;` + - **Note:** You will also need to replace `ContainerException` with `ContainerExceptionInterface` + and `NotFoundException` with `NotFoundExceptionInterface` where it is used + throughout your code. If you _don't_ want to do that, you can include + `as ContainerException`/`as NotFoundException` in the find-and-replace, + and any existing use of `ContainerException`/`NotFoundException` will + continue to work. + +> ### Note +> +> If you use fully-qualified class names in your typehints, rather than taking +> advantage of `use` statements, you will need to run additional find-and-replace +> commands to update those class paths within your code. The exact finds and +> replaces to run for those scenarios are not covered in this migration guide, as +> they can vary greatly and are not a generally recommended practice. diff --git a/docs/book/migration.md b/docs/book/migration.md index 0b95662c..57b19f8f 100644 --- a/docs/book/migration.md +++ b/docs/book/migration.md @@ -251,7 +251,7 @@ $container->mapLazyService('MyClass'); ## ServiceLocatorInterface Changes The `ServiceLocatorInterface` now extends the -[container-interop](https://github.com/container-interop/container-interop) +[Psr\Container\ContainerInterface](https://github.com/php-fig/container) interface `ContainerInterface`, which defines the same `get()` and `has()` methods as were previously defined. @@ -436,7 +436,7 @@ To update this for version 3 compatibility, you will add the methods them, and update the existing methods to proxy to the new methods: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\AbstractFactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; @@ -474,7 +474,7 @@ the migration artifacts: From our example above, we would update the class to read as follows: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Factory\AbstractFactoryInterface; // <-- note the change! class LenientAbstractFactory implements AbstractFactoryInterface @@ -562,7 +562,7 @@ To prepare this for version 3, we'd implement the `__invoke()` signature from version 3, and modify `createDelegatorWithName()` to proxy to it: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\DelegatorFactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; @@ -591,7 +591,7 @@ the migration artifacts: From our example above, we would update the class to read as follows: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Factory\DelegatorFactoryInterface; // <-- note the change! class ObserverAttachmentDelegator implements DelegatorFactoryInterface @@ -674,7 +674,7 @@ To prepare this for version 3, we'd implement the `__invoke()` signature from version 3, and modify `createService()` to proxy to it: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\FactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; @@ -706,7 +706,7 @@ the migration artifacts: From our example above, we would update the class to read as follows: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Factory\FactoryInterface; // <-- note the change! class FooFactory implements FactoryInterface @@ -808,7 +808,7 @@ To prepare this for version 3, we'd implement the `__invoke()` signature from version 3, and modify `initialize()` to proxy to it: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\InitializerInterface; use Zend\ServiceManager\ServiceLocatorInterface; @@ -839,7 +839,7 @@ the migration artifacts: From our example above, we would update the class to read as follows: ```php -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Initializer\InitializerInterface; // <-- note the change! class FooInitializer implements InitializerInterface diff --git a/docs/book/psr-11.md b/docs/book/psr-11.md deleted file mode 100644 index 194aa9e9..00000000 --- a/docs/book/psr-11.md +++ /dev/null @@ -1,58 +0,0 @@ -# PSR-11 Support - -[container-interop/container-interop 1.2.0](https://github.com/container-interop/container-interop/releases/tag/1.2.0) -modifies its codebase to extend interfaces from [psr/container](https://github.com/php-fig/container) -(the official interfaces for [PSR-11](http://www.php-fig.org/psr/psr-11/)). If -you are on a pre-3.3.0 version of zend-servicemanager, update your project, and -receive container-interop 1.2, then zend-servicemanager can already act as a -PSR-11 provider! - -zend-servicemanager 3.3.0 requires at least version 1.2 of container-interop, -and _also_ requires psr/container 1.0 to explicitly signal that it is a PSR-11 -provider, and to allow removal of the container-interop dependency later. - -Version 4.0 will require only psr/container, and will update the various factory -interfaces and exception implementations to typehint against the PSR-11 -interfaces, which will require changes to any implementations you have. In the -meantime, you can [duck-type](https://en.wikipedia.org/wiki/Duck_typing) the -following factory types: - -- `Zend\ServiceManager\Factory\FactoryInterface`: use a callable with the - following signature: - - ```php - function ( - \Psr\Container\ContainerInterface $container, - string $requestedName, - array $options = null - ) - ``` - -- `Zend\ServiceManager\Factory\DelegatorFactoryInterface`: use a callable with - the following signature: - - ```php - function ( - \Psr\Container\ContainerInterface $container, - string $name, - callable $callback, - array $options = null - ) - ``` - -- `Zend\ServiceManager\Initializer\InitializerInterface`: use a callable with - the following signature: - - ```php - function ( - \Psr\Container\ContainerInterface $container, - $instance - ) - ``` - -Abstract factories _can not_ be duck typed, due to the additional `canCreate()` -method. - -You can also leave your factories as-is for now, and update them once -zend-servicemanager v4.0 is released, at which time we will be providing tooling -to help migrate your factories to PSR-11. diff --git a/docs/book/quick-start.md b/docs/book/quick-start.md index 95e157c2..a66515ab 100644 --- a/docs/book/quick-start.md +++ b/docs/book/quick-start.md @@ -3,7 +3,7 @@ The Service Manager is a modern, fast, and easy-to-use implementation of the [Service Locator design pattern](https://en.wikipedia.org/wiki/Service_locator_pattern). The implementation implements the -[Container Interop](https://github.com/container-interop/container-interop) +[Psr\Container\ContainerInterface](https://github.com/psr/container) interfaces, providing interoperability with other implementations. The following is a "quick start" tutorial intended to get you up and running diff --git a/mkdocs.yml b/mkdocs.yml index c08cda87..aff89875 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,7 +4,6 @@ pages: - index.md - 'Quick Start': quick-start.md - Reference: - - 'PSR-11 Support': psr-11.md - 'Configuring the service manager': configuring-the-service-manager.md - Delegators: delegators.md - 'Lazy services': lazy-services.md diff --git a/src/AbstractFactory/ConfigAbstractFactory.php b/src/AbstractFactory/ConfigAbstractFactory.php index 4e53a4a3..8368f475 100644 --- a/src/AbstractFactory/ConfigAbstractFactory.php +++ b/src/AbstractFactory/ConfigAbstractFactory.php @@ -11,15 +11,20 @@ use Zend\ServiceManager\Exception\ServiceNotCreatedException; use Zend\ServiceManager\Factory\AbstractFactoryInterface; +use function array_key_exists; +use function array_map; +use function array_values; +use function is_array; +use function json_encode; + final class ConfigAbstractFactory implements AbstractFactoryInterface { - /** * Factory can create the service if there is a key for it in the config * * {@inheritdoc} */ - public function canCreate(\Interop\Container\ContainerInterface $container, $requestedName) + public function canCreate(\Psr\Container\ContainerInterface $container, $requestedName) { if (! $container->has('config')) { return false; @@ -39,7 +44,7 @@ public function canCreate(\Interop\Container\ContainerInterface $container, $req /** * {@inheritDoc} */ - public function __invoke(\Interop\Container\ContainerInterface $container, $requestedName, array $options = null) + public function __invoke(\Psr\Container\ContainerInterface $container, $requestedName, array $options = null) { if (! $container->has('config')) { throw new ServiceNotCreatedException('Cannot find a config array in the container'); @@ -66,8 +71,8 @@ public function __invoke(\Interop\Container\ContainerInterface $container, $requ $serviceDependencies = $dependencies[$requestedName]; - if ($serviceDependencies !== array_values(array_map('strval', $serviceDependencies))) { - $problem = json_encode(array_map('gettype', $serviceDependencies)); + if ($serviceDependencies !== array_values(array_map('\strval', $serviceDependencies))) { + $problem = json_encode(array_map('\gettype', $serviceDependencies)); throw new ServiceNotCreatedException('Service message must be an array of strings, ' . $problem . ' given'); } diff --git a/src/AbstractFactory/ReflectionBasedAbstractFactory.php b/src/AbstractFactory/ReflectionBasedAbstractFactory.php index 04b3f562..ca519ece 100644 --- a/src/AbstractFactory/ReflectionBasedAbstractFactory.php +++ b/src/AbstractFactory/ReflectionBasedAbstractFactory.php @@ -7,12 +7,16 @@ namespace Zend\ServiceManager\AbstractFactory; -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use ReflectionClass; use ReflectionParameter; use Zend\ServiceManager\Exception\ServiceNotFoundException; use Zend\ServiceManager\Factory\AbstractFactoryInterface; +use function array_map; +use function class_exists; +use function sprintf; + /** * Reflection-based factory. * @@ -220,7 +224,7 @@ private function resolveParameter(ReflectionParameter $parameter, ContainerInter } $type = $parameterClass->getName(); - $type = isset($this->aliases[$type]) ? $this->aliases[$type] : $type; + $type = $this->aliases[$type] ?? $type; if ($container->has($type)) { return $container->get($type); diff --git a/src/AbstractFactoryInterface.php b/src/AbstractFactoryInterface.php index 57e0f43a..054bea35 100644 --- a/src/AbstractFactoryInterface.php +++ b/src/AbstractFactoryInterface.php @@ -16,11 +16,11 @@ * * - rename the method `canCreateServiceWithName()` to `canCreate()`, and: * - rename the `$serviceLocator` argument to `$container`, and change the - * typehint to `Interop\Container\ContainerInterface` + * typehint to `Psr\Container\ContainerInterface` * - merge the `$name` and `$requestedName` arguments * - rename the method `createServiceWithName()` to `__invoke()`, and: * - rename the `$serviceLocator` argument to `$container`, and change the - * typehint to `Interop\Container\ContainerInterface` + * typehint to `Psr\Container\ContainerInterface` * - merge the `$name` and `$requestedName` arguments * - add the optional `array $options = null` argument. * - create a `canCreateServiceWithName()` method as defined in this interface, and have it diff --git a/src/AbstractPluginManager.php b/src/AbstractPluginManager.php index bda531f7..ca196472 100644 --- a/src/AbstractPluginManager.php +++ b/src/AbstractPluginManager.php @@ -7,9 +7,17 @@ namespace Zend\ServiceManager; -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Exception\InvalidServiceException; +use function class_exists; +use function get_class; +use function gettype; +use function is_object; +use function method_exists; +use function sprintf; +use function trigger_error; + /** * Abstract plugin manager. * @@ -112,6 +120,17 @@ public function configure(array $config) return $this; } + /** + * Override setService for additional plugin validation. + * + * {@inheritDoc} + */ + public function setService($name, $service) + { + $this->validate($service); + parent::setService($name, $service); + } + /** * {@inheritDoc} * diff --git a/src/Config.php b/src/Config.php index c43cb0dc..b82d4c85 100644 --- a/src/Config.php +++ b/src/Config.php @@ -10,6 +10,11 @@ use Zend\Stdlib\ArrayUtils\MergeRemoveKey; use Zend\Stdlib\ArrayUtils\MergeReplaceKeyInterface; +use function array_key_exists; +use function array_keys; +use function is_array; +use function is_int; + /** * Object for defining configuration and configuring an existing service manager instance. * diff --git a/src/DelegatorFactoryInterface.php b/src/DelegatorFactoryInterface.php index 9325d1b1..39fcb0dc 100644 --- a/src/DelegatorFactoryInterface.php +++ b/src/DelegatorFactoryInterface.php @@ -16,7 +16,7 @@ * * - rename the method `createDelegatorWithName()` to `__invoke()`, and: * - rename the `$serviceLocator` argument to `$container`, and change the - * typehint to `Interop\Container\ContainerInterface` + * typehint to `Psr\Container\ContainerInterface` * - merge the `$name` and `$requestedName` arguments * - add the `callable` typehint to the `$callback` argument * - add the optional `array $options = null` argument as a final argument diff --git a/src/Exception/ContainerModificationsNotAllowedException.php b/src/Exception/ContainerModificationsNotAllowedException.php index 3bf462b0..8f315d66 100644 --- a/src/Exception/ContainerModificationsNotAllowedException.php +++ b/src/Exception/ContainerModificationsNotAllowedException.php @@ -9,9 +9,24 @@ use DomainException; +use function sprintf; + /** * @inheritDoc */ class ContainerModificationsNotAllowedException extends DomainException implements ExceptionInterface { + /** + * @param string $service Name of service that already exists. + * @return self + */ + public static function fromExistingService($service) + { + return new self(sprintf( + 'The container does not allow replacing or updating a service' + . ' with existing instances; the following service' + . ' already exists in the container: %s', + $service + )); + } } diff --git a/src/Exception/CyclicAliasException.php b/src/Exception/CyclicAliasException.php index 4ed64f84..88460e57 100644 --- a/src/Exception/CyclicAliasException.php +++ b/src/Exception/CyclicAliasException.php @@ -7,11 +7,41 @@ namespace Zend\ServiceManager\Exception; +use function array_filter; +use function array_keys; +use function array_map; +use function array_values; +use function implode; +use function reset; +use function serialize; +use function sort; +use function sprintf; + class CyclicAliasException extends InvalidArgumentException { + /** + * @param string $alias conflicting alias key + * @param string[] $aliases map of referenced services, indexed by alias name (string) + * @return self + */ + public static function fromCyclicAlias($alias, array $aliases) + { + $cycle = $alias; + $cursor = $alias; + while (isset($aliases[$cursor]) && $aliases[$cursor] !== $alias) { + $cursor = $aliases[$cursor]; + $cycle .= ' -> '. $cursor; + } + $cycle .= ' -> ' . $alias . "\n"; + + return new self(sprintf( + "A cycle was detected within the aliases definitions:\n%s", + $cycle + )); + } + /** * @param string[] $aliases map of referenced services, indexed by alias name (string) - * * @return self */ public static function fromAliasesMap(array $aliases) @@ -43,7 +73,6 @@ function ($alias) use ($aliases) { * * @param string[] $aliases * @param string $alias - * * @return array|null */ private static function getCycleFor(array $aliases, $alias) @@ -57,7 +86,6 @@ private static function getCycleFor(array $aliases, $alias) } $cycleCandidate[$targetName] = true; - $targetName = $aliases[$targetName]; } @@ -66,7 +94,6 @@ private static function getCycleFor(array $aliases, $alias) /** * @param string[] $aliases - * * @return string */ private static function printReferencesMap(array $aliases) @@ -82,7 +109,6 @@ private static function printReferencesMap(array $aliases) /** * @param bool[][] $detectedCycles - * * @return string */ private static function printCycles(array $detectedCycles) @@ -92,7 +118,6 @@ private static function printCycles(array $detectedCycles) /** * @param bool[] $detectedCycle - * * @return string */ private static function printCycle(array $detectedCycle) @@ -113,7 +138,6 @@ function ($cycle) { /** * @param bool[][] $detectedCycles - * * @return bool[][] de-duplicated */ private static function deDuplicateDetectedCycles(array $detectedCycles) diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index e7b2c059..278bb06d 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -7,11 +7,11 @@ namespace Zend\ServiceManager\Exception; -use Interop\Container\Exception\ContainerException; +use Psr\Container\ContainerExceptionInterface; /** * Base exception for all Zend\ServiceManager exceptions. */ -interface ExceptionInterface extends ContainerException +interface ExceptionInterface extends ContainerExceptionInterface { } diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index 8fd3205a..4eb9e891 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -8,10 +8,44 @@ namespace Zend\ServiceManager\Exception; use InvalidArgumentException as SplInvalidArgumentException; +use Zend\ServiceManager\AbstractFactoryInterface; +use Zend\ServiceManager\Initializer\InitializerInterface; + +use function get_class; +use function gettype; +use function is_object; +use function sprintf; /** * @inheritDoc */ class InvalidArgumentException extends SplInvalidArgumentException implements ExceptionInterface { + /** + * @param mixed $initializer + * @return self + */ + public static function fromInvalidInitializer($initializer) + { + return new self(sprintf( + 'An invalid initializer was registered. Expected a callable or an' + . ' instance of "%s"; received "%s"', + InitializerInterface::class, + is_object($initializer) ? get_class($initializer) : gettype($initializer) + )); + } + + /** + * @param mixed $abstractFactory + * @return self + */ + public static function fromInvalidAbstractFactory($abstractFactory) + { + return new self(sprintf( + 'An invalid abstract factory was registered. Expected an instance of or a valid' + . ' class name resolving to an implementation of "%s", but "%s" was received.', + AbstractFactoryInterface::class, + is_object($abstractFactory) ? get_class($abstractFactory) : gettype($abstractFactory) + )); + } } diff --git a/src/Exception/ServiceNotCreatedException.php b/src/Exception/ServiceNotCreatedException.php index e056cfa3..54ac106a 100644 --- a/src/Exception/ServiceNotCreatedException.php +++ b/src/Exception/ServiceNotCreatedException.php @@ -7,15 +7,12 @@ namespace Zend\ServiceManager\Exception; -use Interop\Container\Exception\ContainerException; use RuntimeException as SplRuntimeException; /** * This exception is thrown when the service locator do not manage to create * the service (factory that has an error...) */ -class ServiceNotCreatedException extends SplRuntimeException implements - ContainerException, - ExceptionInterface +class ServiceNotCreatedException extends SplRuntimeException implements ExceptionInterface { } diff --git a/src/Exception/ServiceNotFoundException.php b/src/Exception/ServiceNotFoundException.php index bbbbe905..9171f201 100644 --- a/src/Exception/ServiceNotFoundException.php +++ b/src/Exception/ServiceNotFoundException.php @@ -7,8 +7,8 @@ namespace Zend\ServiceManager\Exception; -use Interop\Container\Exception\NotFoundException; use InvalidArgumentException as SplInvalidArgumentException; +use Psr\Container\NotFoundExceptionInterface; /** * This exception is thrown when the service locator do not manage to find a @@ -16,6 +16,6 @@ */ class ServiceNotFoundException extends SplInvalidArgumentException implements ExceptionInterface, - NotFoundException + NotFoundExceptionInterface { } diff --git a/src/Factory/AbstractFactoryInterface.php b/src/Factory/AbstractFactoryInterface.php index 147e7dc5..03a0e471 100644 --- a/src/Factory/AbstractFactoryInterface.php +++ b/src/Factory/AbstractFactoryInterface.php @@ -7,7 +7,7 @@ namespace Zend\ServiceManager\Factory; -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; /** * Interface for an abstract factory. diff --git a/src/Factory/DelegatorFactoryInterface.php b/src/Factory/DelegatorFactoryInterface.php index 14df71d4..02e69c21 100644 --- a/src/Factory/DelegatorFactoryInterface.php +++ b/src/Factory/DelegatorFactoryInterface.php @@ -7,8 +7,7 @@ namespace Zend\ServiceManager\Factory; -use Interop\Container\ContainerInterface; -use Interop\Container\Exception\ContainerException; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Exception\ServiceNotCreatedException; use Zend\ServiceManager\Exception\ServiceNotFoundException; diff --git a/src/Factory/FactoryInterface.php b/src/Factory/FactoryInterface.php index ffc5a04b..33084447 100644 --- a/src/Factory/FactoryInterface.php +++ b/src/Factory/FactoryInterface.php @@ -7,8 +7,7 @@ namespace Zend\ServiceManager\Factory; -use Interop\Container\ContainerInterface; -use Interop\Container\Exception\ContainerException; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Exception\ServiceNotCreatedException; use Zend\ServiceManager\Exception\ServiceNotFoundException; diff --git a/src/Factory/InvokableFactory.php b/src/Factory/InvokableFactory.php index a2927a40..85ac0b58 100644 --- a/src/Factory/InvokableFactory.php +++ b/src/Factory/InvokableFactory.php @@ -7,7 +7,7 @@ namespace Zend\ServiceManager\Factory; -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; /** * Factory for instantiating classes with no dependencies or which accept a single array. diff --git a/src/FactoryInterface.php b/src/FactoryInterface.php index 9118b27b..b488bace 100644 --- a/src/FactoryInterface.php +++ b/src/FactoryInterface.php @@ -16,7 +16,7 @@ * * - rename the method `createService()` to `__invoke()`, and: * - rename the `$serviceLocator` argument to `$container`, and change the - * typehint to `Interop\Container\ContainerInterface` + * typehint to `Psr\Container\ContainerInterface` * - add the `$requestedName` as a second argument * - add the optional `array $options = null` argument as a final argument * - create a `createService()` method as defined in this interface, and have it diff --git a/src/Initializer/InitializerInterface.php b/src/Initializer/InitializerInterface.php index 80fe3e97..24c059c4 100644 --- a/src/Initializer/InitializerInterface.php +++ b/src/Initializer/InitializerInterface.php @@ -7,7 +7,7 @@ namespace Zend\ServiceManager\Initializer; -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; /** * Interface for an initializer diff --git a/src/InitializerInterface.php b/src/InitializerInterface.php index 80338e9f..2f5f3646 100644 --- a/src/InitializerInterface.php +++ b/src/InitializerInterface.php @@ -16,7 +16,7 @@ * * - rename the method `initialize()` to `__invoke()`, and: * - rename the `$serviceLocator` argument to `$container`, and change the - * typehint to `Interop\Container\ContainerInterface` + * typehint to `Psr\Container\ContainerInterface` * - swap the order of the arguments (so that `$instance` comes second) * - create an `initialize()` method as defined in this interface, and have it * proxy to `__invoke()`, passing the arguments in the new order. diff --git a/src/PluginManagerInterface.php b/src/PluginManagerInterface.php index 54a2bc97..7392195e 100644 --- a/src/PluginManagerInterface.php +++ b/src/PluginManagerInterface.php @@ -7,7 +7,6 @@ namespace Zend\ServiceManager; -use Interop\Container\Exception\ContainerException; use Zend\ServiceManager\Exception\InvalidServiceException; /** diff --git a/src/Proxy/LazyServiceFactory.php b/src/Proxy/LazyServiceFactory.php index 838740c5..9f2c2446 100644 --- a/src/Proxy/LazyServiceFactory.php +++ b/src/Proxy/LazyServiceFactory.php @@ -7,12 +7,14 @@ namespace Zend\ServiceManager\Proxy; -use Interop\Container\ContainerInterface; use ProxyManager\Factory\LazyLoadingValueHolderFactory; use ProxyManager\Proxy\LazyLoadingInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Exception; use Zend\ServiceManager\Factory\DelegatorFactoryInterface; +use function sprintf; + /** * Delegator factory responsible of instantiating lazy loading value holder proxies of * given services at runtime diff --git a/src/ServiceLocatorInterface.php b/src/ServiceLocatorInterface.php index 34174c8f..ffcfe08d 100644 --- a/src/ServiceLocatorInterface.php +++ b/src/ServiceLocatorInterface.php @@ -7,16 +7,13 @@ namespace Zend\ServiceManager; -use Psr\Container\ContainerInterface as PsrContainerInterface; use Psr\Container\ContainerExceptionInterface; -use Interop\Container\ContainerInterface as InteropContainerInterface; +use Psr\Container\ContainerInterface; /** * Interface for service locator */ -interface ServiceLocatorInterface extends - PsrContainerInterface, - InteropContainerInterface +interface ServiceLocatorInterface extends ContainerInterface { /** * Build a service by its name, using optional options (such services are NEVER cached). diff --git a/src/ServiceManager.php b/src/ServiceManager.php index 4648e11a..2f8accdd 100644 --- a/src/ServiceManager.php +++ b/src/ServiceManager.php @@ -8,19 +8,33 @@ namespace Zend\ServiceManager; use Exception; -use Interop\Container\ContainerInterface; -use Interop\Container\Exception\ContainerException; use ProxyManager\Configuration as ProxyConfiguration; use ProxyManager\Factory\LazyLoadingValueHolderFactory; use ProxyManager\FileLocator\FileLocator; use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; use ProxyManager\GeneratorStrategy\FileWriterGeneratorStrategy; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Exception\ContainerModificationsNotAllowedException; use Zend\ServiceManager\Exception\CyclicAliasException; +use Zend\ServiceManager\Exception\ExceptionInterface; use Zend\ServiceManager\Exception\InvalidArgumentException; use Zend\ServiceManager\Exception\ServiceNotCreatedException; use Zend\ServiceManager\Exception\ServiceNotFoundException; +use function array_intersect; +use function array_merge_recursive; +use function class_exists; +use function get_class; +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; +use function sprintf; +use function trigger_error; + /** * Service Manager. * @@ -91,11 +105,6 @@ class ServiceManager implements ServiceLocatorInterface */ private $lazyServicesDelegator; - /** - * @var string[] - */ - private $resolvedAliases = []; - /** * A list of already loaded services (this act as a local cache) * @@ -175,43 +184,52 @@ public function getServiceLocator() */ public function get($name) { - $requestedName = $name; + // We start by checking if we have cached the requested service; + // this is the fastest method. + if (isset($this->services[$name])) { + return $this->services[$name]; + } + + // Determine if the service should be shared. + $sharedService = isset($this->shared[$name]) ? $this->shared[$name] : $this->sharedByDefault; + + // We achieve better performance if we can let all alias + // considerations out. + if (! $this->aliases) { + $object = $this->doCreate($name); - // We start by checking if we have cached the requested service (this - // is the fastest method). - if (isset($this->services[$requestedName])) { - return $this->services[$requestedName]; + // Cache the object for later, if it is supposed to be shared. + if ($sharedService) { + $this->services[$name] = $object; + } + return $object; } - $name = isset($this->resolvedAliases[$name]) ? $this->resolvedAliases[$name] : $name; + // We now deal with requests which may be aliases. + $resolvedName = isset($this->aliases[$name]) ? $this->aliases[$name] : $name; - // Next, if the alias should be shared, and we have cached the resolved - // service, use it. - if ($requestedName !== $name - && (! isset($this->shared[$requestedName]) || $this->shared[$requestedName]) - && isset($this->services[$name]) - ) { - $this->services[$requestedName] = $this->services[$name]; - return $this->services[$name]; + // The following is only true if the requested service is a shared alias. + $sharedAlias = $sharedService && isset($this->services[$resolvedName]); + + // If the alias is configured as a shared service, we are done. + if ($sharedAlias) { + $this->services[$name] = $this->services[$resolvedName]; + return $this->services[$resolvedName]; } - // At this point, we need to create the instance; we use the resolved - // name for that. - $object = $this->doCreate($name); + // At this point, we have to create the object. + // We use the resolved name for that. + $object = $this->doCreate($resolvedName); - // Cache it for later, if it is supposed to be shared. - if (($this->sharedByDefault && ! isset($this->shared[$name])) - || (isset($this->shared[$name]) && $this->shared[$name]) - ) { - $this->services[$name] = $object; + // Cache the object for later, if it is supposed to be shared. + if ($sharedService) { + $this->services[$resolvedName] = $object; } - // Also do so for aliases; this allows sharing based on service name used. - if ($requestedName !== $name - && (($this->sharedByDefault && ! isset($this->shared[$requestedName])) - || (isset($this->shared[$requestedName]) && $this->shared[$requestedName])) - ) { - $this->services[$requestedName] = $object; + // Also cache under the alias name; this allows sharing based on the + // service name used. + if ($sharedAlias) { + $this->services[$name] = $object; } return $object; @@ -222,8 +240,8 @@ public function get($name) */ public function build($name, array $options = null) { - // We never cache when using "build" - $name = isset($this->resolvedAliases[$name]) ? $this->resolvedAliases[$name] : $name; + // We never cache when using "build". + $name = $this->aliases[$name] ?? $name; return $this->doCreate($name, $options); } @@ -232,20 +250,36 @@ public function build($name, array $options = null) */ public function has($name) { - $name = isset($this->resolvedAliases[$name]) ? $this->resolvedAliases[$name] : $name; - $found = isset($this->services[$name]) || isset($this->factories[$name]); - - if ($found) { - return $found; + // Check services and factories first to speedup the most common requests. + if (isset($this->services[$name]) || isset($this->factories[$name])) { + return true; } - // Check abstract factories + // Check abstract factories next. foreach ($this->abstractFactories as $abstractFactory) { if ($abstractFactory->canCreate($this->creationContext, $name)) { return true; } } + // If $name is not an alias, we are done. + if (! isset($this->aliases[$name])) { + return false; + } + + // Check aliases. + $resolvedName = $this->aliases[$name]; + if (isset($this->services[$resolvedName]) || isset($this->factories[$resolvedName])) { + return true; + } + + // Check abstract factories on the $resolvedName as well. + foreach ($this->abstractFactories as $abstractFactory) { + if ($abstractFactory->canCreate($this->creationContext, $resolvedName)) { + return true; + } + } + return false; } @@ -311,25 +345,16 @@ public function getAllowOverride() */ public function configure(array $config) { - $this->validateOverrides($config); + // This is a bulk update/initial configuration, + // so we check all definitions up front. + $this->validateServiceNames($config); if (isset($config['services'])) { $this->services = $config['services'] + $this->services; } if (isset($config['invokables']) && ! empty($config['invokables'])) { - $aliases = $this->createAliasesForInvokables($config['invokables']); - $factories = $this->createFactoriesForInvokables($config['invokables']); - - if (! empty($aliases)) { - $config['aliases'] = (isset($config['aliases'])) - ? array_merge($config['aliases'], $aliases) - : $aliases; - } - - $config['factories'] = (isset($config['factories'])) - ? array_merge($config['factories'], $factories) - : $factories; + $this->createAliasesAndFactoriesForInvokables($config['invokables']); } if (isset($config['factories'])) { @@ -344,10 +369,11 @@ public function configure(array $config) $this->shared = $config['shared'] + $this->shared; } - if (isset($config['aliases'])) { - $this->configureAliases($config['aliases']); + if (! empty($config['aliases'])) { + $this->aliases = $config['aliases'] + $this->aliases; + $this->mapAliasesToTargets(); } elseif (! $this->configured && ! empty($this->aliases)) { - $this->resolveAliases($this->aliases); + $this->mapAliasesToTargets(); } if (isset($config['shared_by_default'])) { @@ -364,7 +390,11 @@ public function configure(array $config) // For abstract factories and initializers, we always directly // instantiate them to avoid checks during service construction. if (isset($config['abstract_factories'])) { - $this->resolveAbstractFactories($config['abstract_factories']); + $abstractFactories = $config['abstract_factories']; + // $key not needed, but foreach is faster than foreach + array_values. + foreach ($abstractFactories as $key => $abstractFactory) { + $this->resolveAbstractFactoryInstance($abstractFactory); + } } if (isset($config['initializers'])) { @@ -376,44 +406,21 @@ public function configure(array $config) return $this; } - /** - * @param string[] $aliases - * - * @return void - */ - private function configureAliases(array $aliases) - { - if (! $this->configured) { - $this->aliases = $aliases + $this->aliases; - - $this->resolveAliases($this->aliases); - - return; - } - - // Performance optimization. If there are no collisions, then we don't need to recompute loops - $intersecting = $this->aliases && \array_intersect_key($this->aliases, $aliases); - $this->aliases = $this->aliases ? \array_merge($this->aliases, $aliases) : $aliases; - - if ($intersecting) { - $this->resolveAliases($this->aliases); - - return; - } - - $this->resolveAliases($aliases); - $this->resolveNewAliasesWithPreviouslyResolvedAliases($aliases); - } - /** * 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) { - $this->configure(['aliases' => [$alias => $target]]); + if (isset($this->services[$alias]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($alias); + } + + $this->mapAliasToTarget($alias, $target); } /** @@ -422,10 +429,16 @@ public function setAlias($alias, $target) * @param string $name Service name * @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 setInvokableClass($name, $class = null) { - $this->configure(['invokables' => [$name => $class ?: $name]]); + if (isset($this->services[$name]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($name); + } + + $this->createAliasesAndFactoriesForInvokables([$name => $class ?? $name]); } /** @@ -434,10 +447,16 @@ public function setInvokableClass($name, $class = null) * @param string $name Service name * @param string|callable|Factory\FactoryInterface $factory Factory to which * to map. + * @throws ContainerModificationsNotAllowedException if $name already + * exists as a service and overrides are disallowed. */ public function setFactory($name, $factory) { - $this->configure(['factories' => [$name => $factory]]); + if (isset($this->services[$name]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($name); + } + + $this->factories[$name] = $factory; } /** @@ -455,11 +474,12 @@ public function mapLazyService($name, $class = null) /** * Add an abstract factory for resolving services. * - * @param string|Factory\AbstractFactoryInterface $factory Service name + * @param string|Factory\AbstractFactoryInterface $factory Abstract factory + * instance or class name. */ public function addAbstractFactory($factory) { - $this->configure(['abstract_factories' => [$factory]]); + $this->resolveAbstractFactoryInstance($factory); } /** @@ -489,10 +509,15 @@ public function addInitializer($initializer) * * @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) { - $this->configure(['services' => [$name => $service]]); + if (isset($this->services[$name]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($name); + } + $this->services[$name] = $service; } /** @@ -500,70 +525,22 @@ public function setService($name, $service) * * @param string $name Service name * @param boolean $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($name, $flag) { - $this->configure(['shared' => [$name => (bool) $flag]]); - } - - /** - * Instantiate abstract factories for to avoid checks during service construction. - * - * @param string[]|Factory\AbstractFactoryInterface[] $abstractFactories - * - * @return void - */ - private function resolveAbstractFactories(array $abstractFactories) - { - foreach ($abstractFactories as $abstractFactory) { - if (is_string($abstractFactory) && class_exists($abstractFactory)) { - //Cached string - if (! isset($this->cachedAbstractFactories[$abstractFactory])) { - $this->cachedAbstractFactories[$abstractFactory] = new $abstractFactory(); - } - - $abstractFactory = $this->cachedAbstractFactories[$abstractFactory]; - } - - if ($abstractFactory instanceof Factory\AbstractFactoryInterface) { - $abstractFactoryObjHash = spl_object_hash($abstractFactory); - $this->abstractFactories[$abstractFactoryObjHash] = $abstractFactory; - continue; - } - - // Error condition; let's find out why. - - // If we still have a string, we have a class name that does not resolve - if (is_string($abstractFactory)) { - throw new InvalidArgumentException( - sprintf( - 'An invalid abstract factory was registered; resolved to class "%s" ' . - 'which does not exist; please provide a valid class name resolving ' . - 'to an implementation of %s', - $abstractFactory, - AbstractFactoryInterface::class - ) - ); - } - - // Otherwise, we have an invalid type. - throw new InvalidArgumentException( - sprintf( - 'An invalid abstract factory was registered. Expected an instance of "%s", ' . - 'but "%s" was received', - AbstractFactoryInterface::class, - (is_object($abstractFactory) ? get_class($abstractFactory) : gettype($abstractFactory)) - ) - ); + if (isset($this->services[$name]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($name); } + + $this->shared[$name] = (bool) $flag; } /** * Instantiate initializers for to avoid checks during service construction. * * @param string[]|Initializer\InitializerInterface[]|callable[] $initializers - * - * @return void */ private function resolveInitializers(array $initializers) { @@ -574,77 +551,10 @@ private function resolveInitializers(array $initializers) if (is_callable($initializer)) { $this->initializers[] = $initializer; - continue; - } - - // Error condition; let's find out why. - - if (is_string($initializer)) { - throw new InvalidArgumentException( - sprintf( - 'An invalid initializer 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', - $initializer, - Initializer\InitializerInterface::class - ) - ); - } - - // Otherwise, we have an invalid type. - throw new InvalidArgumentException( - sprintf( - 'An invalid initializer was registered. Expected a callable, or an instance of ' . - '(or string class name resolving to) "%s", ' . - 'but "%s" was received', - Initializer\InitializerInterface::class, - (is_object($initializer) ? get_class($initializer) : gettype($initializer)) - ) - ); - } - } - - /** - * Resolve aliases to their canonical service names. - * - * @param string[] $aliases - * - * @returns void - */ - private function resolveAliases(array $aliases) - { - foreach ($aliases as $alias => $service) { - $visited = []; - /** @var string $name */ - $name = $alias; - - while (isset($this->aliases[$name])) { - if (isset($visited[$name])) { - throw CyclicAliasException::fromAliasesMap($aliases); - } - - $visited[$name] = true; - $name = $this->aliases[$name]; + return; } - $this->resolvedAliases[$alias] = $name; - } - } - - /** - * Rewrites the map of aliases by resolving the given $aliases with the existing resolved ones. - * This is mostly done for performance reasons. - * - * @param string[] $aliases - * - * @return void - */ - private function resolveNewAliasesWithPreviouslyResolvedAliases(array $aliases) - { - foreach ($this->resolvedAliases as $name => $target) { - if (isset($aliases[$target])) { - $this->resolvedAliases[$name] = $this->resolvedAliases[$target]; - } + throw InvalidArgumentException::fromInvalidInitializer($initializer); } } @@ -657,7 +567,7 @@ private function resolveNewAliasesWithPreviouslyResolvedAliases(array $aliases) */ private function getFactory($name) { - $factory = isset($this->factories[$name]) ? $this->factories[$name] : null; + $factory = $this->factories[$name] ?? null; $lazyLoaded = false; if (is_string($factory) && class_exists($factory)) { @@ -669,13 +579,7 @@ private function getFactory($name) if ($lazyLoaded) { $this->factories[$name] = $factory; } - // PHP 5.6 fails on 'class::method' callables unless we explode them: - if (PHP_MAJOR_VERSION < 7 - && is_string($factory) && strpos($factory, '::') !== false - ) { - /** @var callable $factory */ - $factory = explode('::', $factory); - } + return $factory; } @@ -719,9 +623,9 @@ private function createDelegatorFromName($name, array $options = null) if (! is_callable($delegatorFactory)) { 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', + '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 )); @@ -755,7 +659,7 @@ private function createDelegatorFromName($name, array $options = null) * @throws ServiceNotFoundException if unable to resolve the service. * @throws ServiceNotCreatedException if an exception is raised when * creating a service. - * @throws ContainerException if any other error occurs + * @throws ExceptionInterface if any other error occurs */ private function doCreate($resolvedName, array $options = null) { @@ -767,7 +671,7 @@ private function doCreate($resolvedName, array $options = null) } else { $object = $this->createDelegatorFromName($resolvedName, $options); } - } catch (ContainerException $exception) { + } catch (ExceptionInterface $exception) { throw $exception; } catch (Exception $exception) { throw new ServiceNotCreatedException(sprintf( @@ -833,146 +737,214 @@ private function createLazyServiceDelegatorFactory() } /** - * Create aliases for invokable classes. + * Create aliases and factories for invokable classes. * * If an invokable service name does not match the class it maps to, this * creates an alias to the class (which will later be mapped as an * invokable factory). * * @param array $invokables - * @return array */ - private function createAliasesForInvokables(array $invokables) + private function createAliasesAndFactoriesForInvokables(array $invokables) { - $aliases = []; foreach ($invokables as $name => $class) { - if ($name === $class) { - continue; + $this->factories[$class] = Factory\InvokableFactory::class; + if ($name !== $class) { + $this->aliases[$name] = $class; } - $aliases[$name] = $class; } - return $aliases; } /** - * Create invokable factories for invokable classes. + * Determine if a service for any name provided by a service + * manager configuration(services, aliases, factories, ...) + * already exists, and if it exists, determine if is it allowed + * to get overriden. * - * If an invokable service name does not match the class it maps to, this - * creates an invokable factory entry for the class name; otherwise, it - * creates an invokable factory for the entry name. - * - * @param array $invokables - * @return array - */ - private function createFactoriesForInvokables(array $invokables) - { - $factories = []; - foreach ($invokables as $name => $class) { - if ($name === $class) { - $factories[$name] = Factory\InvokableFactory::class; - continue; - } - - $factories[$class] = Factory\InvokableFactory::class; - } - return $factories; - } - - /** - * Determine if one or more services already exist in the container. - * - * If the allow override flag is true or it's first time configured, - * this method does nothing. - * - * Otherwise, it checks against each of the following service types, - * if present, and validates that none are defining services that - * already exist; if they do, it raises an exception indicating - * modification is not allowed. + * Validation in the context of this class means, that for + * a given service name we do not have a service instance + * in the cache OR override is explicitly allowed. * * @param array $config - * @throws ContainerModificationsNotAllowedException if any services - * provided already have instances available. + * @throws ContainerModificationsNotAllowedException if any + * service key is invalid. */ - private function validateOverrides(array $config) + private function validateServiceNames(array $config) { if ($this->allowOverride || ! $this->configured) { return; } if (isset($config['services'])) { - /** @var string[] $keys */ - $keys = array_keys($config['services']); - $this->validateOverrideSet($keys, 'service'); + foreach ($config['services'] as $service => $_) { + if (isset($this->services[$service]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($service); + } + } } if (isset($config['aliases'])) { - /** @var string[] $keys */ - $keys = array_keys($config['aliases']); - $this->validateOverrideSet($keys, 'alias'); + foreach ($config['aliases'] as $service => $_) { + if (isset($this->services[$service]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($service); + } + } } if (isset($config['invokables'])) { - /** @var string[] $keys */ - $keys = array_keys($config['invokables']); - $this->validateOverrideSet($keys, 'invokable class'); + foreach ($config['invokables'] as $service => $_) { + if (isset($this->services[$service]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($service); + } + } } if (isset($config['factories'])) { - /** @var string[] $keys */ - $keys = array_keys($config['factories']); - $this->validateOverrideSet($keys, 'factory'); + foreach ($config['factories'] as $service => $_) { + if (isset($this->services[$service]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($service); + } + } } if (isset($config['delegators'])) { - /** @var string[] $keys */ - $keys = array_keys($config['delegators']); - $this->validateOverrideSet($keys, 'delegator'); + foreach ($config['delegators'] as $service => $_) { + if (isset($this->services[$service]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($service); + } + } } if (isset($config['shared'])) { - /** @var string[] $keys */ - $keys = array_keys($config['shared']); - $this->validateOverrideSet($keys, 'sharing rule'); + foreach ($config['shared'] as $service => $_) { + if (isset($this->services[$service]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($service); + } + } } if (isset($config['lazy_services']['class_map'])) { - /** @var string[] $keys */ - $keys = array_keys($config['lazy_services']['class_map']); - $this->validateOverrideSet($keys, 'lazy service'); + foreach ($config['lazy_services']['class_map'] as $service => $_) { + if (isset($this->services[$service]) && ! $this->allowOverride) { + throw ContainerModificationsNotAllowedException::fromExistingService($service); + } + } } } /** - * Determine if one or more services already exist for a given type. + * Assuming that the alias name is valid (see above) resolve/add it. + * + * This is done differently from bulk mapping aliases for performance reasons, as the + * algorithms for mapping a single item efficiently are different from those of mapping + * many. * - * Loops through the provided service names, checking if any have current - * service instances; if not, it returns, but otherwise, it raises an - * exception indicating modification is not allowed. + * @see mapAliasesToTargets() below * - * @param string[] $services - * @param string $type Type of service being checked. - * @throws ContainerModificationsNotAllowedException if any services - * provided already have instances available. + * @param string $alias + * @param string $target */ - private function validateOverrideSet(array $services, $type) + private function mapAliasToTarget($alias, $target) { - $detected = []; - foreach ($services as $service) { - if (isset($this->services[$service])) { - $detected[] = $service; + // $target is either an alias or something else + // if it is an alias, resolve it + $this->aliases[$alias] = $this->aliases[$target] ?? $target; + + // a self-referencing alias indicates a cycle + if ($alias === $this->aliases[$alias]) { + throw CyclicAliasException::fromCyclicAlias($alias, $this->aliases); + } + + // finally we have to check if existing incomplete alias definitions + // exist which can get resolved by the new alias + if (in_array($alias, $this->aliases)) { + $r = array_intersect($this->aliases, [ $alias ]); + // found some, resolve them + foreach ($r as $name => $service) { + $this->aliases[$name] = $target; } } + } - if (empty($detected)) { - return; + /** + * Assuming that all provided alias keys are valid resolve them. + * + * This function maps $this->aliases in place. + * + * This algorithm is an adaptated version of Tarjans Strongly + * Connected Components. Instead of returning the strongly + * connected components (i.e. cycles in our case), we throw. + * If nodes are not strongly connected (i.e. resolvable in + * our case), they get resolved. + * + * This algorithm is fast for mass updates through configure(). + * It is not appropriate if just a single alias is added. + * + * @see mapAliasToTarget above + * + */ + private function mapAliasesToTargets() + { + $tagged = []; + foreach ($this->aliases as $alias => $target) { + if (isset($tagged[$alias])) { + continue; + } + + $tCursor = $this->aliases[$alias]; + $aCursor = $alias; + if ($aCursor === $tCursor) { + throw CyclicAliasException::fromCyclicAlias($alias, $this->aliases); + } + if (! isset($this->aliases[$tCursor])) { + continue; + } + + $stack = []; + + while (isset($this->aliases[$tCursor])) { + $stack[] = $aCursor; + if ($aCursor === $this->aliases[$tCursor]) { + throw CyclicAliasException::fromCyclicAlias($alias, $this->aliases); + } + $aCursor = $tCursor; + $tCursor = $this->aliases[$tCursor]; + } + + $tagged[$aCursor] = true; + + foreach ($stack as $alias) { + if ($alias === $tCursor) { + throw CyclicAliasException::fromCyclicAlias($alias, $this->aliases); + } + $this->aliases[$alias] = $tCursor; + $tagged[$alias] = true; + } } + } - throw new ContainerModificationsNotAllowedException(sprintf( - 'An updated/new %s is not allowed, as the container does not allow ' - . 'changes for services with existing instances; the following ' - . 'already exist in the container: %s', - $type, - implode(', ', $detected) - )); + /** + * Instantiate abstract factories in order to avoid checks during service construction. + * + * @param string|Factory\AbstractFactoryInterface $abstractFactories + * @return void + */ + private function resolveAbstractFactoryInstance($abstractFactory) + { + if (is_string($abstractFactory) && class_exists($abstractFactory)) { + // Cached string factory name + if (! isset($this->cachedAbstractFactories[$abstractFactory])) { + $this->cachedAbstractFactories[$abstractFactory] = new $abstractFactory(); + } + + $abstractFactory = $this->cachedAbstractFactories[$abstractFactory]; + } + + if (! $abstractFactory instanceof Factory\AbstractFactoryInterface) { + throw InvalidArgumentException::fromInvalidAbstractFactory($abstractFactory); + } + + $abstractFactoryObjHash = spl_object_hash($abstractFactory); + $this->abstractFactories[$abstractFactoryObjHash] = $abstractFactory; } } diff --git a/src/Test/CommonPluginManagerTrait.php b/src/Test/CommonPluginManagerTrait.php index a3d82ee7..5898cc24 100644 --- a/src/Test/CommonPluginManagerTrait.php +++ b/src/Test/CommonPluginManagerTrait.php @@ -11,6 +11,9 @@ use ReflectionProperty; use Zend\ServiceManager\Exception\InvalidServiceException; +use function get_class; +use function method_exists; + /** * Trait for testing plugin managers for v2-v3 compatibility * diff --git a/src/Tool/ConfigDumper.php b/src/Tool/ConfigDumper.php index 609c201e..1b358a09 100644 --- a/src/Tool/ConfigDumper.php +++ b/src/Tool/ConfigDumper.php @@ -7,13 +7,29 @@ namespace Zend\ServiceManager\Tool; -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use ReflectionClass; use ReflectionParameter; use Traversable; use Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory; use Zend\ServiceManager\Exception\InvalidArgumentException; +use function array_filter; +use function array_key_exists; +use function class_exists; +use function date; +use function get_class; +use function gettype; +use function implode; +use function interface_exists; +use function is_array; +use function is_int; +use function is_null; +use function is_string; +use function sprintf; +use function str_repeat; +use function var_export; + class ConfigDumper { const CONFIG_TEMPLATE = <<createHelpArgument(); } @@ -129,7 +139,7 @@ private function parseArgs(array $args) $arg1 = array_shift($args); } - if (! count($args)) { + if (! $args) { return $this->createErrorArgument('Missing class name'); } diff --git a/src/Tool/FactoryCreator.php b/src/Tool/FactoryCreator.php index dbd60bd5..1b6bb00c 100644 --- a/src/Tool/FactoryCreator.php +++ b/src/Tool/FactoryCreator.php @@ -11,6 +11,17 @@ use ReflectionParameter; use Zend\ServiceManager\Exception\InvalidArgumentException; +use function array_filter; +use function array_map; +use function array_shift; +use function count; +use function implode; +use function sprintf; +use function str_repeat; +use function str_replace; +use function strrpos; +use function substr; + class FactoryCreator { const FACTORY_TEMPLATE = <<<'EOT' @@ -18,7 +29,7 @@ class FactoryCreator namespace %s; -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Factory\FactoryInterface; use %s; diff --git a/src/Tool/FactoryCreatorCommand.php b/src/Tool/FactoryCreatorCommand.php index 075407a3..3bcffb85 100644 --- a/src/Tool/FactoryCreatorCommand.php +++ b/src/Tool/FactoryCreatorCommand.php @@ -10,6 +10,11 @@ use Zend\ServiceManager\Exception; use Zend\Stdlib\ConsoleHelper; +use function array_shift; +use function class_exists; +use function in_array; +use function sprintf; + class FactoryCreatorCommand { const COMMAND_DUMP = 'dump'; @@ -98,7 +103,7 @@ public function __invoke(array $args) */ private function parseArgs(array $args) { - if (! count($args)) { + if (! $args) { return $this->createArguments(self::COMMAND_HELP); } diff --git a/test/AbstractFactory/ConfigAbstractFactoryTest.php b/test/AbstractFactory/ConfigAbstractFactoryTest.php index b48b5186..d6d7c5bd 100644 --- a/test/AbstractFactory/ConfigAbstractFactoryTest.php +++ b/test/AbstractFactory/ConfigAbstractFactoryTest.php @@ -184,8 +184,8 @@ public function testExceptsWhenConfigNotSet() { $abstractFactory = new ConfigAbstractFactory(); $serviceManager = new ServiceManager(); - self::expectException(ServiceNotCreatedException::class); - self::expectExceptionMessage('Cannot find a config array in the container'); + $this->expectException(ServiceNotCreatedException::class); + $this->expectExceptionMessage('Cannot find a config array in the container'); $abstractFactory($serviceManager, 'Dirk_Gently'); } @@ -195,8 +195,8 @@ public function testExceptsWhenConfigKeyNotSet() $abstractFactory = new ConfigAbstractFactory(); $serviceManager = new ServiceManager(); $serviceManager->setService('config', []); - self::expectException(ServiceNotCreatedException::class); - self::expectExceptionMessage('Cannot find a `' . ConfigAbstractFactory::class . '` key in the config array'); + $this->expectException(ServiceNotCreatedException::class); + $this->expectExceptionMessage('Cannot find a `' . ConfigAbstractFactory::class . '` key in the config array'); $abstractFactory($serviceManager, 'Dirk_Gently'); } @@ -206,8 +206,8 @@ public function testExceptsWhenConfigIsNotArray() $abstractFactory = new ConfigAbstractFactory(); $serviceManager = new ServiceManager(); $serviceManager->setService('config', 'Holistic'); - self::expectException(ServiceNotCreatedException::class); - self::expectExceptionMessage('Config must be an array'); + $this->expectException(ServiceNotCreatedException::class); + $this->expectExceptionMessage('Config must be an array'); $abstractFactory($serviceManager, 'Dirk_Gently'); } @@ -222,8 +222,8 @@ public function testExceptsWhenServiceConfigIsNotArray() ConfigAbstractFactory::class => 'Detective_Agency' ] ); - self::expectException(ServiceNotCreatedException::class); - self::expectExceptionMessage('Dependencies config must exist and be an array'); + $this->expectException(ServiceNotCreatedException::class); + $this->expectExceptionMessage('Dependencies config must exist and be an array'); $abstractFactory($serviceManager, 'Dirk_Gently'); } @@ -238,8 +238,8 @@ public function testExceptsWhenServiceConfigDoesNotExist() ConfigAbstractFactory::class => [], ] ); - self::expectException(ServiceNotCreatedException::class); - self::expectExceptionMessage('Dependencies config must exist and be an array'); + $this->expectException(ServiceNotCreatedException::class); + $this->expectExceptionMessage('Dependencies config must exist and be an array'); $abstractFactory($serviceManager, 'Dirk_Gently'); } @@ -256,8 +256,8 @@ public function testExceptsWhenServiceConfigForRequestedNameIsNotArray() ], ] ); - self::expectException(ServiceNotCreatedException::class); - self::expectExceptionMessage('Dependencies config must exist and be an array'); + $this->expectException(ServiceNotCreatedException::class); + $this->expectExceptionMessage('Dependencies config must exist and be an array'); $abstractFactory($serviceManager, 'Dirk_Gently'); } @@ -279,8 +279,8 @@ public function testExceptsWhenServiceConfigForRequestedNameIsNotArrayOfStrings( ], ] ); - self::expectException(ServiceNotCreatedException::class); - self::expectExceptionMessage( + $this->expectException(ServiceNotCreatedException::class); + $this->expectExceptionMessage( 'Service message must be an array of strings, ["string","string","string","integer"] given' ); diff --git a/test/AbstractFactory/ReflectionBasedAbstractFactoryTest.php b/test/AbstractFactory/ReflectionBasedAbstractFactoryTest.php index 94f4acd6..254d2563 100644 --- a/test/AbstractFactory/ReflectionBasedAbstractFactoryTest.php +++ b/test/AbstractFactory/ReflectionBasedAbstractFactoryTest.php @@ -8,11 +8,13 @@ namespace ZendTest\ServiceManager\AbstractFactory; use ArrayAccess; -use Interop\Container\ContainerInterface; use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory; use Zend\ServiceManager\Exception\ServiceNotFoundException; +use function sprintf; + class ReflectionBasedAbstractFactoryTest extends TestCase { public function setUp() @@ -33,21 +35,21 @@ public function nonClassRequestedNames() public function testCanCreateReturnsFalseForNonClassRequestedNames($requestedName) { $factory = new ReflectionBasedAbstractFactory(); - $this->assertFalse($factory->canCreate($this->container->reveal(), $requestedName)); + self::assertFalse($factory->canCreate($this->container->reveal(), $requestedName)); } public function testFactoryInstantiatesClassDirectlyIfItHasNoConstructor() { $factory = new ReflectionBasedAbstractFactory(); $instance = $factory($this->container->reveal(), TestAsset\ClassWithNoConstructor::class); - $this->assertInstanceOf(TestAsset\ClassWithNoConstructor::class, $instance); + self::assertInstanceOf(TestAsset\ClassWithNoConstructor::class, $instance); } public function testFactoryInstantiatesClassDirectlyIfConstructorHasNoArguments() { $factory = new ReflectionBasedAbstractFactory(); $instance = $factory($this->container->reveal(), TestAsset\ClassWithEmptyConstructor::class); - $this->assertInstanceOf(TestAsset\ClassWithEmptyConstructor::class, $instance); + self::assertInstanceOf(TestAsset\ClassWithEmptyConstructor::class, $instance); } public function testFactoryRaisesExceptionWhenUnableToResolveATypeHintedService() @@ -72,7 +74,7 @@ public function testFactoryRaisesExceptionForScalarParameters() 'Unable to create service "%s"; unable to resolve parameter "foo" to a class, interface, or array type', TestAsset\ClassWithScalarParameters::class )); - $instance = $factory($this->container->reveal(), TestAsset\ClassWithScalarParameters::class); + $factory($this->container->reveal(), TestAsset\ClassWithScalarParameters::class); } public function testFactoryInjectsConfigServiceForConfigArgumentsTypeHintedAsArray() @@ -83,8 +85,8 @@ public function testFactoryInjectsConfigServiceForConfigArgumentsTypeHintedAsArr $factory = new ReflectionBasedAbstractFactory(); $instance = $factory($this->container->reveal(), TestAsset\ClassAcceptingConfigToConstructor::class); - $this->assertInstanceOf(TestAsset\ClassAcceptingConfigToConstructor::class, $instance); - $this->assertEquals($config, $instance->config); + self::assertInstanceOf(TestAsset\ClassAcceptingConfigToConstructor::class, $instance); + self::assertEquals($config, $instance->config); } public function testFactoryCanInjectKnownTypeHintedServices() @@ -96,8 +98,8 @@ public function testFactoryCanInjectKnownTypeHintedServices() $factory = new ReflectionBasedAbstractFactory(); $instance = $factory($this->container->reveal(), TestAsset\ClassWithTypeHintedConstructorParameter::class); - $this->assertInstanceOf(TestAsset\ClassWithTypeHintedConstructorParameter::class, $instance); - $this->assertSame($sample, $instance->sample); + self::assertInstanceOf(TestAsset\ClassWithTypeHintedConstructorParameter::class, $instance); + self::assertSame($sample, $instance->sample); } public function testFactoryResolvesTypeHintsForServicesToWellKnownServiceNames() @@ -113,11 +115,11 @@ public function testFactoryResolvesTypeHintsForServicesToWellKnownServiceNames() $this->container->reveal(), TestAsset\ClassAcceptingWellKnownServicesAsConstructorParameters::class ); - $this->assertInstanceOf( + self::assertInstanceOf( TestAsset\ClassAcceptingWellKnownServicesAsConstructorParameters::class, $instance ); - $this->assertSame($validators, $instance->validators); + self::assertSame($validators, $instance->validators); } public function testFactoryCanSupplyAMixOfParameterTypes() @@ -136,12 +138,12 @@ public function testFactoryCanSupplyAMixOfParameterTypes() $factory = new ReflectionBasedAbstractFactory([TestAsset\ValidatorPluginManager::class => 'ValidatorManager']); $instance = $factory($this->container->reveal(), TestAsset\ClassWithMixedConstructorParameters::class); - $this->assertInstanceOf(TestAsset\ClassWithMixedConstructorParameters::class, $instance); + self::assertInstanceOf(TestAsset\ClassWithMixedConstructorParameters::class, $instance); - $this->assertEquals($config, $instance->config); - $this->assertEquals([], $instance->options); - $this->assertSame($sample, $instance->sample); - $this->assertSame($validators, $instance->validators); + self::assertEquals($config, $instance->config); + self::assertEquals([], $instance->options); + self::assertSame($sample, $instance->sample); + self::assertSame($validators, $instance->validators); } public function testFactoryWillUseDefaultValueWhenPresentForScalarArgument() @@ -152,8 +154,8 @@ public function testFactoryWillUseDefaultValueWhenPresentForScalarArgument() $this->container->reveal(), TestAsset\ClassWithScalarDependencyDefiningDefaultValue::class ); - $this->assertInstanceOf(TestAsset\ClassWithScalarDependencyDefiningDefaultValue::class, $instance); - $this->assertEquals('bar', $instance->foo); + self::assertInstanceOf(TestAsset\ClassWithScalarDependencyDefiningDefaultValue::class, $instance); + self::assertEquals('bar', $instance->foo); } /** diff --git a/test/AbstractPluginManagerTest.php b/test/AbstractPluginManagerTest.php index 9d8884aa..5329092a 100644 --- a/test/AbstractPluginManagerTest.php +++ b/test/AbstractPluginManagerTest.php @@ -7,8 +7,8 @@ namespace ZendTest\ServiceManager; -use Interop\Container\ContainerInterface; use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; use stdClass; use Zend\ServiceManager\ConfigInterface; use Zend\ServiceManager\Exception\InvalidArgumentException; @@ -22,6 +22,10 @@ use ZendTest\ServiceManager\TestAsset\SimplePluginManager; use ZendTest\ServiceManager\TestAsset\V2v3PluginManager; +use function get_class; +use function restore_error_handler; +use function set_error_handler; + /** * @covers \Zend\ServiceManager\AbstractPluginManager */ @@ -57,7 +61,7 @@ public function testInjectCreationContextInFactories() $object = $pluginManager->get(InvokableObject::class); - $this->assertInstanceOf(InvokableObject::class, $object); + self::assertInstanceOf(InvokableObject::class, $object); } public function testValidateInstance() @@ -95,9 +99,9 @@ public function testCachesInstanceByDefaultIfNoOptionsArePassed() $first = $pluginManager->get(InvokableObject::class); $second = $pluginManager->get(InvokableObject::class); - $this->assertInstanceOf(InvokableObject::class, $first); - $this->assertInstanceOf(InvokableObject::class, $second); - $this->assertSame($first, $second); + self::assertInstanceOf(InvokableObject::class, $first); + self::assertInstanceOf(InvokableObject::class, $second); + self::assertSame($first, $second); } public function shareByDefaultSettings() @@ -127,9 +131,9 @@ public function testReturnsDiscreteInstancesIfOptionsAreProvidedRegardlessOfShar $first = $pluginManager->get(InvokableObject::class, $options); $second = $pluginManager->get(InvokableObject::class, $options); - $this->assertInstanceOf(InvokableObject::class, $first); - $this->assertInstanceOf(InvokableObject::class, $second); - $this->assertNotSame($first, $second); + self::assertInstanceOf(InvokableObject::class, $first); + self::assertInstanceOf(InvokableObject::class, $second); + self::assertNotSame($first, $second); } /** @@ -164,13 +168,13 @@ function ($container, $name, $callback) { ]); $instance = $pluginManager->get(stdClass::class); - $this->assertTrue(isset($instance->option), 'Delegator-injected option was not found'); - $this->assertEquals( + self::assertTrue(isset($instance->option), 'Delegator-injected option was not found'); + self::assertEquals( $config['option'], $instance->option, 'Delegator-injected option does not match configuration' ); - $this->assertEquals('bar', $instance->foo); + self::assertEquals('bar', $instance->foo); } /** @@ -192,21 +196,21 @@ public function testGetRaisesExceptionWhenNoFactoryIsResolved() public function testCallingSetServiceLocatorSetsCreationContextWithDeprecationNotice() { set_error_handler(function ($errno, $errstr) { - $this->assertEquals(E_USER_DEPRECATED, $errno); + self::assertEquals(E_USER_DEPRECATED, $errno); }, E_USER_DEPRECATED); $pluginManager = new TestAsset\LenientPluginManager(); restore_error_handler(); - $this->assertAttributeSame($pluginManager, 'creationContext', $pluginManager); + self::assertAttributeSame($pluginManager, 'creationContext', $pluginManager); $serviceManager = new ServiceManager(); set_error_handler(function ($errno, $errstr) { - $this->assertEquals(E_USER_DEPRECATED, $errno); + self::assertEquals(E_USER_DEPRECATED, $errno); }, E_USER_DEPRECATED); $pluginManager->setServiceLocator($serviceManager); restore_error_handler(); - $this->assertAttributeSame($serviceManager, 'creationContext', $pluginManager); + self::assertAttributeSame($serviceManager, 'creationContext', $pluginManager); } /** @@ -215,11 +219,11 @@ public function testCallingSetServiceLocatorSetsCreationContextWithDeprecationNo public function testPassingNoInitialConstructorArgumentSetsPluginManagerAsCreationContextWithDeprecationNotice() { set_error_handler(function ($errno, $errstr) { - $this->assertEquals(E_USER_DEPRECATED, $errno); + self::assertEquals(E_USER_DEPRECATED, $errno); }, E_USER_DEPRECATED); $pluginManager = new TestAsset\LenientPluginManager(); restore_error_handler(); - $this->assertAttributeSame($pluginManager, 'creationContext', $pluginManager); + self::assertAttributeSame($pluginManager, 'creationContext', $pluginManager); } /** @@ -231,12 +235,12 @@ public function testCanPassConfigInterfaceAsFirstConstructorArgumentWithDeprecat $config->toArray()->willReturn([]); set_error_handler(function ($errno, $errstr) { - $this->assertEquals(E_USER_DEPRECATED, $errno); + self::assertEquals(E_USER_DEPRECATED, $errno); }, E_USER_DEPRECATED); $pluginManager = new TestAsset\LenientPluginManager($config->reveal()); restore_error_handler(); - $this->assertAttributeSame($pluginManager, 'creationContext', $pluginManager); + self::assertAttributeSame($pluginManager, 'creationContext', $pluginManager); } public function invalidConstructorArguments() @@ -273,12 +277,12 @@ public function testPassingConfigInstanceAsFirstConstructorArgumentSkipsSecondAr $config->toArray()->willReturn(['services' => [__CLASS__ => $this]]); set_error_handler(function ($errno, $errstr) { - $this->assertEquals(E_USER_DEPRECATED, $errno); + self::assertEquals(E_USER_DEPRECATED, $errno); }, E_USER_DEPRECATED); $pluginManager = new TestAsset\LenientPluginManager($config->reveal(), ['services' => [__CLASS__ => []]]); restore_error_handler(); - $this->assertSame($this, $pluginManager->get(__CLASS__)); + self::assertSame($this, $pluginManager->get(__CLASS__)); } /** @@ -288,7 +292,7 @@ public function testPassingConfigInstanceAsFirstConstructorArgumentSkipsSecondAr public function testAutoInvokableServicesAreNotKnownBeforeRetrieval() { $pluginManager = new TestAsset\SimplePluginManager(new ServiceManager()); - $this->assertFalse($pluginManager->has(TestAsset\InvokableObject::class)); + self::assertFalse($pluginManager->has(TestAsset\InvokableObject::class)); } /** @@ -299,7 +303,7 @@ public function testSupportsRetrievingAutoInvokableServicesByDefault() { $pluginManager = new TestAsset\SimplePluginManager(new ServiceManager()); $invokable = $pluginManager->get(TestAsset\InvokableObject::class); - $this->assertInstanceOf(TestAsset\InvokableObject::class, $invokable); + self::assertInstanceOf(TestAsset\InvokableObject::class, $invokable); } /** @@ -322,7 +326,7 @@ public function testValidateWillFallBackToValidatePluginWhenDefinedAndEmitDeprec $assertionCalled = false; $instance = (object) []; $assertion = function ($plugin) use ($instance, &$assertionCalled) { - $this->assertSame($instance, $plugin); + self::assertSame($instance, $plugin); $assertionCalled = true; }; $pluginManager = new TestAsset\V2ValidationPluginManager(new ServiceManager()); @@ -330,15 +334,15 @@ public function testValidateWillFallBackToValidatePluginWhenDefinedAndEmitDeprec $errorHandlerCalled = false; set_error_handler(function ($errno, $errmsg) use (&$errorHandlerCalled) { - $this->assertEquals(E_USER_DEPRECATED, $errno); - $this->assertContains('3.0', $errmsg); + self::assertEquals(E_USER_DEPRECATED, $errno); + self::assertContains('3.0', $errmsg); $errorHandlerCalled = true; }, E_USER_DEPRECATED); $pluginManager->validate($instance); restore_error_handler(); - $this->assertTrue($assertionCalled, 'Assertion was not called by validatePlugin!'); - $this->assertTrue($errorHandlerCalled, 'Error handler was not triggered by validatePlugin!'); + self::assertTrue($assertionCalled, 'Assertion was not called by validatePlugin!'); + self::assertTrue($errorHandlerCalled, 'Error handler was not triggered by validatePlugin!'); } public function testSetServiceShouldRaiseExceptionForInvalidPlugin() @@ -371,12 +375,12 @@ public function testAbstractFactoryGetsCreationContext() $abstractFactory->__invoke($serviceManager, 'foo', null) ->willReturn(new InvokableObject()); $pluginManager->addAbstractFactory($abstractFactory->reveal()); - $this->assertInstanceOf(InvokableObject::class, $pluginManager->get('foo')); + self::assertInstanceOf(InvokableObject::class, $pluginManager->get('foo')); } public function testAliasPropertyResolves() { $pluginManager = new V2v3PluginManager(new ServiceManager()); - $this->assertInstanceOf(InvokableObject::class, $pluginManager->get('foo')); + self::assertInstanceOf(InvokableObject::class, $pluginManager->get('foo')); } } diff --git a/test/CommonServiceLocatorBehaviorsTrait.php b/test/CommonServiceLocatorBehaviorsTrait.php index 2350e090..53862b6e 100644 --- a/test/CommonServiceLocatorBehaviorsTrait.php +++ b/test/CommonServiceLocatorBehaviorsTrait.php @@ -8,7 +8,7 @@ namespace ZendTest\ServiceManager; use DateTime; -use Interop\Container\Exception\ContainerException; +use Psr\Container\ContainerExceptionInterface; use ReflectionProperty; use stdClass; use Zend\ServiceManager\Exception\ContainerModificationsNotAllowedException; @@ -19,13 +19,23 @@ use Zend\ServiceManager\Factory\InvokableFactory; use Zend\ServiceManager\Initializer\InitializerInterface; use Zend\ServiceManager\ServiceLocatorInterface; +use ZendTest\ServiceManager\TestAsset\AbstractFactoryFoo; use ZendTest\ServiceManager\TestAsset\CallTimesAbstractFactory; use ZendTest\ServiceManager\TestAsset\FailingAbstractFactory; -use ZendTest\ServiceManager\TestAsset\FailingFactory; use ZendTest\ServiceManager\TestAsset\FailingExceptionWithStringAsCodeFactory; +use ZendTest\ServiceManager\TestAsset\FailingFactory; use ZendTest\ServiceManager\TestAsset\InvokableObject; +use ZendTest\ServiceManager\TestAsset\PassthroughDelegatorFactory; +use ZendTest\ServiceManager\TestAsset\SampleFactory; use ZendTest\ServiceManager\TestAsset\SimpleAbstractFactory; +use function array_fill_keys; +use function array_keys; +use function array_merge; +use function call_user_func_array; +use function restore_error_handler; +use function set_error_handler; + trait CommonServiceLocatorBehaviorsTrait { /** @@ -46,7 +56,7 @@ public function testIsSharedByDefault() $object1 = $serviceManager->get(stdClass::class); $object2 = $serviceManager->get(stdClass::class); - $this->assertSame($object1, $object2); + self::assertSame($object1, $object2); } public function testCanDisableSharedByDefault() @@ -61,7 +71,7 @@ public function testCanDisableSharedByDefault() $object1 = $serviceManager->get(stdClass::class); $object2 = $serviceManager->get(stdClass::class); - $this->assertNotSame($object1, $object2); + self::assertNotSame($object1, $object2); } public function testCanDisableSharedForSingleService() @@ -78,7 +88,7 @@ public function testCanDisableSharedForSingleService() $object1 = $serviceManager->get(stdClass::class); $object2 = $serviceManager->get(stdClass::class); - $this->assertNotSame($object1, $object2); + self::assertNotSame($object1, $object2); } public function testCanEnableSharedForSingleService() @@ -96,7 +106,7 @@ public function testCanEnableSharedForSingleService() $object1 = $serviceManager->get(stdClass::class); $object2 = $serviceManager->get(stdClass::class); - $this->assertSame($object1, $object2); + self::assertSame($object1, $object2); } public function testCanBuildObjectWithInvokableFactory() @@ -109,8 +119,8 @@ public function testCanBuildObjectWithInvokableFactory() $object = $serviceManager->build(InvokableObject::class, ['foo' => 'bar']); - $this->assertInstanceOf(InvokableObject::class, $object); - $this->assertEquals(['foo' => 'bar'], $object->options); + self::assertInstanceOf(InvokableObject::class, $object); + self::assertEquals(['foo' => 'bar'], $object->options); } public function testCanCreateObjectWithClosureFactory() @@ -118,14 +128,14 @@ public function testCanCreateObjectWithClosureFactory() $serviceManager = $this->createContainer([ 'factories' => [ stdClass::class => function (ServiceLocatorInterface $serviceLocator, $className) { - $this->assertEquals(stdClass::class, $className); + self::assertEquals(stdClass::class, $className); return new stdClass(); } ] ]); $object = $serviceManager->get(stdClass::class); - $this->assertInstanceOf(stdClass::class, $object); + self::assertInstanceOf(stdClass::class, $object); } public function testCanCreateServiceWithAbstractFactory() @@ -136,7 +146,7 @@ public function testCanCreateServiceWithAbstractFactory() ] ]); - $this->assertInstanceOf(DateTime::class, $serviceManager->get(DateTime::class)); + self::assertInstanceOf(DateTime::class, $serviceManager->get(DateTime::class)); } public function testAllowsMultipleInstancesOfTheSameAbstractFactory() @@ -156,7 +166,7 @@ public function testAllowsMultipleInstancesOfTheSameAbstractFactory() $serviceManager->addAbstractFactory($obj2); $serviceManager->has(stdClass::class); - $this->assertEquals(2, CallTimesAbstractFactory::getCallTimes()); + self::assertEquals(2, CallTimesAbstractFactory::getCallTimes()); } public function testWillReUseAnExistingNamedAbstractFactoryInstance() @@ -172,7 +182,7 @@ public function testWillReUseAnExistingNamedAbstractFactoryInstance() $serviceManager->addAbstractFactory(CallTimesAbstractFactory::class); $serviceManager->has(stdClass::class); - $this->assertEquals(1, CallTimesAbstractFactory::getCallTimes()); + self::assertEquals(1, CallTimesAbstractFactory::getCallTimes()); } public function testCanCreateServiceWithAlias() @@ -189,9 +199,9 @@ public function testCanCreateServiceWithAlias() $object = $serviceManager->get('bar'); - $this->assertInstanceOf(InvokableObject::class, $object); - $this->assertTrue($serviceManager->has('bar')); - $this->assertFalse($serviceManager->has('baz')); + self::assertInstanceOf(InvokableObject::class, $object); + self::assertTrue($serviceManager->has('bar')); + self::assertFalse($serviceManager->has('baz')); } public function testCheckingServiceExistenceWithChecksAgainstAbstractFactories() @@ -205,8 +215,8 @@ public function testCheckingServiceExistenceWithChecksAgainstAbstractFactories() ] ]); - $this->assertTrue($serviceManager->has(stdClass::class)); - $this->assertTrue($serviceManager->has(DateTime::class)); + self::assertTrue($serviceManager->has(stdClass::class)); + self::assertTrue($serviceManager->has(DateTime::class)); } public function testBuildNeverSharesInstances() @@ -223,7 +233,7 @@ public function testBuildNeverSharesInstances() $object1 = $serviceManager->build(stdClass::class); $object2 = $serviceManager->build(stdClass::class, ['foo' => 'bar']); - $this->assertNotSame($object1, $object2); + self::assertNotSame($object1, $object2); } public function testInitializersAreRunAfterCreation() @@ -284,8 +294,8 @@ public function testConfigureCanAddNewServices() ] ]); - $this->assertTrue($serviceManager->has(DateTime::class)); - $this->assertFalse($serviceManager->has(stdClass::class)); + self::assertTrue($serviceManager->has(DateTime::class)); + self::assertFalse($serviceManager->has(stdClass::class)); $newServiceManager = $serviceManager->configure([ 'factories' => [ @@ -293,10 +303,10 @@ public function testConfigureCanAddNewServices() ] ]); - $this->assertSame($serviceManager, $newServiceManager); + self::assertSame($serviceManager, $newServiceManager); - $this->assertTrue($newServiceManager->has(DateTime::class)); - $this->assertTrue($newServiceManager->has(stdClass::class)); + self::assertTrue($newServiceManager->has(DateTime::class)); + self::assertTrue($newServiceManager->has(stdClass::class)); } public function testConfigureCanOverridePreviousSettings() @@ -318,7 +328,7 @@ public function testConfigureCanOverridePreviousSettings() ] ]); - $this->assertSame($serviceManager, $newServiceManager); + self::assertSame($serviceManager, $newServiceManager); $firstFactory->expects($this->never())->method('__invoke'); $secondFactory->expects($this->once())->method('__invoke'); @@ -336,7 +346,7 @@ public function testHasReturnsFalseIfServiceNotConfigured() stdClass::class => InvokableFactory::class, ], ]); - $this->assertFalse($serviceManager->has('Some\Made\Up\Entry')); + self::assertFalse($serviceManager->has('Some\Made\Up\Entry')); } /** @@ -349,7 +359,7 @@ public function testHasReturnsTrueIfServiceIsConfigured() stdClass::class => new stdClass, ], ]); - $this->assertTrue($serviceManager->has(stdClass::class)); + self::assertTrue($serviceManager->has(stdClass::class)); } /** @@ -362,7 +372,7 @@ public function testHasReturnsTrueIfFactoryIsConfigured() stdClass::class => InvokableFactory::class, ], ]); - $this->assertTrue($serviceManager->has(stdClass::class)); + self::assertTrue($serviceManager->has(stdClass::class)); } public function abstractFactories() @@ -385,7 +395,7 @@ public function testHasChecksAgainstAbstractFactories($abstractFactory, $expecte ], ]); - $this->assertSame($expected, $serviceManager->has(DateTime::class)); + self::assertSame($expected, $serviceManager->has(DateTime::class)); } /** @@ -431,40 +441,40 @@ function ($container, $instance) { ]); $dateTime = $serviceManager->get(DateTime::class); - $this->assertInstanceOf(DateTime::class, $dateTime, 'DateTime service did not resolve as expected'); + self::assertInstanceOf(DateTime::class, $dateTime, 'DateTime service did not resolve as expected'); $notShared = $serviceManager->get(DateTime::class); - $this->assertInstanceOf(DateTime::class, $notShared, 'DateTime service did not re-resolve as expected'); - $this->assertNotSame( + self::assertInstanceOf(DateTime::class, $notShared, 'DateTime service did not re-resolve as expected'); + self::assertNotSame( $dateTime, $notShared, 'Expected unshared instances for DateTime service but received shared instances' ); $config = $serviceManager->get('config'); - $this->assertInternalType('array', $config, 'Config service did not resolve as expected'); - $this->assertSame( + self::assertInternalType('array', $config, 'Config service did not resolve as expected'); + self::assertSame( $config, $serviceManager->get('config'), 'Config service resolved as unshared instead of shared' ); $stdClass = $serviceManager->get(stdClass::class); - $this->assertInstanceOf(stdClass::class, $stdClass, 'stdClass service did not resolve as expected'); - $this->assertSame( + self::assertInstanceOf(stdClass::class, $stdClass, 'stdClass service did not resolve as expected'); + self::assertSame( $stdClass, $serviceManager->get(stdClass::class), 'stdClass service should be shared, but resolved as unshared' ); - $this->assertTrue( + self::assertTrue( isset($stdClass->foo), 'Expected delegator to inject "foo" property in stdClass service, but it was not' ); - $this->assertEquals('bar', $stdClass->foo, 'stdClass "foo" property was not injected correctly'); - $this->assertTrue( + self::assertEquals('bar', $stdClass->foo, 'stdClass "foo" property was not injected correctly'); + self::assertTrue( isset($stdClass->bar), 'Expected initializer to inject "bar" property in stdClass service, but it was not' ); - $this->assertEquals('baz', $stdClass->bar, 'stdClass "bar" property was not injected correctly'); + self::assertEquals('baz', $stdClass->bar, 'stdClass "bar" property was not injected correctly'); } /** @@ -479,7 +489,7 @@ public function testCanSpecifyAbstractFactoryUsingStringViaConfiguration() ]); $dateTime = $serviceManager->get(DateTime::class); - $this->assertInstanceOf(DateTime::class, $dateTime); + self::assertInstanceOf(DateTime::class, $dateTime); } public function invalidFactories() @@ -514,7 +524,7 @@ public function testPassingInvalidAbstractFactoryTypeViaConfigurationRaisesExcep ) { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage($contains); - $serviceManager = $this->createContainer([ + $this->createContainer([ 'abstract_factories' => [ $factory, ], @@ -533,15 +543,15 @@ public function testCanSpecifyInitializerUsingStringViaConfiguration() ]); $instance = $serviceManager->get(stdClass::class); - $this->assertInstanceOf(stdClass::class, $instance); - $this->assertTrue(isset($instance->foo), '"foo" property was not injected by initializer'); - $this->assertEquals('bar', $instance->foo, '"foo" property was not properly injected'); + self::assertInstanceOf(stdClass::class, $instance); + self::assertTrue(isset($instance->foo), '"foo" property was not injected by initializer'); + self::assertEquals('bar', $instance->foo, '"foo" property was not properly injected'); } public function invalidInitializers() { $factories = $this->invalidFactories(); - $factories['non-class-string'] = ['non-callable-string', 'valid function name or class name']; + $factories['non-class-string'] = ['non-callable-string', 'callable or an instance of']; return $factories; } @@ -555,7 +565,7 @@ public function testPassingInvalidInitializerTypeViaConfigurationRaisesException ) { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage($contains); - $serviceManager = $this->createContainer([ + $this->createContainer([ 'initializers' => [ $initializer, ], @@ -568,7 +578,7 @@ public function testPassingInvalidInitializerTypeViaConfigurationRaisesException public function testGetRaisesExceptionWhenNoFactoryIsResolved() { $serviceManager = $this->createContainer(); - $this->expectException(ContainerException::class); + $this->expectException(ContainerExceptionInterface::class); $this->expectExceptionMessage('Unable to resolve'); $serviceManager->get('Some\Unknown\Service'); } @@ -589,9 +599,6 @@ public function testInvalidDelegatorShouldRaiseExceptionDuringCreation( $delegator, $contains = 'non-callable delegator' ) { - $config = [ - 'option' => 'OPTIONED', - ]; $serviceManager = $this->createContainer([ 'factories' => [ stdClass::class => InvokableFactory::class, @@ -626,9 +633,9 @@ public function testCanInjectAliases() $foo = $container->get('foo'); $bar = $container->get('bar'); - $this->assertInstanceOf(stdClass::class, $foo); - $this->assertInstanceOf(stdClass::class, $bar); - $this->assertSame($foo, $bar); + self::assertInstanceOf(stdClass::class, $foo); + self::assertInstanceOf(stdClass::class, $bar); + self::assertSame($foo, $bar); } /** @@ -639,10 +646,10 @@ public function testCanInjectInvokables() { $container = $this->createContainer(); $container->setInvokableClass('foo', stdClass::class); - $this->assertTrue($container->has('foo')); - $this->assertTrue($container->has(stdClass::class)); + self::assertTrue($container->has('foo')); + self::assertTrue($container->has(stdClass::class)); $foo = $container->get('foo'); - $this->assertInstanceOf(stdClass::class, $foo); + self::assertInstanceOf(stdClass::class, $foo); } /** @@ -657,9 +664,9 @@ public function testCanInjectFactories() $container->setFactory('foo', function () use ($instance) { return $instance; }); - $this->assertTrue($container->has('foo')); + self::assertTrue($container->has('foo')); $foo = $container->get('foo'); - $this->assertSame($instance, $foo); + self::assertSame($instance, $foo); } /** @@ -673,9 +680,9 @@ public function testCanMapLazyServices() $r = new ReflectionProperty($container, 'lazyServices'); $r->setAccessible(true); $lazyServices = $r->getValue($container); - $this->assertArrayHasKey('class_map', $lazyServices); - $this->assertArrayHasKey('foo', $lazyServices['class_map']); - $this->assertEquals(__CLASS__, $lazyServices['class_map']['foo']); + self::assertArrayHasKey('class_map', $lazyServices); + self::assertArrayHasKey('foo', $lazyServices['class_map']); + self::assertEquals(__CLASS__, $lazyServices['class_map']['foo']); } /** @@ -687,9 +694,9 @@ public function testCanInjectAbstractFactories() $container = $this->createContainer(); $container->addAbstractFactory(TestAsset\SimpleAbstractFactory::class); // @todo Remove "true" flag once #49 is merged - $this->assertTrue($container->has(stdClass::class, true)); + self::assertTrue($container->has(stdClass::class, true)); $instance = $container->get(stdClass::class); - $this->assertInstanceOf(stdClass::class, $instance); + self::assertInstanceOf(stdClass::class, $instance); } /** @@ -712,8 +719,8 @@ public function testCanInjectDelegators() }); $foo = $container->get('foo'); - $this->assertInstanceOf(stdClass::class, $foo); - $this->assertAttributeEquals('foo', 'name', $foo); + self::assertInstanceOf(stdClass::class, $foo); + self::assertAttributeEquals('foo', 'name', $foo); } /** @@ -738,8 +745,8 @@ public function testCanInjectInitializers() }); $foo = $container->get('foo'); - $this->assertInstanceOf(stdClass::class, $foo); - $this->assertAttributeEquals(stdClass::class, 'name', $foo); + self::assertInstanceOf(stdClass::class, $foo); + self::assertAttributeEquals(stdClass::class, 'name', $foo); } /** @@ -750,7 +757,7 @@ public function testCanInjectServices() { $container = $this->createContainer(); $container->setService('foo', $this); - $this->assertSame($this, $container->get('foo')); + self::assertSame($this, $container->get('foo')); } /** @@ -769,7 +776,7 @@ public function testCanInjectSharingRules() $container->setShared('foo', false); $first = $container->get('foo'); $second = $container->get('foo'); - $this->assertNotSame($first, $second); + self::assertNotSame($first, $second); } public function methodsAffectedByOverrideSettings() @@ -813,7 +820,7 @@ public function testConfiguringInstanceRaisesExceptionIfAllowOverrideIsFalse($me public function testAllowOverrideFlagIsFalseByDefault() { $container = $this->createContainer(); - $this->assertFalse($container->getAllowOverride()); + self::assertFalse($container->getAllowOverride()); return $container; } @@ -824,7 +831,7 @@ public function testAllowOverrideFlagIsFalseByDefault() public function testAllowOverrideFlagIsMutable($container) { $container->setAllowOverride(true); - $this->assertTrue($container->getAllowOverride()); + self::assertTrue($container->getAllowOverride()); } /** @@ -834,9 +841,9 @@ public function testCanRetrieveParentContainerViaGetServiceLocatorWithDeprecatio { $container = $this->createContainer(); set_error_handler(function ($errno, $errstr) { - $this->assertEquals(E_USER_DEPRECATED, $errno); + self::assertEquals(E_USER_DEPRECATED, $errno); }, E_USER_DEPRECATED); - $this->assertSame($this->creationContext, $container->getServiceLocator()); + self::assertSame($this->creationContext, $container->getServiceLocator()); restore_error_handler(); } @@ -854,4 +861,178 @@ public function testCrashesOnCyclicAliases() ], ]); } + + public function testMinimalCyclicAliasDefinitionShouldThrow() + { + $sm = $this->createContainer([]); + + $this->expectException(CyclicAliasException::class); + $sm->setAlias('alias', 'alias'); + } + + public function testCoverageDepthFirstTaggingOnRecursiveAliasDefinitions() + { + $sm = $this->createContainer([ + 'factories' => [ + stdClass::class => InvokableFactory::class, + ], + 'aliases' => [ + 'alias1' => 'alias2', + 'alias2' => 'alias3', + 'alias3' => stdClass::class, + ], + ]); + $this->assertSame($sm->get('alias1'), $sm->get('alias2')); + $this->assertSame($sm->get(stdClass::class), $sm->get('alias1')); + } + + /** + * The ServiceManager can change internal state on calls to get, + * build or has, latter not currently. Possible state changes + * are caching a factory, registering a service produced by + * a factory, ... + * + * This tests performs three consecutive calls to build/get for + * each registered service to push the service manager through + * all internal states, thereby verifying that build/get/has + * remain stable through the internal states. + * + * @dataProvider provideConsistencyOverInternalStatesTests + * + * @param ContainerInterface $smTemplate + * @param string $name + * @param array[] string $test + */ + public function testConsistencyOverInternalStates($smTemplate, $name, $test, $shared) + { + $sm = clone $smTemplate; + $object['get'] = []; + $object['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; + $this->assertNotNull($obj); + $this->assertTrue($sm->has($name)); + } + + // compares the first to the first also, but ok + foreach ($object['get'] as $sharedObj) { + $this->assertSame($object['get'][0], $sharedObj); + } + // objects from object['build'] have to be different + // from all other objects + foreach ($object['build'] as $idx1 => $nonSharedObj1) { + $this->assertNotContains($nonSharedObj1, $object['get']); + foreach ($object['build'] as $idx2 => $nonSharedObj2) { + if ($idx1 !== $idx2) { + $this->assertNotSame($nonSharedObj1, $nonSharedObj2); + } + } + } + } + + /** + * Data provider + * + * @see testConsistencyOverInternalStates above + * + * @param ContainerInterface $smTemplate + * @param string $name + * @param string[] $test + */ + public function provideConsistencyOverInternalStatesTests() + { + $config1 = [ + 'factories' => [ + // to allow build('service') + 'service' => function ($container, $requestedName, array $options = null) { + return new stdClass(); + }, + 'factory' => SampleFactory::class, + 'delegator' => SampleFactory::class, + ], + 'delegators' => [ + 'delegator' => [ + PassthroughDelegatorFactory::class + ], + ], + 'invokables' => [ + 'invokable' => InvokableObject::class, + ], + 'services' => [ + 'service' => new stdClass(), + ], + 'aliases' => [ + 'serviceAlias' => 'service', + 'invokableAlias' => 'invokable', + 'factoryAlias' => 'factory', + 'abstractFactoryAlias' => 'foo', + 'delegatorAlias' => 'delegator', + ], + 'abstract_factories' => [ + AbstractFactoryFoo::class + ] + ]; + $config2 = $config1; + $config2['shared_by_default'] = false; + + $configs = [ $config1, $config2 ]; + + foreach ($configs as $config) { + $smTemplates[] = $this->createContainer($config); + } + + // produce all 3-tuples of 'build' and 'get', i.e. + // + // [['get', 'get', 'get'], ['get', 'get', 'build'], ... + // ['build', 'build', 'build']] + // + $methods = ['get', 'build']; + foreach ($methods as $method1) { + foreach ($methods as $method2) { + foreach ($methods as $method3) { + $callSequences[] = [$method1, $method2, $method3]; + } + } + } + + foreach ($configs as $config) { + $smTemplate = $this->createContainer($config); + + // setup sharing, services are always shared + $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 ($names as $name => $shared) { + foreach ($callSequences as $callSequence) { + $sm = clone $smTemplate; + $tests[] = [$smTemplate, $name, $callSequence, $shared]; + } + } + } + return $tests; + } } diff --git a/test/ConfigTest.php b/test/ConfigTest.php index 0ceb7987..b2257d01 100644 --- a/test/ConfigTest.php +++ b/test/ConfigTest.php @@ -51,7 +51,7 @@ public function testMergeArrays() ], ]; - $this->assertEquals($expected, $result); + self::assertEquals($expected, $result); } public function testPassesKnownServiceConfigKeysToServiceManagerWithConfigMethod() @@ -107,7 +107,7 @@ public function testPassesKnownServiceConfigKeysToServiceManagerWithConfigMethod $services->configure($expected)->willReturn('CALLED'); $configuration = new Config($config); - $this->assertEquals('CALLED', $configuration->configureServiceManager($services->reveal())); + self::assertEquals('CALLED', $configuration->configureServiceManager($services->reveal())); return [ 'array' => $expected, @@ -122,6 +122,6 @@ public function testToArrayReturnsConfiguration($dependencies) { $configuration = $dependencies['array']; $configInstance = $dependencies['config']; - $this->assertSame($configuration, $configInstance->toArray()); + self::assertSame($configuration, $configInstance->toArray()); } } diff --git a/test/ContainerTest.php b/test/ContainerTest.php new file mode 100644 index 00000000..7091c518 --- /dev/null +++ b/test/ContainerTest.php @@ -0,0 +1,23 @@ +getMessage()); + } + + /** + * Test data provider for testFromCyclicAlias * - * @return void + * @return string[][]|string[][][] + */ + public function cyclicAliasProvider() + { + return [ + [ + 'a', + [ + 'a' => 'a', + ], + "A cycle was detected within the aliases definitions:\n" + . "a -> a\n", + ], + [ + 'a', + [ + 'a' => 'b', + 'b' => 'a' + ], + "A cycle was detected within the aliases definitions:\n" + . "a -> b -> a\n", + ], + [ + 'b', + [ + 'a' => 'b', + 'b' => 'a' + ], + "A cycle was detected within the aliases definitions:\n" + . "b -> a -> b\n", + ], + [ + 'b', + [ + 'a' => 'b', + 'b' => 'a', + ], + "A cycle was detected within the aliases definitions:\n" + . "b -> a -> b\n", + ], + [ + 'a', + [ + 'a' => 'b', + 'b' => 'c', + 'c' => 'a', + ], + "A cycle was detected within the aliases definitions:\n" + . "a -> b -> c -> a\n", + ], + [ + 'b', + [ + 'a' => 'b', + 'b' => 'c', + 'c' => 'a', + ], + "A cycle was detected within the aliases definitions:\n" + . "b -> c -> a -> b\n", + ], + [ + 'c', + [ + 'a' => 'b', + 'b' => 'c', + 'c' => 'a', + ], + "A cycle was detected within the aliases definitions:\n" + . "c -> a -> b -> c\n", + ], + ]; + } + + /** + * @dataProvider aliasesProvider + * + * @param string[] $aliases + * @param string $expectedMessage */ public function testFromAliasesMap(array $aliases, $expectedMessage) { diff --git a/test/Factory/InvokableFactoryTest.php b/test/Factory/InvokableFactoryTest.php index 9494989f..4177bd75 100644 --- a/test/Factory/InvokableFactoryTest.php +++ b/test/Factory/InvokableFactoryTest.php @@ -7,8 +7,8 @@ namespace ZendTest\ServiceManager\Factory; -use Interop\Container\ContainerInterface; use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Factory\InvokableFactory; use ZendTest\ServiceManager\TestAsset\InvokableObject; @@ -25,7 +25,7 @@ public function testCanCreateObject() $object = $factory($container, InvokableObject::class, ['foo' => 'bar']); - $this->assertInstanceOf(InvokableObject::class, $object); - $this->assertEquals(['foo' => 'bar'], $object->options); + self::assertInstanceOf(InvokableObject::class, $object); + self::assertEquals(['foo' => 'bar'], $object->options); } } diff --git a/test/LazyServiceIntegrationTest.php b/test/LazyServiceIntegrationTest.php index 33557bbf..3397a83b 100644 --- a/test/LazyServiceIntegrationTest.php +++ b/test/LazyServiceIntegrationTest.php @@ -21,6 +21,21 @@ use Zend\ServiceManager\ServiceManager; use ZendTest\ServiceManager\TestAsset\InvokableObject; +use function array_filter; +use function closedir; +use function get_class; +use function is_dir; +use function is_file; +use function iterator_to_array; +use function mkdir; +use function opendir; +use function readdir; +use function rmdir; +use function spl_autoload_functions; +use function spl_autoload_unregister; +use function sys_get_temp_dir; +use function unlink; + /** * @covers \Zend\ServiceManager\ServiceManager */ @@ -81,14 +96,14 @@ public function assertProxyDirEmpty($message = '') { $message = $message ?: 'Expected empty proxy directory; found files'; // AssertEquals instead AssertEmpty because the first one prints the list of files. - $this->assertEquals([], iterator_to_array($this->listProxyFiles()), $message); + self::assertEquals([], iterator_to_array($this->listProxyFiles()), $message); } public function assertProxyFileWritten($message = '') { $message = $message ?: 'Expected ProxyManager to write at least one class file; none found'; // AssertNotEquals instead AssertNotEmpty because the first one prints the list of files. - $this->assertNotEquals([], iterator_to_array($this->listProxyFiles()), $message); + self::assertNotEquals([], iterator_to_array($this->listProxyFiles()), $message); } /** @@ -121,12 +136,12 @@ public function testCanUseLazyServiceFactoryFactoryToCreateLazyServiceFactoryToA $this->assertProxyFileWritten(); // Test we got a usable proxy - $this->assertInstanceOf( + self::assertInstanceOf( InvokableObject::class, $instance, 'Service returned does not extend ' . InvokableObject::class ); - $this->assertContains( + self::assertContains( 'TestAssetProxy', get_class($instance), 'Service returned does not contain expected namespace' @@ -134,15 +149,15 @@ public function testCanUseLazyServiceFactoryFactoryToCreateLazyServiceFactoryToA // Test proxying works as expected $options = $instance->getOptions(); - $this->assertInternalType( + self::assertInternalType( 'array', $options, 'Expected an array of options' ); - $this->assertEquals(['foo' => 'bar'], $options, 'Options returned do not match configuration'); + self::assertEquals(['foo' => 'bar'], $options, 'Options returned do not match configuration'); $proxyAutoloadFunctions = $this->getRegisteredProxyAutoloadFunctions(); - $this->assertCount(1, $proxyAutoloadFunctions, 'Only 1 proxy autoloader should be registered'); + self::assertCount(1, $proxyAutoloadFunctions, 'Only 1 proxy autoloader should be registered'); } /** @@ -196,12 +211,12 @@ public function testWillNotGenerateProxyClassFilesByDefault() $this->assertProxyDirEmpty('Expected proxy directory to remain empty when write_proxy_files disabled'); // Test we got a usable proxy - $this->assertInstanceOf( + self::assertInstanceOf( InvokableObject::class, $instance, 'Service returned does not extend ' . InvokableObject::class ); - $this->assertContains( + self::assertContains( 'TestAssetProxy', get_class($instance), 'Service returned does not contain expected namespace' @@ -209,15 +224,15 @@ public function testWillNotGenerateProxyClassFilesByDefault() // Test proxying works as expected $options = $instance->getOptions(); - $this->assertInternalType( + self::assertInternalType( 'array', $options, 'Expected an array of options' ); - $this->assertEquals(['foo' => 'bar'], $options, 'Options returned do not match configuration'); + self::assertEquals(['foo' => 'bar'], $options, 'Options returned do not match configuration'); $proxyAutoloadFunctions = $this->getRegisteredProxyAutoloadFunctions(); - $this->assertCount(1, $proxyAutoloadFunctions, 'Only 1 proxy autoloader should be registered'); + self::assertCount(1, $proxyAutoloadFunctions, 'Only 1 proxy autoloader should be registered'); } public function testOnlyOneProxyAutoloaderItsRegisteredOnSubsequentCalls() @@ -241,20 +256,20 @@ public function testOnlyOneProxyAutoloaderItsRegisteredOnSubsequentCalls() $container = new ServiceManager($config); $instance = $container->build(InvokableObject::class, ['foo' => 'bar']); - $this->assertInstanceOf( + self::assertInstanceOf( InvokableObject::class, $instance, 'Service returned does not extend ' . InvokableObject::class ); $instance = $container->build(stdClass::class, ['foo' => 'bar']); - $this->assertInstanceOf( + self::assertInstanceOf( stdClass::class, $instance, 'Service returned does not extend ' . stdClass::class ); $proxyAutoloadFunctions = $this->getRegisteredProxyAutoloadFunctions(); - $this->assertCount(1, $proxyAutoloadFunctions, 'Only 1 proxy autoloader should be registered'); + self::assertCount(1, $proxyAutoloadFunctions, 'Only 1 proxy autoloader should be registered'); } public function testRaisesServiceNotFoundExceptionIfRequestedLazyServiceIsNotInClassMap() diff --git a/test/Proxy/LazyServiceFactoryTest.php b/test/Proxy/LazyServiceFactoryTest.php index 7f430865..934a4909 100644 --- a/test/Proxy/LazyServiceFactoryTest.php +++ b/test/Proxy/LazyServiceFactoryTest.php @@ -7,12 +7,12 @@ namespace ZendTest\ServiceManager\Proxy; -use Interop\Container\ContainerInterface; -use PHPUnit_Framework_MockObject_MockObject as MockObject; use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; use ProxyManager\Factory\LazyLoadingValueHolderFactory; use ProxyManager\Proxy\LazyLoadingInterface; use ProxyManager\Proxy\VirtualProxyInterface; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\Exception\ServiceNotFoundException; use Zend\ServiceManager\Factory\DelegatorFactoryInterface; use Zend\ServiceManager\Proxy\LazyServiceFactory; @@ -48,7 +48,7 @@ protected function setUp() public function testImplementsDelegatorFactoryInterface() { - $this->assertInstanceOf(DelegatorFactoryInterface::class, $this->factory); + self::assertInstanceOf(DelegatorFactoryInterface::class, $this->factory); } public function testThrowExceptionWhenServiceNotExists() @@ -87,7 +87,7 @@ public function testCreates() ->method('createProxy') ->willReturnCallback( function ($className, $initializer) use ($expectedService) { - $this->assertEquals('FooClass', $className, 'class name not match'); + self::assertEquals('FooClass', $className, 'class name not match'); $wrappedInstance = null; $result = $initializer( @@ -95,8 +95,8 @@ function ($className, $initializer) use ($expectedService) { $this->getMockBuilder(LazyLoadingInterface::class)->getMock() ); - $this->assertEquals('fooValue', $wrappedInstance, 'expected callback return value'); - $this->assertTrue($result, 'initializer should return true'); + self::assertEquals('fooValue', $wrappedInstance, 'expected callback return value'); + self::assertTrue($result, 'initializer should return true'); return $expectedService; } @@ -105,7 +105,7 @@ function ($className, $initializer) use ($expectedService) { $result = $this->factory->__invoke($container, 'fooService', [$callback, 'callback']); - $this->assertSame($expectedService, $result, 'service created not match the expected'); + self::assertSame($expectedService, $result, 'service created not match the expected'); } /** diff --git a/test/ServiceManagerTest.php b/test/ServiceManagerTest.php index 8c559946..b89d66fa 100644 --- a/test/ServiceManagerTest.php +++ b/test/ServiceManagerTest.php @@ -11,12 +11,15 @@ use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use stdClass; +use Zend\ServiceManager\Factory\AbstractFactoryInterface; use Zend\ServiceManager\Factory\FactoryInterface; use Zend\ServiceManager\Factory\InvokableFactory; use Zend\ServiceManager\ServiceManager; use ZendTest\ServiceManager\TestAsset\InvokableObject; use ZendTest\ServiceManager\TestAsset\SimpleServiceManager; +use function get_class; + /** * @covers \Zend\ServiceManager\ServiceManager */ @@ -33,7 +36,7 @@ public function createContainer(array $config = []) public function testServiceManagerIsAPsr11Container() { $container = $this->createContainer(); - $this->assertInstanceOf(ContainerInterface::class, $container); + self::assertInstanceOf(ContainerInterface::class, $container); } public function testConfigurationCanBeMerged() @@ -44,9 +47,9 @@ public function testConfigurationCanBeMerged() ] ]); - $this->assertTrue($serviceManager->has(DateTime::class)); + self::assertTrue($serviceManager->has(DateTime::class)); // stdClass service is inlined in SimpleServiceManager - $this->assertTrue($serviceManager->has(stdClass::class)); + self::assertTrue($serviceManager->has(stdClass::class)); } public function testConfigurationTakesPrecedenceWhenMerged() @@ -94,13 +97,13 @@ function ($container, $name, $callback) { ]); $instance = $serviceManager->get(stdClass::class); - $this->assertTrue(isset($instance->option), 'Delegator-injected option was not found'); - $this->assertEquals( + self::assertTrue(isset($instance->option), 'Delegator-injected option was not found'); + self::assertEquals( $config['option'], $instance->option, 'Delegator-injected option does not match configuration' ); - $this->assertEquals('bar', $instance->foo); + self::assertEquals('bar', $instance->foo); } public function shareProvider() @@ -148,7 +151,7 @@ public function testShareability($sharedByDefault, $serviceShared, $serviceDefin $a = $serviceManager->get(stdClass::class); $b = $serviceManager->get(stdClass::class); - $this->assertEquals($shouldBeSameInstance, $a === $b); + self::assertEquals($shouldBeSameInstance, $a === $b); } public function testMapsOneToOneInvokablesAsInvokableFactoriesInternally() @@ -160,7 +163,7 @@ public function testMapsOneToOneInvokablesAsInvokableFactoriesInternally() ]; $serviceManager = new ServiceManager($config); - $this->assertAttributeSame([ + self::assertAttributeSame([ InvokableObject::class => InvokableFactory::class, ], 'factories', $serviceManager, 'Invokable object factory not found'); } @@ -174,10 +177,10 @@ public function testMapsNonSymmetricInvokablesAsAliasPlusInvokableFactory() ]; $serviceManager = new ServiceManager($config); - $this->assertAttributeSame([ + self::assertAttributeSame([ 'Invokable' => InvokableObject::class, ], 'aliases', $serviceManager, 'Alias not found for non-symmetric invokable'); - $this->assertAttributeSame([ + self::assertAttributeSame([ InvokableObject::class => InvokableFactory::class, ], 'factories', $serviceManager, 'Factory not found for non-symmetric invokable target'); } @@ -200,7 +203,7 @@ public function testSharedServicesReferencingInvokableAliasShouldBeHonored() $instance1 = $serviceManager->get('Invokable'); $instance2 = $serviceManager->get('Invokable'); - $this->assertNotSame($instance1, $instance2); + self::assertNotSame($instance1, $instance2); } public function testSharedServicesReferencingAliasShouldBeHonored() @@ -221,7 +224,7 @@ public function testSharedServicesReferencingAliasShouldBeHonored() $instance1 = $serviceManager->get('Invokable'); $instance2 = $serviceManager->get('Invokable'); - $this->assertNotSame($instance1, $instance2); + self::assertNotSame($instance1, $instance2); } public function testAliasToAnExplicitServiceShouldWork() @@ -240,7 +243,7 @@ public function testAliasToAnExplicitServiceShouldWork() $service = $serviceManager->get(InvokableObject::class); $alias = $serviceManager->get('Invokable'); - $this->assertSame($service, $alias); + self::assertSame($service, $alias); } /** @@ -264,8 +267,33 @@ public function testSetAliasShouldWorkWithRecursiveAlias() $alias = $serviceManager->get('Alias'); $headAlias = $serviceManager->get('HeadAlias'); - $this->assertSame($service, $alias); - $this->assertSame($service, $headAlias); + self::assertSame($service, $alias); + self::assertSame($service, $headAlias); + } + + public function testAbstractFactoryShouldBeCheckedForResolvedAliasesInsteadOfAliasName() + { + $abstractFactory = $this->createMock(AbstractFactoryInterface::class); + + $serviceManager = new SimpleServiceManager([ + 'aliases' => [ + 'Alias' => 'ServiceName', + ], + 'abstract_factories' => [ + $abstractFactory, + ], + ]); + + $abstractFactory + ->method('canCreate') + ->withConsecutive( + [ $this->anything(), $this->equalTo('Alias') ], + [ $this->anything(), $this->equalTo('ServiceName')] + ) + ->willReturnCallback(function ($context, $name) { + return $name === 'Alias'; + }); + $this->assertTrue($serviceManager->has('Alias')); } public static function sampleFactory() @@ -283,4 +311,56 @@ public function testFactoryMayBeStaticMethodDescribedByCallableString() $serviceManager = new SimpleServiceManager($config); $this->assertEquals(stdClass::class, get_class($serviceManager->get(stdClass::class))); } + + public function testResolvedAliasFromAbstractFactory() + { + $abstractFactory = $this->createMock(AbstractFactoryInterface::class); + + $serviceManager = new SimpleServiceManager([ + 'aliases' => [ + 'Alias' => 'ServiceName', + ], + 'abstract_factories' => [ + $abstractFactory, + ], + ]); + + $abstractFactory + ->expects(self::any()) + ->method('canCreate') + ->withConsecutive( + [self::anything(), 'Alias'], + [self::anything(), 'ServiceName'] + ) + ->will(self::returnCallback(function ($context, $name) { + return $name === 'ServiceName'; + })); + + self::assertTrue($serviceManager->has('Alias')); + } + + public function testResolvedAliasNoMatchingAbstractFactoryReturnsFalse() + { + $abstractFactory = $this->createMock(AbstractFactoryInterface::class); + + $serviceManager = new SimpleServiceManager([ + 'aliases' => [ + 'Alias' => 'ServiceName', + ], + 'abstract_factories' => [ + $abstractFactory, + ], + ]); + + $abstractFactory + ->expects(self::any()) + ->method('canCreate') + ->withConsecutive( + [self::anything(), 'Alias'], + [self::anything(), 'ServiceName'] + ) + ->willReturn(false); + + self::assertFalse($serviceManager->has('Alias')); + } } diff --git a/test/TestAsset/AbstractFactoryFoo.php b/test/TestAsset/AbstractFactoryFoo.php new file mode 100644 index 00000000..9ab36eb7 --- /dev/null +++ b/test/TestAsset/AbstractFactoryFoo.php @@ -0,0 +1,27 @@ +options = $options; + } +} diff --git a/test/TestAsset/PassthroughDelegatorFactory.php b/test/TestAsset/PassthroughDelegatorFactory.php new file mode 100644 index 00000000..d0eec4b9 --- /dev/null +++ b/test/TestAsset/PassthroughDelegatorFactory.php @@ -0,0 +1,23 @@ +command; $this->assertHelp(); - $this->assertEquals(0, $command([])); + self::assertEquals(0, $command([])); } public function helpArguments() @@ -75,7 +78,7 @@ public function testEmitsHelpWhenHelpArgumentProvidedAsFirstArgument($argument) { $command = $this->command; $this->assertHelp(); - $this->assertEquals(0, $command([$argument])); + self::assertEquals(0, $command([$argument])); } public function testEmitsErrorWhenTooFewArgumentsPresent() @@ -83,7 +86,7 @@ public function testEmitsErrorWhenTooFewArgumentsPresent() $command = $this->command; $this->assertErrorRaised('Missing class name'); $this->assertHelp(STDERR); - $this->assertEquals(1, $command(['foo'])); + self::assertEquals(1, $command(['foo'])); } public function testRaisesExceptionIfConfigFileNotFoundAndDirectoryNotWritable() @@ -94,7 +97,7 @@ public function testRaisesExceptionIfConfigFileNotFoundAndDirectoryNotWritable() $config = vfsStream::url('project/config/test.config.php'); $this->assertErrorRaised(sprintf('Cannot create configuration at path "%s"; not writable.', $config)); $this->assertHelp(STDERR); - $this->assertEquals(1, $command([$config, 'Not\A\Real\Class'])); + self::assertEquals(1, $command([$config, 'Not\A\Real\Class'])); } public function testGeneratesConfigFileWhenProvidedConfigurationFileNotFound() @@ -106,17 +109,17 @@ public function testGeneratesConfigFileWhenProvidedConfigurationFileNotFound() $this->helper->writeLine('[DONE] Changes written to ' . $config)->shouldBeCalled(); - $this->assertEquals(0, $command([$config, SimpleDependencyObject::class])); + self::assertEquals(0, $command([$config, SimpleDependencyObject::class])); $generated = include $config; - $this->assertInternalType('array', $generated); - $this->assertArrayHasKey(ConfigAbstractFactory::class, $generated); + self::assertInternalType('array', $generated); + self::assertArrayHasKey(ConfigAbstractFactory::class, $generated); $factoryConfig = $generated[ConfigAbstractFactory::class]; - $this->assertInternalType('array', $factoryConfig); - $this->assertArrayHasKey(SimpleDependencyObject::class, $factoryConfig); - $this->assertArrayHasKey(InvokableObject::class, $factoryConfig); - $this->assertContains(InvokableObject::class, $factoryConfig[SimpleDependencyObject::class]); - $this->assertEquals([], $factoryConfig[InvokableObject::class]); + self::assertInternalType('array', $factoryConfig); + self::assertArrayHasKey(SimpleDependencyObject::class, $factoryConfig); + self::assertArrayHasKey(InvokableObject::class, $factoryConfig); + self::assertContains(InvokableObject::class, $factoryConfig[SimpleDependencyObject::class]); + self::assertEquals([], $factoryConfig[InvokableObject::class]); } /** @@ -131,24 +134,24 @@ public function testGeneratesConfigFileIgnoringUnresolved($argument) $this->helper->writeLine('[DONE] Changes written to ' . $config)->shouldBeCalled(); - $this->assertEquals(0, $command([$argument, $config, ObjectWithObjectScalarDependency::class])); + self::assertEquals(0, $command([$argument, $config, ObjectWithObjectScalarDependency::class])); $generated = include $config; - $this->assertInternalType('array', $generated); - $this->assertArrayHasKey(ConfigAbstractFactory::class, $generated); + self::assertInternalType('array', $generated); + self::assertArrayHasKey(ConfigAbstractFactory::class, $generated); $factoryConfig = $generated[ConfigAbstractFactory::class]; - $this->assertInternalType('array', $factoryConfig); - $this->assertArrayHasKey(SimpleDependencyObject::class, $factoryConfig); - $this->assertArrayHasKey(InvokableObject::class, $factoryConfig); - $this->assertContains(InvokableObject::class, $factoryConfig[SimpleDependencyObject::class]); - $this->assertEquals([], $factoryConfig[InvokableObject::class]); - - $this->assertArrayHasKey(ObjectWithObjectScalarDependency::class, $factoryConfig); - $this->assertContains( + self::assertInternalType('array', $factoryConfig); + self::assertArrayHasKey(SimpleDependencyObject::class, $factoryConfig); + self::assertArrayHasKey(InvokableObject::class, $factoryConfig); + self::assertContains(InvokableObject::class, $factoryConfig[SimpleDependencyObject::class]); + self::assertEquals([], $factoryConfig[InvokableObject::class]); + + self::assertArrayHasKey(ObjectWithObjectScalarDependency::class, $factoryConfig); + self::assertContains( SimpleDependencyObject::class, $factoryConfig[ObjectWithObjectScalarDependency::class] ); - $this->assertContains( + self::assertContains( ObjectWithScalarDependency::class, $factoryConfig[ObjectWithObjectScalarDependency::class] ); @@ -163,7 +166,7 @@ public function testEmitsErrorWhenConfigurationFileDoesNotReturnArray() $config = vfsStream::url('project/config/invalid.config.php'); $this->assertErrorRaised('Configuration at path "' . $config . '" does not return an array.'); $this->assertHelp(STDERR); - $this->assertEquals(1, $command([$config, 'Not\A\Real\Class'])); + self::assertEquals(1, $command([$config, 'Not\A\Real\Class'])); } public function testEmitsErrorWhenClassDoesNotExist() @@ -175,7 +178,7 @@ public function testEmitsErrorWhenClassDoesNotExist() $config = vfsStream::url('project/config/test.config.php'); $this->assertErrorRaised('Class "Not\\A\\Real\\Class" does not exist or could not be autoloaded.'); $this->assertHelp(STDERR); - $this->assertEquals(1, $command([$config, 'Not\A\Real\Class'])); + self::assertEquals(1, $command([$config, 'Not\A\Real\Class'])); } public function testEmitsErrorWhenUnableToCreateConfiguration() @@ -187,7 +190,7 @@ public function testEmitsErrorWhenUnableToCreateConfiguration() $config = vfsStream::url('project/config/test.config.php'); $this->assertErrorRaised('Unable to create config for "' . ObjectWithScalarDependency::class . '":'); $this->assertHelp(STDERR); - $this->assertEquals(1, $command([$config, ObjectWithScalarDependency::class])); + self::assertEquals(1, $command([$config, ObjectWithScalarDependency::class])); } public function testEmitsConfigFileToStdoutWhenSuccessful() @@ -200,16 +203,16 @@ public function testEmitsConfigFileToStdoutWhenSuccessful() $this->helper->writeLine('[DONE] Changes written to ' . $config)->shouldBeCalled(); - $this->assertEquals(0, $command([$config, SimpleDependencyObject::class])); + self::assertEquals(0, $command([$config, SimpleDependencyObject::class])); $generated = include $config; - $this->assertInternalType('array', $generated); - $this->assertArrayHasKey(ConfigAbstractFactory::class, $generated); + self::assertInternalType('array', $generated); + self::assertArrayHasKey(ConfigAbstractFactory::class, $generated); $factoryConfig = $generated[ConfigAbstractFactory::class]; - $this->assertInternalType('array', $factoryConfig); - $this->assertArrayHasKey(SimpleDependencyObject::class, $factoryConfig); - $this->assertArrayHasKey(InvokableObject::class, $factoryConfig); - $this->assertContains(InvokableObject::class, $factoryConfig[SimpleDependencyObject::class]); - $this->assertEquals([], $factoryConfig[InvokableObject::class]); + self::assertInternalType('array', $factoryConfig); + self::assertArrayHasKey(SimpleDependencyObject::class, $factoryConfig); + self::assertArrayHasKey(InvokableObject::class, $factoryConfig); + self::assertContains(InvokableObject::class, $factoryConfig[SimpleDependencyObject::class]); + self::assertEquals([], $factoryConfig[InvokableObject::class]); } } diff --git a/test/Tool/ConfigDumperTest.php b/test/Tool/ConfigDumperTest.php index 23b50637..38ff0846 100644 --- a/test/Tool/ConfigDumperTest.php +++ b/test/Tool/ConfigDumperTest.php @@ -7,8 +7,8 @@ namespace ZendTest\ServiceManager\Tool; -use Interop\Container\ContainerInterface; use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; use Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory; use Zend\ServiceManager\Exception\InvalidArgumentException; use Zend\ServiceManager\Factory\FactoryInterface; @@ -22,6 +22,11 @@ use ZendTest\ServiceManager\TestAsset\SecondComplexDependencyObject; use ZendTest\ServiceManager\TestAsset\SimpleDependencyObject; +use function file_put_contents; +use function sys_get_temp_dir; +use function tempnam; +use function unlink; + class ConfigDumperTest extends TestCase { /** @@ -36,16 +41,16 @@ public function setUp() public function testCreateDependencyConfigExceptsIfClassNameIsNotString() { - self::expectException(InvalidArgumentException::class); - self::expectExceptionMessage('Class name must be a string, integer given'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Class name must be a string, integer given'); $this->dumper->createDependencyConfig([], 42); } public function testCreateDependencyConfigExceptsIfClassDoesNotExist() { $className = 'Dirk\Gentley\Holistic\Detective\Agency'; - self::expectException(InvalidArgumentException::class); - self::expectExceptionMessage('Cannot find class or interface with name ' . $className); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Cannot find class or interface with name ' . $className); $this->dumper->createDependencyConfig([], $className); } @@ -92,12 +97,12 @@ public function testCreateDependencyConfigClassWithoutConstructorHandlesAsInvoka public function testCreateDependencyConfigWithoutTypeHintedParameterExcepts() { - self::expectException(InvalidArgumentException::class); - self::expectExceptionMessage( + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( 'Cannot create config for constructor argument "aName", ' . 'it has no type hint, or non-class/interface type hint' ); - $config = $this->dumper->createDependencyConfig( + $this->dumper->createDependencyConfig( [ConfigAbstractFactory::class => []], ObjectWithScalarDependency::class ); @@ -105,8 +110,8 @@ public function testCreateDependencyConfigWithoutTypeHintedParameterExcepts() public function testCreateDependencyConfigWithContainerAndNoServiceWithoutTypeHintedParameterExcepts() { - self::expectException(InvalidArgumentException::class); - self::expectExceptionMessage( + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( 'Cannot create config for constructor argument "aName", ' . 'it has no type hint, or non-class/interface type hint' ); @@ -117,7 +122,7 @@ public function testCreateDependencyConfigWithContainerAndNoServiceWithoutTypeHi $dumper = new ConfigDumper($container->reveal()); - $config = $dumper->createDependencyConfig( + $dumper->createDependencyConfig( [ConfigAbstractFactory::class => []], ObjectWithScalarDependency::class ); @@ -209,16 +214,16 @@ public function testCreateDependencyConfigWorksWithMultipleDependenciesOfSameTyp public function testCreateFactoryMappingsExceptsIfClassNameIsNotString() { - self::expectException(InvalidArgumentException::class); - self::expectExceptionMessage('Class name must be a string, integer given'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Class name must be a string, integer given'); $this->dumper->createFactoryMappings([], 42); } public function testCreateFactoryMappingsExceptsIfClassDoesNotExist() { $className = 'Dirk\Gentley\Holistic\Detective\Agency'; - self::expectException(InvalidArgumentException::class); - self::expectExceptionMessage('Cannot find class or interface with name ' . $className); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Cannot find class or interface with name ' . $className); $this->dumper->createFactoryMappings([], $className); } @@ -265,8 +270,8 @@ public function testCreateFactoryMappingsFromConfigReturnsIfNoConfigKey() public function testCreateFactoryMappingsFromConfigExceptsWhenConfigNotArray() { - self::expectException(InvalidArgumentException::class); - self::expectExceptionMessage( + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( 'Config key for ' . ConfigAbstractFactory::class . ' should be an array, boolean given' ); @@ -319,20 +324,20 @@ public function testCreateFactoryMappingsFromConfigWithWorkingConfig() public function testDumpConfigFileReturnsContentsForConfigFileUsingUsingClassNotationAndShortArrays(array $config) { $formatted = $this->dumper->dumpConfigFile($config); - $this->assertContains( + self::assertContains( '<' . "?php\n/**\n * This file generated by Zend\ServiceManager\Tool\ConfigDumper.\n", $formatted ); - $this->assertNotContains('array(', $formatted); - $this->assertContains('::class', $formatted); + self::assertNotContains('array(', $formatted); + self::assertContains('::class', $formatted); $file = tempnam(sys_get_temp_dir(), 'ZSCLI'); file_put_contents($file, $formatted); - $test = include($file); + $test = include $file; unlink($file); - $this->assertEquals($test, $config); + self::assertEquals($test, $config); } public function testWillDumpConfigForClassDependingOnInterfaceButOmitInterfaceConfig() diff --git a/test/Tool/FactoryCreatorCommandTest.php b/test/Tool/FactoryCreatorCommandTest.php index c7332462..3437664e 100644 --- a/test/Tool/FactoryCreatorCommandTest.php +++ b/test/Tool/FactoryCreatorCommandTest.php @@ -15,6 +15,9 @@ use ZendTest\ServiceManager\TestAsset\ObjectWithScalarDependency; use ZendTest\ServiceManager\TestAsset\SimpleDependencyObject; +use function file_get_contents; +use function sprintf; + class FactoryCreatorCommandTest extends TestCase { public function setUp() @@ -27,7 +30,7 @@ public function testEmitsHelpWhenNoArgumentsProvided() { $command = $this->command; $this->assertHelp(); - $this->assertEquals(0, $command([])); + self::assertEquals(0, $command([])); } public function assertHelp($stream = STDOUT) @@ -55,7 +58,7 @@ public function testEmitsHelpWhenHelpArgumentProvidedAsFirstArgument($argument) { $command = $this->command; $this->assertHelp(); - $this->assertEquals(0, $command([$argument])); + self::assertEquals(0, $command([$argument])); } public function invalidArguments() @@ -74,7 +77,7 @@ public function testEmitsErrorMessageIfArgumentIsNotAClass($argument) $command = $this->command; $this->assertErrorRaised(sprintf('Class "%s" does not exist', $argument)); $this->assertHelp(STDERR); - $this->assertEquals(1, $command([$argument])); + self::assertEquals(1, $command([$argument])); } public function assertErrorRaised($message) @@ -89,7 +92,7 @@ public function testEmitsErrorWhenUnableToCreateFactory() $command = $this->command; $this->assertErrorRaised('Unable to create factory for "' . ObjectWithScalarDependency::class . '":'); $this->assertHelp(STDERR); - $this->assertEquals(1, $command([ObjectWithScalarDependency::class])); + self::assertEquals(1, $command([ObjectWithScalarDependency::class])); } public function testEmitsFactoryFileToStdoutWhenSuccessful() @@ -98,6 +101,6 @@ public function testEmitsFactoryFileToStdoutWhenSuccessful() $expected = file_get_contents(__DIR__ . '/../TestAsset/factories/SimpleDependencyObject.php'); $this->helper->write($expected, false)->shouldBeCalled(); - $this->assertEquals(0, $command([SimpleDependencyObject::class])); + self::assertEquals(0, $command([SimpleDependencyObject::class])); } } diff --git a/test/Tool/FactoryCreatorTest.php b/test/Tool/FactoryCreatorTest.php index a386bcc9..0c315f18 100644 --- a/test/Tool/FactoryCreatorTest.php +++ b/test/Tool/FactoryCreatorTest.php @@ -13,6 +13,8 @@ use ZendTest\ServiceManager\TestAsset\InvokableObject; use ZendTest\ServiceManager\TestAsset\SimpleDependencyObject; +use function file_get_contents; + class FactoryCreatorTest extends TestCase { /**