From a943b96d4291ba60d49dedb92ba6f224fb6d752d Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 27 Apr 2017 11:48:07 -0400 Subject: [PATCH] Not allowing autoconfigure, instanceofConditionals or defaults for ChildDefinition Also, not allowing arguments or method calls for autoconfigure. This is a safety mechanism, since we don't have merging logic. It will allow us to add this in the future if we want to. The reason is that parent-child definitions are a different mechanism for "inheritance" than instanceofConditionas and defaults... creating some edge cases when trying to figure out which settings "win". For example: Suppose a child and parent definitions are defined in different YAML files. The child receives public: false from its _defaults, and the parent receives public: true from its _defaults. Should the final child definition be public: true (so the parent overrides the child, even though it only came from _defaults) or public: false (where the child wins... even though it was only set from its _defaults). Or, if the parent is explicitly set to public: true, should that override the public: false of the child (which it got from its _defaults)? On one hand, the parent is being explicitly set. On the other hand, the child is explicitly in a file settings _defaults public to false. There's no correct answer. There are also problems with instanceof. The importance goes: defaults < instanceof < service definition But how does parent-child relationships fit into that? If a child has public: false from an _instanceof, but the parent explicitly sets public: true, which wins? Should we assume the parent definition wins because it's explicitly set? Or would the _instanceof win, because that's being explicitly applied to the child definition's class by an _instanceof that lives in the same file as that class (whereas the parent definition may live in a different file). Because of this, @nicolas-grekas and I (we also talked a bit to Fabien) decided that the complexity was growing too much. The solution is to not allow any of these new feature to be used by ChildDefinition objects. In other words, when you want some sort of "inheritance" for your service, you should *either* giving your service a parent *or* using defaults and instanceof. And instead of silently not applying defaults and instanceof to child definitions, I think it's better to scream that it's not supported. --- .../DependencyInjection/ChildDefinition.php | 17 +++ .../ResolveDefinitionTemplatesPass.php | 7 +- .../ResolveInstanceofConditionalsPass.php | 22 +++- .../DependencyInjection/ContainerBuilder.php | 19 +-- .../DependencyInjection/Loader/FileLoader.php | 2 +- .../Loader/XmlFileLoader.php | 14 +- .../Loader/YamlFileLoader.php | 15 ++- .../Tests/ChildDefinitionTest.php | 18 +++ .../Tests/Compiler/IntegrationTest.php | 120 +++++++++++------- .../ResolveDefinitionTemplatesPassTest.php | 19 +-- .../ResolveInstanceofConditionalsPassTest.php | 44 +++++-- .../Tests/ContainerBuilderTest.php | 4 +- .../Tests/Fixtures/xml/services29.xml | 6 - .../services_autoconfigure_with_parent.xml | 7 + .../xml/services_defaults_with_parent.xml | 9 ++ .../xml/services_instanceof_with_parent.xml | 9 ++ .../_child.yml | 4 + .../expected.yml | 10 ++ .../autoconfigure_child_not_applied/main.yml | 7 + .../autoconfigure_parent_child/_child.yml | 3 + .../autoconfigure_parent_child/expected.yml | 5 + .../autoconfigure_parent_child/main.yml | 7 + .../_child.yml | 3 + .../expected.yml | 6 + .../autoconfigure_parent_child_tags/main.yml | 7 + .../integration/child_parent/expected.yml | 9 ++ .../yaml/integration/child_parent/main.yml | 13 ++ .../expected.yml | 27 ++++ .../defaults_instanceof_importance/main.yml | 30 +++++ .../defaults_parent_child/_child.yml | 4 + .../defaults_parent_child/expected.yml | 6 + .../defaults_parent_child/main.yml | 9 ++ .../instanceof_parent_child/_child.yml | 4 + .../instanceof_parent_child/expected.yml | 7 + .../instanceof_parent_child/main.yml | 11 ++ .../Tests/Fixtures/yaml/services28.yml | 13 -- .../services_autoconfigure_with_parent.yml | 8 ++ .../services_defaults_instanceof_parent.yml | 57 --------- .../yaml/services_defaults_with_parent.yml | 10 ++ .../yaml/services_instanceof_with_parent.yml | 11 ++ .../Tests/Loader/XmlFileLoaderTest.php | 43 ++++++- .../Tests/Loader/YamlFileLoaderTest.php | 43 ++++++- 42 files changed, 496 insertions(+), 193 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_defaults_with_parent.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof_with_parent.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/_child.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/_child.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/_child.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/child_parent/expected.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/child_parent/main.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/expected.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/main.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/_child.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/_child.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml delete mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_instanceof_parent.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_with_parent.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml diff --git a/src/Symfony/Component/DependencyInjection/ChildDefinition.php b/src/Symfony/Component/DependencyInjection/ChildDefinition.php index b75124f61977f..d202170e373b2 100644 --- a/src/Symfony/Component/DependencyInjection/ChildDefinition.php +++ b/src/Symfony/Component/DependencyInjection/ChildDefinition.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection; +use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; @@ -134,6 +135,22 @@ public function replaceArgument($index, $value) return $this; } + + /** + * @internal + */ + public function setAutoconfigured($autoconfigured) + { + throw new BadMethodCallException('A ChildDefinition cannot be autoconfigured.'); + } + + /** + * @internal + */ + public function setInstanceofConditionals(array $instanceof) + { + throw new BadMethodCallException('A ChildDefinition cannot have instanceof conditionals set on it.'); + } } class_alias(ChildDefinition::class, DefinitionDecorator::class); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php index 0be05dea23bcb..f4f8cd5c151c7 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -101,7 +101,6 @@ private function doResolveDefinition(ChildDefinition $definition) $def->setPublic($parentDef->isPublic()); $def->setLazy($parentDef->isLazy()); $def->setAutowired($parentDef->isAutowired()); - $def->setAutoconfigured($parentDef->isAutoconfigured()); $def->setChanges($parentDef->getChanges()); // overwrite with values specified in the decorator @@ -130,9 +129,6 @@ private function doResolveDefinition(ChildDefinition $definition) if (isset($changes['autowired'])) { $def->setAutowired($definition->isAutowired()); } - if (isset($changes['autoconfigured'])) { - $def->setAutoconfigured($definition->isAutoconfigured()); - } if (isset($changes['shared'])) { $def->setShared($definition->isShared()); } @@ -174,6 +170,9 @@ private function doResolveDefinition(ChildDefinition $definition) // these attributes are always taken from the child $def->setAbstract($definition->isAbstract()); $def->setTags($definition->getTags()); + // autoconfigure is never taken from parent (on purpose) + // and it's not legal on an instanceof + $def->setAutoconfigured($definition->isAutoconfigured()); return $def; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php index 555e8eb36fe3e..6ec07672567d6 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; /** * Applies instanceof conditionals to definitions. @@ -28,6 +29,15 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { + foreach ($container->getAutoconfiguredInstanceof() as $interface => $definition) { + if ($definition->getArguments()) { + throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines arguments but these are not supported and should be removed.', $interface)); + } + if ($definition->getMethodCalls()) { + throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines method calls but these are not supported and should be removed.', $interface)); + } + } + foreach ($container->getDefinitions() as $id => $definition) { if ($definition instanceof ChildDefinition) { // don't apply "instanceof" to children: it will be applied to their parent @@ -40,8 +50,8 @@ public function process(ContainerBuilder $container) private function processDefinition(ContainerBuilder $container, $id, Definition $definition) { $instanceofConditionals = $definition->getInstanceofConditionals(); - $automaticInstanceofConditionals = $definition->isAutoconfigured() ? $container->getAutomaticInstanceofDefinitions() : array(); - if (!$instanceofConditionals && !$automaticInstanceofConditionals) { + $autoconfiguredInstanceof = $definition->isAutoconfigured() ? $container->getAutoconfiguredInstanceof() : array(); + if (!$instanceofConditionals && !$autoconfiguredInstanceof) { return $definition; } @@ -49,7 +59,7 @@ private function processDefinition(ContainerBuilder $container, $id, Definition return $definition; } - $conditionals = $this->mergeConditionals($automaticInstanceofConditionals, $instanceofConditionals, $container); + $conditionals = $this->mergeConditionals($autoconfiguredInstanceof, $instanceofConditionals, $container); $definition->setInstanceofConditionals(array()); $parent = $shared = null; @@ -113,10 +123,10 @@ private function processDefinition(ContainerBuilder $container, $id, Definition return $definition; } - private function mergeConditionals(array $automaticInstanceofConditionals, array $instanceofConditionals, ContainerBuilder $container) + private function mergeConditionals(array $autoconfiguredInstanceof, array $instanceofConditionals, ContainerBuilder $container) { // make each value an array of ChildDefinition - $conditionals = array_map(function ($childDef) { return array($childDef); }, $automaticInstanceofConditionals); + $conditionals = array_map(function ($childDef) { return array($childDef); }, $autoconfiguredInstanceof); foreach ($instanceofConditionals as $interface => $instanceofDef) { // make sure the interface/class exists (but don't validate automaticInstanceofConditionals) @@ -124,7 +134,7 @@ private function mergeConditionals(array $automaticInstanceofConditionals, array throw new RuntimeException(sprintf('"%s" is set as an "instanceof" conditional, but it does not exist.', $interface)); } - if (!isset($automaticInstanceofConditionals[$interface])) { + if (!isset($autoconfiguredInstanceof[$interface])) { $conditionals[$interface] = array(); } diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 910427b210788..929b8983cacc4 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -118,7 +118,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface */ private $vendors; - private $automaticInstanceofDefinitions = array(); + private $autoconfiguredInstanceof = array(); public function __construct(ParameterBagInterface $parameterBag = null) { @@ -641,12 +641,12 @@ public function merge(ContainerBuilder $container) } } - foreach ($container->getAutomaticInstanceofDefinitions() as $interface => $childDefinition) { - if (isset($this->automaticInstanceofDefinitions[$interface])) { + foreach ($container->getAutoconfiguredInstanceof() as $interface => $childDefinition) { + if (isset($this->autoconfiguredInstanceof[$interface])) { throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.', $interface)); } - $this->automaticInstanceofDefinitions[$interface] = $childDefinition; + $this->autoconfiguredInstanceof[$interface] = $childDefinition; } } @@ -1272,15 +1272,16 @@ public function getExpressionLanguageProviders() * Returns a ChildDefinition that will be used for autoconfiguring the interface/class. * * @param string $interface The class or interface to match + * * @return ChildDefinition */ public function registerForAutoconfiguration($interface) { - if (!isset($this->automaticInstanceofDefinitions[$interface])) { - $this->automaticInstanceofDefinitions[$interface] = new ChildDefinition(''); + if (!isset($this->autoconfiguredInstanceof[$interface])) { + $this->autoconfiguredInstanceof[$interface] = new ChildDefinition(''); } - return $this->automaticInstanceofDefinitions[$interface]; + return $this->autoconfiguredInstanceof[$interface]; } /** @@ -1288,9 +1289,9 @@ public function registerForAutoconfiguration($interface) * * @return ChildDefinition[] */ - public function getAutomaticInstanceofDefinitions() + public function getAutoconfiguredInstanceof() { - return $this->automaticInstanceofDefinitions; + return $this->autoconfiguredInstanceof; } /** diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index e183fdad42167..4e3c37d87eb72 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -79,7 +79,7 @@ protected function setDefinition($id, Definition $definition) } $this->instanceof[$id] = $definition; } else { - $this->container->setDefinition($id, $definition->setInstanceofConditionals($this->instanceof)); + $this->container->setDefinition($id, $definition instanceof ChildDefinition ? $definition : $definition->setInstanceofConditionals($this->instanceof)); } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 9f730472d72e1..b9a0f004e73e1 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -215,6 +215,14 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults = if ($this->isLoadingInstanceof) { $definition = new ChildDefinition(''); } elseif ($parent = $service->getAttribute('parent')) { + if (!empty($this->instanceof)) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot use the "parent" option in the same file where "instanceof" configuration is defined as using both is not supported. Try moving your child definitions to a different file.', $service->getAttribute('id'))); + } + + if (!empty($defaults)) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot use the "parent" option in the same file where "defaults" configuration is defined as using both is not supported. Try moving your child definitions to a different file.', $service->getAttribute('id'))); + } + $definition = new ChildDefinition($parent); if ($value = $service->getAttribute('inherit-tags')) { @@ -255,7 +263,11 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults = } if ($value = $service->getAttribute('autoconfigure')) { - $definition->setAutoconfigured(XmlUtils::phpize($value)); + if (!$definition instanceof ChildDefinition) { + $definition->setAutoconfigured(XmlUtils::phpize($value)); + } elseif ($value = XmlUtils::phpize($value)) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try setting autoconfigure="false" for the service.', $service->getAttribute('id'))); + } } if ($files = $this->getChildren($service, 'file')) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index e7703aabf89a9..7ee19e7f6c50c 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -88,7 +88,6 @@ class YamlFileLoader extends FileLoader 'calls' => 'calls', 'tags' => 'tags', 'autowire' => 'autowire', - 'autoconfigure' => 'autoconfigure', ); private static $defaultsKeywords = array( @@ -357,6 +356,14 @@ private function parseDefinition($id, $service, $file, array $defaults) if ($this->isLoadingInstanceof) { $definition = new ChildDefinition(''); } elseif (isset($service['parent'])) { + if (!empty($this->instanceof)) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot use the "parent" option in the same file where "_instanceof" configuration is defined as using both is not supported. Try moving your child definitions to a different file.', $id)); + } + + if (!empty($defaults)) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot use the "parent" option in the same file where "_defaults" configuration is defined as using both is not supported. Try moving your child definitions to a different file.', $id)); + } + $definition = new ChildDefinition($service['parent']); $inheritTag = isset($service['inherit_tags']) ? $service['inherit_tags'] : (isset($defaults['inherit_tags']) ? $defaults['inherit_tags'] : null); @@ -518,7 +525,11 @@ private function parseDefinition($id, $service, $file, array $defaults) } if (isset($service['autoconfigure'])) { - $definition->setAutoconfigured($service['autoconfigure']); + if (!$definition instanceof ChildDefinition) { + $definition->setAutoconfigured($service['autoconfigure']); + } elseif ($service['autoconfigure']) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try setting "autoconfigure: false" for the service.', $id)); + } } if (array_key_exists('resource', $service)) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php index 0890f53cdbbbe..a367a8b2c4d03 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ChildDefinitionTest.php @@ -132,4 +132,22 @@ public function testDefinitionDecoratorAliasExistsForBackwardsCompatibility() { $this->assertInstanceOf(ChildDefinition::class, new DefinitionDecorator('foo')); } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\BadMethodCallException + */ + public function testCannotCallSetAutoconfigured() + { + $def = new ChildDefinition('foo'); + $def->setAutoconfigured(true); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\BadMethodCallException + */ + public function testCannotCallSetInstanceofConditionals() + { + $def = new ChildDefinition('foo'); + $def->setInstanceofConditionals(array('Foo' => new ChildDefinition(''))); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php index 9d75c0b962186..a1856dfe84828 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php @@ -117,64 +117,86 @@ public function testProcessInlinesWhenThereAreMultipleReferencesButFromTheSameDe $this->assertFalse($container->hasDefinition('c'), 'Service C was not inlined.'); } - public function testInstanceofDefaultsAndParentDefinitionResolution() + /** + * @dataProvider getYamlCompileTests + */ + public function testYamlContainerCompiles($directory, $actualServiceId, $expectedServiceId, ContainerBuilder $mainContainer = null) { - $container = new ContainerBuilder(); + // allow a container to be passed in, which might have autoconfigure settings + $container = $mainContainer ? $mainContainer : new ContainerBuilder(); $container->setResourceTracking(false); + $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Fixtures/yaml/integration/'.$directory)); + $loader->load('main.yml'); + $container->compile(); + $actualService = $container->getDefinition($actualServiceId); - // loading YAML with an expressive test-case in that file - $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Fixtures/yaml')); - $loader->load('services_defaults_instanceof_parent.yml'); + // create a fresh ContainerBuilder, to avoid autoconfigure stuff + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Fixtures/yaml/integration/'.$directory)); + $loader->load('expected.yml'); $container->compile(); + $expectedService = $container->getDefinition($expectedServiceId); + + // reset changes, we don't care if these differ + $actualService->setChanges(array()); + $expectedService->setChanges(array()); + + $this->assertEquals($expectedService, $actualService); + } + + public function getYamlCompileTests() + { + $container = new ContainerBuilder(); + $container->registerForAutoconfiguration(IntegrationTestStub::class); + yield array( + 'autoconfigure_child_not_applied', + 'child_service', + 'child_service_expected', + $container, + ); + + $container = new ContainerBuilder(); + $container->registerForAutoconfiguration(IntegrationTestStub::class); + yield array( + 'autoconfigure_parent_child', + 'child_service', + 'child_service_expected', + $container, + ); + + $container = new ContainerBuilder(); + $container->registerForAutoconfiguration(IntegrationTestStub::class) + ->addTag('from_autoconfigure'); + yield array( + 'autoconfigure_parent_child_tags', + 'child_service', + 'child_service_expected', + $container, + ); + + yield array( + 'child_parent', + 'child_service', + 'child_service_expected', + ); - // instanceof overrides defaults - $simpleService = $container->getDefinition('service_simple'); - $this->assertFalse($simpleService->isAutowired()); - $this->assertFalse($simpleService->isAutoconfigured()); - $this->assertFalse($simpleService->isShared()); - - // all tags are kept - $this->assertEquals( - array( - 'foo_tag' => array(array('tag_option' => 'from_service'), array('tag_option' => 'from_instanceof')), - 'bar_tag' => array(array()), - ), - $simpleService->getTags() + yield array( + 'defaults_instanceof_importance', + 'main_service', + 'main_service_expected', ); - // calls are all kept, but service-level calls are last - $this->assertEquals( - array( - // from instanceof - array('setSunshine', array('bright')), - // from service - array('enableSummer', array(true)), - array('setSunshine', array('warm')), - ), - $simpleService->getMethodCalls() + yield array( + 'defaults_parent_child', + 'child_service', + 'child_service_expected', ); - // service override instanceof - $overrideService = $container->getDefinition('service_override_instanceof'); - $this->assertTrue($overrideService->isAutowired()); - $this->assertTrue($overrideService->isAutoconfigured()); - - // children definitions get no instanceof - $childDef = $container->getDefinition('child_service'); - $this->assertEmpty($childDef->getTags()); - - $childDef2 = $container->getDefinition('child_service_with_parent_instanceof'); - // taken from instanceof applied to parent - $this->assertFalse($childDef2->isAutowired()); - // override the instanceof - $this->assertTrue($childDef2->isShared()); - // tags inherit like normal - $this->assertEquals( - array( - 'foo_tag' => array(array('tag_option' => 'from_child_def'), array('tag_option' => 'from_parent_def'), array('tag_option' => 'from_instanceof')), - 'bar_tag' => array(array()), - ), - $childDef2->getTags() + yield array( + 'instanceof_parent_child', + 'child_service', + 'child_service_expected', ); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php index a2dd9b130d536..9c64c502f659a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php @@ -381,23 +381,6 @@ public function testProcessSetsArguments() $this->assertSame(array(2, 1, 'foo' => 3), $def->getArguments()); } - public function testSetAutoconfiguredOnServiceHasParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass') - ->setAutoconfigured(true) - ; - - $container->setDefinition('child1', new ChildDefinition('parent')) - ->setAutoconfigured(false) - ; - - $this->process($container); - - $this->assertFalse($container->getDefinition('child1')->isAutoconfigured()); - } - public function testSetAutoconfiguredOnServiceIsParent() { $container = new ContainerBuilder(); @@ -410,7 +393,7 @@ public function testSetAutoconfiguredOnServiceIsParent() $this->process($container); - $this->assertTrue($container->getDefinition('child1')->isAutoconfigured()); + $this->assertFalse($container->getDefinition('child1')->isAutoconfigured()); } protected function process(ContainerBuilder $container) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php index 03ef2d40b8250..6467ac7ed4d71 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php @@ -56,9 +56,6 @@ public function testProcessInheritance() )); $def = (new ChildDefinition('parent'))->setClass(self::class); - $def->setInstanceofConditionals(array( - parent::class => (new ChildDefinition(''))->addMethodCall('foo', array('baz')), - )); $container->setDefinition('child', $def); (new ResolveInstanceofConditionalsPass())->process($container); @@ -108,7 +105,7 @@ public function testProcessHandlesMultipleInheritance() $this->assertTrue($def->isShared()); } - public function testProcessUsesAutomaticInstanceofDefinitions() + public function testProcessUsesAutoconfiguredInstanceof() { $container = new ContainerBuilder(); $def = $container->register('normal_service', self::class); @@ -119,24 +116,24 @@ public function testProcessUsesAutomaticInstanceofDefinitions() )); $def->setAutoconfigured(true); $container->registerForAutoconfiguration(parent::class) - ->addTag('automatic_instanceof_tag') + ->addTag('autoconfigured_tag') ->setAutowired(true) - ->setFactory('automatically_set_factory'); + ->setFactory('autoconfigured_factory'); (new ResolveInstanceofConditionalsPass())->process($container); (new ResolveTagsInheritancePass())->process($container); (new ResolveDefinitionTemplatesPass())->process($container); $def = $container->getDefinition('normal_service'); - // autowired thanks to the automatic instanceof + // autowired thanks to the autoconfigured instanceof $this->assertTrue($def->isAutowired()); // factory from the specific instanceof overrides global one $this->assertEquals('locally_set_factory', $def->getFactory()); // tags are merged, the locally set one is first - $this->assertSame(array('local_instanceof_tag' => array(array()), 'automatic_instanceof_tag' => array(array())), $def->getTags()); + $this->assertSame(array('local_instanceof_tag' => array(array()), 'autoconfigured_tag' => array(array())), $def->getTags()); } - public function testProcessDoesNotUseAutomaticInstanceofDefinitionsIfNotEnabled() + public function testProcessDoesNotUseAutoconfiguredInstanceofIfNotEnabled() { $container = new ContainerBuilder(); $def = $container->register('normal_service', self::class); @@ -151,7 +148,6 @@ public function testProcessDoesNotUseAutomaticInstanceofDefinitionsIfNotEnabled( (new ResolveDefinitionTemplatesPass())->process($container); $def = $container->getDefinition('normal_service'); - // no automatic_tag, it was not enabled on the Definition $this->assertFalse($def->isAutowired()); } @@ -171,7 +167,7 @@ public function testBadInterfaceThrowsException() (new ResolveInstanceofConditionalsPass())->process($container); } - public function testBadInterfaceForAutomaticInstanceofIsOkException() + public function testBadInterfaceForAutomaticInstanceofIsOk() { $container = new ContainerBuilder(); $container->register('normal_service', self::class) @@ -182,4 +178,30 @@ public function testBadInterfaceForAutomaticInstanceofIsOkException() (new ResolveInstanceofConditionalsPass())->process($container); $this->assertTrue($container->hasDefinition('normal_service')); } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Autoconfigured instanceof for type "PHPUnit\Framework\TestCase" defines method calls but these are not supported and should be removed. + */ + public function testProcessThrowsExceptionForAutoconfiguredCalls() + { + $container = new ContainerBuilder(); + $container->registerForAutoconfiguration(parent::class) + ->addMethodCall('setFoo'); + + (new ResolveInstanceofConditionalsPass())->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Autoconfigured instanceof for type "PHPUnit\Framework\TestCase" defines arguments but these are not supported and should be removed. + */ + public function testProcessThrowsExceptionForArguments() + { + $container = new ContainerBuilder(); + $container->registerForAutoconfiguration(parent::class) + ->addArgument('bar'); + + (new ResolveInstanceofConditionalsPass())->process($container); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 552898f73d555..05c549bcce2fe 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -573,7 +573,7 @@ public function testMerge() $childDefA = $container->registerForAutoconfiguration('AInterface'); $childDefB = $config->registerForAutoconfiguration('BInterface'); $container->merge($config); - $this->assertSame(array('AInterface' => $childDefA, 'BInterface' => $childDefB), $container->getAutomaticInstanceofDefinitions()); + $this->assertSame(array('AInterface' => $childDefA, 'BInterface' => $childDefB), $container->getAutoconfiguredInstanceof()); } /** @@ -1123,7 +1123,7 @@ public function testRegisterForAutoconfiguration() $container = new ContainerBuilder(); $childDefA = $container->registerForAutoconfiguration('AInterface'); $childDefB = $container->registerForAutoconfiguration('BInterface'); - $this->assertSame(array('AInterface' => $childDefA, 'BInterface' => $childDefB), $container->getAutomaticInstanceofDefinitions()); + $this->assertSame(array('AInterface' => $childDefA, 'BInterface' => $childDefB), $container->getAutoconfiguredInstanceof()); // when called multiple times, the same instance is returned $this->assertSame($childDefA, $container->registerForAutoconfiguration('AInterface')); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml index 4ab511ed92dbf..81f1c1a4409a1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml @@ -8,11 +8,5 @@ - - - - - - diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml new file mode 100644 index 0000000000000..103045d38fcb3 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_defaults_with_parent.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_defaults_with_parent.xml new file mode 100644 index 0000000000000..875ed6d51f996 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_defaults_with_parent.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof_with_parent.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof_with_parent.xml new file mode 100644 index 0000000000000..67ce6917249bd --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof_with_parent.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/_child.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/_child.yml new file mode 100644 index 0000000000000..89d8b914491eb --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/_child.yml @@ -0,0 +1,4 @@ +services: + child_service: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + parent: parent_service diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml new file mode 100644 index 0000000000000..ca08caad673f7 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml @@ -0,0 +1,10 @@ +services: + child_service_expected: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + # the parent has autoconfigure true, but that does not cascade to the child + autoconfigure: false + # an autoconfigured "instanceof" is setup for IntegrationTestStub + # but its calls are NOT added, because the class was only + # set on the parent, not the child + #calls: + # - [enableSummer, [true]] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml new file mode 100644 index 0000000000000..02533bf0f5739 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml @@ -0,0 +1,7 @@ +imports: + - { resource: _child.yml } + +services: + parent_service: + autoconfigure: true + abstract: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/_child.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/_child.yml new file mode 100644 index 0000000000000..5319c2045f93a --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/_child.yml @@ -0,0 +1,3 @@ +services: + child_service: + parent: parent_service diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml new file mode 100644 index 0000000000000..c1dca0763cc90 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml @@ -0,0 +1,5 @@ +services: + child_service_expected: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + # autoconfigure is set on the parent, but not on the child + autoconfigure: false diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml new file mode 100644 index 0000000000000..ab9877d16b9e7 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml @@ -0,0 +1,7 @@ +imports: + - { resource: _child.yml } + +services: + parent_service: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + autoconfigure: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/_child.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/_child.yml new file mode 100644 index 0000000000000..5319c2045f93a --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/_child.yml @@ -0,0 +1,3 @@ +services: + child_service: + parent: parent_service diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml new file mode 100644 index 0000000000000..02cf0037e215d --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml @@ -0,0 +1,6 @@ +services: + child_service_expected: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + # from an autoconfigured "instanceof" applied to parent class + # but NOT inherited down to child + # tags: [from_autoconfigure] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml new file mode 100644 index 0000000000000..ab9877d16b9e7 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml @@ -0,0 +1,7 @@ +imports: + - { resource: _child.yml } + +services: + parent_service: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + autoconfigure: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/child_parent/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/child_parent/expected.yml new file mode 100644 index 0000000000000..54cd91c2022a8 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/child_parent/expected.yml @@ -0,0 +1,9 @@ +services: + # child_service in the end should be identical to this + child_service_expected: + class: stdClass + autowire: false + public: true + lazy: true + # ONLY the child tag, the parent tag does not inherit + tags: [from_child] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/child_parent/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/child_parent/main.yml new file mode 100644 index 0000000000000..edaa3a3b43993 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/child_parent/main.yml @@ -0,0 +1,13 @@ +services: + parent_service: + abstract: true + lazy: true + autowire: false + public: false + tags: [from_parent] + + child_service: + class: stdClass + parent: parent_service + public: true + tags: [from_child] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/expected.yml new file mode 100644 index 0000000000000..4edeffa7c3053 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/expected.yml @@ -0,0 +1,27 @@ +services: + # main_service should look like this in the end + main_service_expected: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + # _instanceof overrides _defaults + autowire: false + # inherited from _defaults + autoconfigure: true + # from _instanceof + shared: false + # service definition overrides _instanceof + public: true + + tags: + - { name: foo_tag, tag_option: from_service } + # these 2 are from instanceof + - { name: foo_tag, tag_option: from_instanceof } + - { name: bar_tag } + # the tag from defaults do NOT cascade (but see #22530) + # - { name: from_defaults } + # calls from instanceof are kept, but this comes later + calls: + # first call is from instanceof + - [setSunshine, [bright]] + # + - [enableSummer, [true]] + - [setSunshine, [warm]] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/main.yml new file mode 100644 index 0000000000000..406a351d1d3ef --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/main.yml @@ -0,0 +1,30 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: true + tags: [from_defaults] + + _instanceof: + Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStubParent: + autowire: false + shared: false + public: false + tags: + - { name: foo_tag, tag_option: from_instanceof } + calls: + - [setSunshine, [bright]] + + Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub: + tags: + - { name: bar_tag } + + main_service: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + public: true + tags: + - { name: foo_tag, tag_option: from_service } + # calls from instanceof are kept, but this comes later + calls: + - [enableSummer, [true]] + - [setSunshine, [warm]] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/_child.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/_child.yml new file mode 100644 index 0000000000000..86ef83c26dfca --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/_child.yml @@ -0,0 +1,4 @@ +services: + # loaded here to avoid defaults in other file + child_service: + parent: parent_service diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml new file mode 100644 index 0000000000000..012672ff8b8fb --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml @@ -0,0 +1,6 @@ +services: + child_service_expected: + class: stdClass + # _defaults is applied to the parent, but autoconfigure: true + # does not cascade to the child + autoconfigure: false diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml new file mode 100644 index 0000000000000..8b90b4e985892 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml @@ -0,0 +1,9 @@ +imports: + - { resource: _child.yml } + +services: + _defaults: + autoconfigure: true + + parent_service: + class: stdClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/_child.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/_child.yml new file mode 100644 index 0000000000000..86ef83c26dfca --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/_child.yml @@ -0,0 +1,4 @@ +services: + # loaded here to avoid defaults in other file + child_service: + parent: parent_service diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml new file mode 100644 index 0000000000000..074ed01d03c4c --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml @@ -0,0 +1,7 @@ +services: + child_service_expected: + class: stdClass + # applied to _instanceof of parent + autowire: true + # from _instanceof, applies to parent, but does NOT inherit to here + # tags: [from_instanceof] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml new file mode 100644 index 0000000000000..44cf9b0045d40 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml @@ -0,0 +1,11 @@ +imports: + - { resource: _child.yml } + +services: + _instanceof: + stdClass: + autowire: true + tags: [from_instanceof] + + parent_service: + class: stdClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml index 99f72f1f94ac4..cacebf469cf2e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml @@ -22,19 +22,6 @@ services: autowire: false tags: [] - no_defaults_child: - parent: no_defaults - autowire: ~ - tags: - - name: bar - - with_defaults_child: - parent: with_defaults - public: true - inherit_tags: true - tags: - - name: baz - with_defaults_aliased: alias: with_defaults diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml new file mode 100644 index 0000000000000..c6e3080192a7e --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml @@ -0,0 +1,8 @@ +services: + parent_service: + class: stdClass + + child_service: + class: stdClass + autoconfigure: true + parent: parent_service diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_instanceof_parent.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_instanceof_parent.yml deleted file mode 100644 index 9e332a8405b0b..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_instanceof_parent.yml +++ /dev/null @@ -1,57 +0,0 @@ -services: - _defaults: - autowire: true - autoconfigure: true - - _instanceof: - Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStubParent: - # should override _defaults - autowire: false - autoconfigure: false - shared: false - tags: - - { name: foo_tag, tag_option: from_instanceof } - calls: - - [setSunshine, [bright]] - - # a second instanceof that will be applied - Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub: - tags: - - { name: bar_tag } - - service_simple: - class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub - tags: - - { name: foo_tag, tag_option: from_service } - # calls from instanceof are kept, but this comes later - calls: - - [enableSummer, [true]] - - [setSunshine, [warm]] - - service_override_instanceof: - class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub - # override instanceof - autowire: true - autoconfigure: true - - parent_service: - abstract: true - lazy: true - - # instanceof will not be applied to this - child_service: - class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub - parent: parent_service - - parent_service_with_class: - abstract: true - class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub - tags: - - { name: foo_tag, tag_option: from_parent_def } - - child_service_with_parent_instanceof: - parent: parent_service_with_class - shared: true - inherit_tags: true - tags: - - { name: foo_tag, tag_option: from_child_def } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_with_parent.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_with_parent.yml new file mode 100644 index 0000000000000..28dec4ce91791 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_defaults_with_parent.yml @@ -0,0 +1,10 @@ +services: + _defaults: + autowire: true + + parent_service: + class: stdClass + + child_service: + class: stdClass + parent: parent_service diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml new file mode 100644 index 0000000000000..fb21cdf2fb674 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml @@ -0,0 +1,11 @@ +services: + _instanceof: + FooInterface: + autowire: true + + parent_service: + class: stdClass + + child_service: + class: stdClass + parent: parent_service diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 3b6ddd7fcb862..6f85d2ee16be4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -651,20 +651,13 @@ public function testDefaults() $this->assertArrayNotHasKey('public', $container->getDefinition('with_defaults')->getChanges()); $this->assertArrayNotHasKey('autowire', $container->getDefinition('with_defaults')->getChanges()); - $this->assertArrayNotHasKey('public', $container->getDefinition('no_defaults_child')->getChanges()); - $this->assertArrayNotHasKey('autowire', $container->getDefinition('no_defaults_child')->getChanges()); - $container->compile(); $this->assertTrue($container->getDefinition('no_defaults')->isPublic()); - $this->assertTrue($container->getDefinition('no_defaults_child')->isPublic()); $this->assertSame(array(), $container->getDefinition('no_defaults')->getTags()); - $this->assertSame(array('bar' => array(array())), $container->getDefinition('no_defaults_child')->getTags()); - $this->assertSame(array('baz' => array(array()), 'foo' => array(array())), $container->getDefinition('with_defaults_child')->getTags()); $this->assertFalse($container->getDefinition('no_defaults')->isAutowired()); - $this->assertFalse($container->getDefinition('no_defaults_child')->isAutowired()); } public function testNamedArguments() @@ -694,6 +687,42 @@ public function testInstanceof() $this->assertSame(array('foo' => array(array()), 'bar' => array(array())), $definition->getTags()); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The service "child_service" cannot use the "parent" option in the same file where "instanceof" configuration is defined as using both is not supported. Try moving your child definitions to a different file. + */ + public function testInstanceOfAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_instanceof_with_parent.xml'); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The service "child_service" cannot have a "parent" and also have "autoconfigure". Try setting autoconfigure="false" for the service. + */ + public function testAutoConfigureAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_autoconfigure_with_parent.xml'); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The service "child_service" cannot use the "parent" option in the same file where "defaults" configuration is defined as using both is not supported. Try moving your child definitions to a different file. + */ + public function testDefaultsAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_defaults_with_parent.xml'); + $container->compile(); + } + public function testAutoConfigureInstanceof() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 87a05eb2abd1f..e20575d2aa8f7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -403,9 +403,6 @@ public function testDefaults() $this->assertFalse($container->getAlias('with_defaults_aliased')->isPublic()); $this->assertFalse($container->getAlias('with_defaults_aliased_short')->isPublic()); - $this->assertArrayNotHasKey('public', $container->getDefinition('no_defaults_child')->getChanges()); - $this->assertArrayNotHasKey('autowire', $container->getDefinition('no_defaults_child')->getChanges()); - $this->assertFalse($container->getDefinition('Acme\WithShortCutArgs')->isPublic()); $this->assertSame(array('foo' => array(array())), $container->getDefinition('Acme\WithShortCutArgs')->getTags()); $this->assertTrue($container->getDefinition('Acme\WithShortCutArgs')->isAutowired()); @@ -414,16 +411,12 @@ public function testDefaults() $this->assertTrue($container->getDefinition('with_null')->isPublic()); $this->assertTrue($container->getDefinition('no_defaults')->isPublic()); - $this->assertTrue($container->getDefinition('no_defaults_child')->isPublic()); $this->assertSame(array(), $container->getDefinition('with_null')->getTags()); $this->assertSame(array(), $container->getDefinition('no_defaults')->getTags()); - $this->assertSame(array('bar' => array(array())), $container->getDefinition('no_defaults_child')->getTags()); - $this->assertSame(array('baz' => array(array()), 'foo' => array(array())), $container->getDefinition('with_defaults_child')->getTags()); $this->assertTrue($container->getDefinition('with_null')->isAutowired()); $this->assertFalse($container->getDefinition('no_defaults')->isAutowired()); - $this->assertFalse($container->getDefinition('no_defaults_child')->isAutowired()); } public function testNamedArguments() @@ -455,6 +448,42 @@ public function testInstanceof() $this->assertSame(array('foo' => array(array()), 'bar' => array(array())), $definition->getTags()); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The service "child_service" cannot use the "parent" option in the same file where "_instanceof" configuration is defined as using both is not supported. Try moving your child definitions to a different file. + */ + public function testInstanceOfAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_instanceof_with_parent.yml'); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The service "child_service" cannot have a "parent" and also have "autoconfigure". Try setting "autoconfigure: false" for the service. + */ + public function testAutoConfigureAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_autoconfigure_with_parent.yml'); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The service "child_service" cannot use the "parent" option in the same file where "_defaults" configuration is defined as using both is not supported. Try moving your child definitions to a different file. + */ + public function testDefaultsAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_defaults_with_parent.yml'); + $container->compile(); + } + /** * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException * @expectedExceptionMessage The value of the "decorates" option for the "bar" service must be the id of the service without the "@" prefix (replace "@foo" with "foo").