diff --git a/src/ServiceManager.php b/src/ServiceManager.php index 0f2c23b0..3729d010 100644 --- a/src/ServiceManager.php +++ b/src/ServiceManager.php @@ -20,6 +20,7 @@ use Zend\ServiceManager\Exception\InvalidArgumentException; use Zend\ServiceManager\Exception\ServiceNotCreatedException; use Zend\ServiceManager\Exception\ServiceNotFoundException; +use Zend\Stdlib\ArrayUtils; /** * Service Manager. @@ -337,7 +338,7 @@ public function configure(array $config) } if (isset($config['delegators'])) { - $this->delegators = array_merge_recursive($this->delegators, $config['delegators']); + $this->mergeDelegators($config['delegators']); } if (isset($config['shared'])) { @@ -357,7 +358,7 @@ public function configure(array $config) // If lazy service configuration was provided, reset the lazy services // delegator factory. if (isset($config['lazy_services']) && ! empty($config['lazy_services'])) { - $this->lazyServices = array_merge_recursive($this->lazyServices, $config['lazy_services']); + $this->lazyServices = ArrayUtils::merge($this->lazyServices, $config['lazy_services']); $this->lazyServicesDelegator = null; } @@ -830,6 +831,32 @@ private function createLazyServiceDelegatorFactory() return $this->lazyServicesDelegator; } + /** + * Merge delegators avoiding multiple same delegators for the same service. + * It works with strings and class instances. + * It's not possibile to de-duple anonymous functions + * + * @param string[][]|Factory\DelegatorFactoryInterface[][] $config + * @return string[][]|Factory\DelegatorFactoryInterface[][] + */ + private function mergeDelegators(array $config) + { + foreach ($config as $key => $delegators) { + if (! isset($this->delegators[$key])) { + $this->delegators[$key] = $delegators; + continue; + } + + foreach ($delegators as $delegator) { + if (! in_array($delegator, $this->delegators[$key], true)) { + $this->delegators[$key][] = $delegator; + } + } + } + + return $this->delegators; + } + /** * Create aliases for invokable classes. * diff --git a/test/ServiceManagerTest.php b/test/ServiceManagerTest.php index 8c559946..446521e3 100644 --- a/test/ServiceManagerTest.php +++ b/test/ServiceManagerTest.php @@ -13,6 +13,7 @@ use stdClass; use Zend\ServiceManager\Factory\FactoryInterface; use Zend\ServiceManager\Factory\InvokableFactory; +use Zend\ServiceManager\Proxy\LazyServiceFactory; use Zend\ServiceManager\ServiceManager; use ZendTest\ServiceManager\TestAsset\InvokableObject; use ZendTest\ServiceManager\TestAsset\SimpleServiceManager; @@ -283,4 +284,51 @@ public function testFactoryMayBeStaticMethodDescribedByCallableString() $serviceManager = new SimpleServiceManager($config); $this->assertEquals(stdClass::class, get_class($serviceManager->get(stdClass::class))); } + + /** + * Hotfix #279 + * @see https://github.com/zendframework/zend-servicemanager/issues/279 + */ + public function testConfigureMultipleTimes() + { + $delegatorFactory = function ( + ContainerInterface $container, + $name, + callable $callback + ) { + /** @var InvokableObject $instance */ + $instance = $callback(); + $options = $instance->getOptions(); + $inc = ! empty($options['inc']) ? $options['inc'] : 0; + return new InvokableObject(['inc' => ++$inc]); + }; + + $config = [ + 'factories' => [ + 'Foo' => function () { + return new InvokableObject(); + }, + ], + 'delegators' => [ + 'Foo' => [ + $delegatorFactory, + LazyServiceFactory::class, + ], + ], + 'lazy_services' => [ + 'class_map' => [ + 'Foo' => InvokableObject::class, + ], + ], + ]; + + $serviceManager = new ServiceManager($config); + $serviceManager->configure($config); + + /** @var InvokableObject $instance */ + $instance = $serviceManager->get('Foo'); + + self::assertInstanceOf(InvokableObject::class, $instance); + self::assertSame(1, $instance->getOptions()['inc']); + } }