Skip to content
This repository was archived by the owner on Feb 6, 2020. It is now read-only.

Commit b253d70

Browse files
committed
Apply allow_override in same way as v2
This patch alters how the allow_override flag works in order to better mimic how it was done in v2. This means: - the flag is `false` by default. - the flag only kicks in when new configuration would change a service that has already been fetched and cached internally (i.e., it exists in the `$services` array). The implication is that the following will never be problematic: - abstract factories (as they are a factory of "last resort") - initializers (they only apply to newly created instances) Tests were updated to reflect the changes.
1 parent 4f3680b commit b253d70

File tree

2 files changed

+108
-23
lines changed

2 files changed

+108
-23
lines changed

src/ServiceManager.php

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class ServiceManager implements ServiceLocatorInterface
5959
*
6060
* @param bool
6161
*/
62-
protected $allowOverride = true;
62+
protected $allowOverride = false;
6363

6464
/**
6565
* @var ContainerInterface
@@ -275,15 +275,12 @@ public function getAllowOverride()
275275
* @param array $config
276276
* @return self
277277
* @throws ContainerModificationsNotAllowedException if the allow
278-
* override flag has been toggled off.
278+
* override flag has been toggled off, and a service instance
279+
* exists for a given service.
279280
*/
280281
public function configure(array $config)
281282
{
282-
if ($this->allowOverride === false) {
283-
throw new ContainerModificationsNotAllowedException(
284-
'Overrides have been disabled on this container instance'
285-
);
286-
}
283+
$this->validateOverrides($config);
287284

288285
if (isset($config['services'])) {
289286
$this->services = $config['services'] + $this->services;
@@ -785,4 +782,87 @@ private function createFactoriesForInvokables(array $invokables)
785782
}
786783
return $factories;
787784
}
785+
786+
/**
787+
* Determine if one or more services already exist in the container.
788+
*
789+
* If the allow override flag is true, this method does nothing.
790+
*
791+
* Otherwise, it checks against each of the following service types,
792+
* if present, and validates that none are defining services that
793+
* already exist; if they do, it raises an exception indicating
794+
* modification is not allowed.
795+
*
796+
* @param array $config
797+
* @throws ContainerModificationsNotAllowedException if any services
798+
* provided already have instances available.
799+
*/
800+
private function validateOverrides(array $config)
801+
{
802+
if ($this->allowOverride) {
803+
return;
804+
}
805+
806+
if (isset($config['services'])) {
807+
$this->validateOverrideSet(array_keys($config['services']), 'service');
808+
}
809+
810+
if (isset($config['aliases'])) {
811+
$this->validateOverrideSet(array_keys($config['aliases']), 'alias');
812+
}
813+
814+
if (isset($config['invokables'])) {
815+
$this->validateOverrideSet(array_keys($config['invokables']), 'invokable class');
816+
}
817+
818+
if (isset($config['factories'])) {
819+
$this->validateOverrideSet(array_keys($config['factories']), 'factory');
820+
}
821+
822+
if (isset($config['delegators'])) {
823+
$this->validateOverrideSet(array_keys($config['delegators']), 'delegator');
824+
}
825+
826+
if (isset($config['shared'])) {
827+
$this->validateOverrideSet(array_keys($config['shared']), 'sharing rule');
828+
}
829+
830+
if (isset($config['lazy_services']['class_map'])) {
831+
$this->validateOverrideSet(array_keys($config['lazy_services']['class_map']), 'lazy service');
832+
}
833+
}
834+
835+
/**
836+
* Determine if one or more services already exist for a given type.
837+
*
838+
* Loops through the provided service names, checking if any have current
839+
* service instances; if not, it returns, but otherwise, it raises an
840+
* exception indicating modification is not allowed.
841+
*
842+
* @param string[] $services
843+
* @param string $type Type of service being checked.
844+
* @throws ContainerModificationsNotAllowedException if any services
845+
* provided already have instances available.
846+
*/
847+
private function validateOverrideSet(array $services, $type)
848+
{
849+
$detected = [];
850+
foreach ($services as $service) {
851+
if (isset($this->services[$service])) {
852+
$detected[] = $service;
853+
}
854+
}
855+
856+
if (empty($detected)) {
857+
return;
858+
}
859+
860+
throw new ContainerModificationsNotAllowedException(sprintf(
861+
'An updated/new %s is not allowed, as the container does not allow '
862+
. 'changes for services with existing instances; the following '
863+
. 'already exist in the container: %s',
864+
$type,
865+
implode(', ', $detected)
866+
));
867+
}
788868
}

test/CommonServiceLocatorBehaviorsTrait.php

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -719,17 +719,22 @@ public function testCanInjectSharingRules()
719719
public function methodsAffectedByOverrideSettings()
720720
{
721721
// @codingStandardsIgnoreStart
722-
// name => [ 'method to invoke', [arguments for invocation]]
722+
// name => [ 'method to invoke', [arguments for invocation]]
723723
return [
724-
'setAlias' => ['setAlias', ['foo', 'bar']],
725-
'setInvokableClass' => ['setInvokableClass', ['foo', __CLASS__]],
726-
'setFactory' => ['setFactory', ['foo', function () {}]],
727-
'addAbstractFactory' => ['addAbstractFactory', [TestAsset\SimpleAbstractFactory::class]],
728-
'addDelegator' => ['addDelegator', ['foo', TestAsset\PreDelegator::class]],
729-
'addInitializer' => ['addInitializer', [function () {}]],
730-
'setService' => ['setService', ['foo', $this]],
731-
'setShared' => ['setShared', ['foo', false]],
732-
'configure' => ['configure', [[]]],
724+
'setAlias' => ['setAlias', ['foo', 'bar']],
725+
'setInvokableClass' => ['setInvokableClass', ['foo', __CLASS__]],
726+
'setFactory' => ['setFactory', ['foo', function () {}]],
727+
'setService' => ['setService', ['foo', $this]],
728+
'setShared' => ['setShared', ['foo', false]],
729+
'mapLazyService' => ['mapLazyService', ['foo', __CLASS__]],
730+
'addDelegator' => ['addDelegator', ['foo', function () {}]],
731+
'configure-alias' => ['configure', [['aliases' => ['foo' => 'bar']]]],
732+
'configure-invokable' => ['configure', [['invokables' => ['foo' => 'foo']]]],
733+
'configure-invokable-alias' => ['configure', [['invokables' => ['foo' => 'bar']]]],
734+
'configure-factory' => ['configure', [['factories' => ['foo' => function () {}]]]],
735+
'configure-service' => ['configure', [['services' => ['foo' => $this]]]],
736+
'configure-shared' => ['configure', [['shared' => ['foo' => false]]]],
737+
'configure-lazy-service' => ['configure', [['lazy_services' => ['class_map' => ['foo' => __CLASS__]]]]],
733738
];
734739
// @codingStandardsIgnoreEnd
735740
}
@@ -740,7 +745,7 @@ public function methodsAffectedByOverrideSettings()
740745
*/
741746
public function testConfiguringInstanceRaisesExceptionIfAllowOverrideIsFalse($method, $args)
742747
{
743-
$container = $this->createContainer();
748+
$container = $this->createContainer(['services' => ['foo' => $this]]);
744749
$container->setAllowOverride(false);
745750
$this->setExpectedException(ContainerModificationsNotAllowedException::class);
746751
call_user_func_array([$container, $method], $args);
@@ -749,20 +754,20 @@ public function testConfiguringInstanceRaisesExceptionIfAllowOverrideIsFalse($me
749754
/**
750755
* @group mutation
751756
*/
752-
public function testAllowOverrideFlagIsTrueByDefault()
757+
public function testAllowOverrideFlagIsFalseByDefault()
753758
{
754759
$container = $this->createContainer();
755-
$this->assertTrue($container->getAllowOverride());
760+
$this->assertFalse($container->getAllowOverride());
756761
return $container;
757762
}
758763

759764
/**
760765
* @group mutation
761-
* @depends testAllowOverrideFlagIsTrueByDefault
766+
* @depends testAllowOverrideFlagIsFalseByDefault
762767
*/
763768
public function testAllowOverrideFlagIsMutable($container)
764769
{
765-
$container->setAllowOverride(false);
766-
$this->assertFalse($container->getAllowOverride());
770+
$container->setAllowOverride(true);
771+
$this->assertTrue($container->getAllowOverride());
767772
}
768773
}

0 commit comments

Comments
 (0)