From 2fb601983f01e13ac9d91229052c37f759a799ed Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 3 Feb 2017 20:41:28 +0100 Subject: [PATCH 1/2] [DependencyInjection] Add "instanceof" section for local interface-defined configs --- .../DependencyInjection/CHANGELOG.md | 1 + .../DependencyInjection/ChildDefinition.php | 20 ++++ .../Compiler/PassConfig.php | 1 + .../ResolveDefinitionInheritancePass.php | 106 ++++++++++++++++++ .../ResolveDefinitionTemplatesPass.php | 41 ++++--- .../DependencyInjection/Definition.php | 31 ++++- .../DependencyInjection/Loader/FileLoader.php | 20 +++- .../Loader/XmlFileLoader.php | 24 +++- .../Loader/YamlFileLoader.php | 67 ++++++++--- .../schema/dic/services/services-1.0.xsd | 21 ++++ 10 files changed, 285 insertions(+), 47 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionInheritancePass.php diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index c118ed3d2a..b34002951b 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 3.3.0 ----- + * [EXPERIMENTAL] added "instanceof" section for local interface-defined configs * [EXPERIMENTAL] added "service-locator" argument for lazy loading a set of identified values and services * [EXPERIMENTAL] added prototype services for PSR4-based discovery and registration * added `ContainerBuilder::getReflectionClass()` for retrieving and tracking reflection class info diff --git a/src/Symfony/Component/DependencyInjection/ChildDefinition.php b/src/Symfony/Component/DependencyInjection/ChildDefinition.php index ba38a0b5cf..58ff5762b4 100644 --- a/src/Symfony/Component/DependencyInjection/ChildDefinition.php +++ b/src/Symfony/Component/DependencyInjection/ChildDefinition.php @@ -119,6 +119,16 @@ class ChildDefinition extends Definition return parent::setFile($file); } + /** + * {@inheritdoc} + */ + public function setShared($boolean) + { + $this->changes['shared'] = true; + + return parent::setShared($boolean); + } + /** * {@inheritdoc} */ @@ -139,6 +149,16 @@ class ChildDefinition extends Definition return parent::setLazy($boolean); } + /** + * {@inheritdoc} + */ + public function setAbstract($boolean) + { + $this->changes['abstract'] = true; + + return parent::setAbstract($boolean); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 4be4345cd9..4c7422b35b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -42,6 +42,7 @@ class PassConfig $this->beforeOptimizationPasses = array( 100 => array( $resolveClassPass = new ResolveClassPass(), + new ResolveDefinitionInheritancePass(), ), ); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionInheritancePass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionInheritancePass.php new file mode 100644 index 0000000000..19d06579d5 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionInheritancePass.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Definition; + +/** + * Applies tags and instanceof inheritance to definitions. + * + * @author Nicolas Grekas + */ +class ResolveDefinitionInheritancePass extends AbstractRecursivePass +{ + protected function processValue($value, $isRoot = false) + { + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + if ($value instanceof ChildDefinition) { + $this->resolveDefinition($value); + } + $class = $value->getClass(); + if (!$class || false !== strpos($class, '%') || !$instanceof = $value->getInstanceofConditionals()) { + return parent::processValue($value, $isRoot); + } + + foreach ($instanceof as $interface => $definition) { + if ($interface !== $class && (!$this->container->getReflectionClass($interface) || !$this->container->getReflectionClass($class))) { + continue; + } + if ($interface === $class || is_subclass_of($class, $interface)) { + $this->mergeDefinition($value, $definition); + } + } + + return parent::processValue($value, $isRoot); + } + + /** + * Populates the class and tags from parent definitions. + */ + private function resolveDefinition(ChildDefinition $definition) + { + if (!$this->container->has($parent = $definition->getParent())) { + return; + } + + $parentDef = $this->container->findDefinition($parent); + if ($parentDef instanceof ChildDefinition) { + $this->resolveDefinition($parentDef); + } + + if (!isset($definition->getChanges()['class'])) { + $definition->setClass($parentDef->getClass()); + } + + // append parent tags when inheriting is enabled + if ($definition->getInheritTags()) { + foreach ($parentDef->getTags() as $k => $v) { + foreach ($v as $v) { + $definition->addTag($k, $v); + } + } + } + + $definition->setInheritTags(false); + } + + private function mergeDefinition(Definition $def, ChildDefinition $definition) + { + $changes = $definition->getChanges(); + if (isset($changes['shared'])) { + $def->setShared($definition->isShared()); + } + if (isset($changes['abstract'])) { + $def->setAbstract($definition->isAbstract()); + } + if (isset($changes['autowired_calls'])) { + $autowiredCalls = $def->getAutowiredCalls(); + } + + ResolveDefinitionTemplatesPass::mergeDefinition($def, $definition); + + // merge autowired calls + if (isset($changes['autowired_calls'])) { + $def->setAutowiredCalls(array_merge($autowiredCalls, $def->getAutowiredCalls())); + } + + // merge tags + foreach ($definition->getTags() as $k => $v) { + foreach ($v as $v) { + $def->addTag($k, $v); + } + } + } +} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php index 375308835b..a1e6eb9533 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -103,6 +103,26 @@ class ResolveDefinitionTemplatesPass extends AbstractRecursivePass $def->setLazy($parentDef->isLazy()); $def->setAutowiredCalls($parentDef->getAutowiredCalls()); + self::mergeDefinition($def, $definition); + + // merge autowiring types + foreach ($definition->getAutowiringTypes(false) as $autowiringType) { + $def->addAutowiringType($autowiringType); + } + + // these attributes are always taken from the child + $def->setAbstract($definition->isAbstract()); + $def->setShared($definition->isShared()); + $def->setTags($definition->getTags()); + + return $def; + } + + /** + * @internal + */ + public static function mergeDefinition(Definition $def, ChildDefinition $definition) + { // overwrite with values specified in the decorator $changes = $definition->getChanges(); if (isset($changes['class'])) { @@ -168,26 +188,5 @@ class ResolveDefinitionTemplatesPass extends AbstractRecursivePass foreach ($definition->getOverriddenGetters() as $k => $v) { $def->setOverriddenGetter($k, $v); } - - // merge autowiring types - foreach ($definition->getAutowiringTypes(false) as $autowiringType) { - $def->addAutowiringType($autowiringType); - } - - // these attributes are always taken from the child - $def->setAbstract($definition->isAbstract()); - $def->setShared($definition->isShared()); - $def->setTags($definition->getTags()); - - // append parent tags when inheriting is enabled - if ($definition->getInheritTags()) { - foreach ($parentDef->getTags() as $k => $v) { - foreach ($v as $v) { - $def->addTag($k, $v); - } - } - } - - return $def; } } diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 7cd781442c..17a294b4d2 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -30,6 +30,7 @@ class Definition private $properties = array(); private $calls = array(); private $getters = array(); + private $instanceof = array(); private $configurator; private $tags = array(); private $public = true; @@ -363,6 +364,32 @@ class Definition return $this->getters; } + /** + * Sets the definition templates to conditionally apply on the current definition, keyed by parent interface/class. + * + * @param $instanceof ChildDefinition[] + * + * @experimental in version 3.3 + */ + public function setInstanceofConditionals(array $instanceof) + { + $this->instanceof = $instanceof; + + return $this; + } + + /** + * Gets the definition templates to conditionally apply on the current definition, keyed by parent interface/class. + * + * @return ChildDefinition[] + * + * @experimental in version 3.3 + */ + public function getInstanceofConditionals() + { + return $this->instanceof; + } + /** * Sets tags for this definition. * @@ -736,9 +763,7 @@ class Definition */ public function setAutowired($autowired) { - $this->autowiredCalls = $autowired ? array('__construct') : array(); - - return $this; + return $this->setAutowiredCalls($autowired ? array('__construct') : array()); } /** diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index 68cf818c5b..ba0b77f0f5 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Loader; use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; @@ -29,6 +30,8 @@ use Symfony\Component\Finder\Glob; abstract class FileLoader extends BaseFileLoader { protected $container; + protected $isLoadingInstanceof = false; + protected $instanceof = array(); /** * @param ContainerBuilder $container A ContainerBuilder instance @@ -80,7 +83,22 @@ abstract class FileLoader extends BaseFileLoader $prototype = serialize($prototype); foreach ($classes as $class) { - $this->container->setDefinition($class, unserialize($prototype)); + $this->setDefinition($class, unserialize($prototype)); + } + } + + /** + * @experimental in version 3.3 + */ + protected function setDefinition($id, Definition $definition) + { + if ($this->isLoadingInstanceof) { + if (!$definition instanceof ChildDefinition) { + throw new InvalidArgumentException(sprintf('Invalid type definition "%s": ChildDefinition expected, "%s" given.', $id, get_class($definition))); + } + $this->instanceof[$id] = $definition; + } else { + $this->container->setDefinition($id, $definition->setInstanceofConditionals($this->instanceof)); } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 0902087a05..0809ab2630 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -57,7 +57,11 @@ class XmlFileLoader extends FileLoader $this->loadFromExtensions($xml); // services - $this->parseDefinitions($xml, $path); + try { + $this->parseDefinitions($xml, $path); + } finally { + $this->instanceof = array(); + } } /** @@ -126,13 +130,21 @@ class XmlFileLoader extends FileLoader } $this->setCurrentDir(dirname($file)); + $this->instanceof = array(); + $this->isLoadingInstanceof = true; + $instanceof = $xpath->query('//container:services/container:instanceof'); + foreach ($instanceof as $service) { + $this->setDefinition((string) $service->getAttribute('id'), $this->parseDefinition($service, $file, array())); + } + + $this->isLoadingInstanceof = false; $defaults = $this->getServiceDefaults($xml, $file); foreach ($services as $service) { if (null !== $definition = $this->parseDefinition($service, $file, $defaults)) { if ('prototype' === $service->tagName) { $this->registerClasses($definition, (string) $service->getAttribute('namespace'), (string) $service->getAttribute('resource')); } else { - $this->container->setDefinition((string) $service->getAttribute('id'), $definition); + $this->setDefinition((string) $service->getAttribute('id'), $definition); } } } @@ -209,7 +221,9 @@ class XmlFileLoader extends FileLoader return; } - if ($parent = $service->getAttribute('parent')) { + if ($this->isLoadingInstanceof) { + $definition = new ChildDefinition(''); + } elseif ($parent = $service->getAttribute('parent')) { $definition = new ChildDefinition($parent); if ($value = $service->getAttribute('inherit-tags')) { @@ -247,7 +261,7 @@ class XmlFileLoader extends FileLoader $definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null); } - $definition->setArguments($this->getArgumentsAsPhp($service, 'argument', false, (bool) $parent)); + $definition->setArguments($this->getArgumentsAsPhp($service, 'argument', false, $definition instanceof ChildDefinition)); $definition->setProperties($this->getArgumentsAsPhp($service, 'property')); $definition->setOverriddenGetters($this->getArgumentsAsPhp($service, 'getter')); @@ -422,7 +436,7 @@ class XmlFileLoader extends FileLoader uksort($definitions, 'strnatcmp'); foreach (array_reverse($definitions) as $id => list($domElement, $file, $wild)) { if (null !== $definition = $this->parseDefinition($domElement, $file)) { - $this->container->setDefinition($id, $definition); + $this->setDefinition($id, $definition); } if (true === $wild) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index e6b771faf2..f5e97f3cb9 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -81,6 +81,22 @@ class YamlFileLoader extends FileLoader 'autowire' => 'autowire', ); + private static $instanceofKeywords = array( + 'shared' => 'shared', + 'lazy' => 'lazy', + 'public' => 'public', + 'abstract' => 'abstract', + 'deprecated' => 'deprecated', + 'factory' => 'factory', + 'arguments' => 'arguments', + 'properties' => 'properties', + 'getters' => 'getters', + 'configurator' => 'configurator', + 'calls' => 'calls', + 'tags' => 'tags', + 'autowire' => 'autowire', + ); + private static $defaultsKeywords = array( 'public' => 'public', 'tags' => 'tags', @@ -125,7 +141,11 @@ class YamlFileLoader extends FileLoader // services $this->setCurrentDir(dirname($path)); - $this->parseDefinitions($content, $resource); + try { + $this->parseDefinitions($content, $resource); + } finally { + $this->instanceof = array(); + } } /** @@ -187,6 +207,22 @@ class YamlFileLoader extends FileLoader throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file)); } + if ($this->isUnderscoredParamValid($content, '_instanceof', $file)) { + $this->instanceof = array(); + $this->isLoadingInstanceof = true; + foreach ($content['services']['_instanceof'] as $id => $service) { + if (!$service || !is_array($service)) { + throw new InvalidArgumentException(sprintf('Type definition "%s" must be a non-empty array within "_instanceof" in %s. Check your YAML syntax.', $id, $file)); + } + if (is_string($service) && 0 === strpos($service, '@')) { + throw new InvalidArgumentException(sprintf('Type definition "%s" cannot be an alias within "_instanceof" in %s. Check your YAML syntax.', $id, $file)); + } + $this->parseDefinition($id, $service, $file, array()); + } + unset($content['services']['_instanceof']); + } + + $this->isLoadingInstanceof = false; $defaults = $this->parseDefaults($content, $file); foreach ($content['services'] as $id => $service) { $this->parseDefinition($id, $service, $file, $defaults); @@ -203,18 +239,11 @@ class YamlFileLoader extends FileLoader */ private function parseDefaults(array &$content, $file) { - if (!isset($content['services']['_defaults'])) { - return array(); - } - if (!is_array($defaults = $content['services']['_defaults'])) { - throw new InvalidArgumentException(sprintf('Service defaults must be an array, "%s" given in "%s".', gettype($defaults), $file)); - } - if (isset($defaults['alias']) || isset($defaults['class']) || isset($defaults['factory'])) { - // @deprecated code path, to be removed in 4.0 - + if (!$this->isUnderscoredParamValid($content, '_defaults', $file)) { return array(); } + $defaults = $content['services']['_defaults']; unset($content['services']['_defaults']); foreach ($defaults as $key => $default) { @@ -304,7 +333,7 @@ class YamlFileLoader extends FileLoader throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', gettype($service), $id, $file)); } - static::checkDefinition($id, $service, $file); + $this->checkDefinition($id, $service, $file); if (isset($service['alias'])) { $public = array_key_exists('public', $service) ? (bool) $service['public'] : (isset($defaults['public']) ? $defaults['public'] : true); @@ -319,7 +348,9 @@ class YamlFileLoader extends FileLoader return; } - if (isset($service['parent'])) { + if ($this->isLoadingInstanceof) { + $definition = new ChildDefinition(''); + } elseif (isset($service['parent'])) { $definition = new ChildDefinition($service['parent']); $inheritTag = isset($service['inherit_tags']) ? $service['inherit_tags'] : (isset($defaults['inherit_tags']) ? $defaults['inherit_tags'] : null); @@ -494,7 +525,7 @@ class YamlFileLoader extends FileLoader } $this->registerClasses($definition, $id, $service['resource']); } else { - $this->container->setDefinition($id, $definition); + $this->setDefinition($id, $definition); } } @@ -723,12 +754,14 @@ class YamlFileLoader extends FileLoader * @param array $definition The service definition to check * @param string $file The loaded YAML file */ - private static function checkDefinition($id, array $definition, $file) + private function checkDefinition($id, array $definition, $file) { - if ($throw = isset($definition['resource'])) { - $keywords = static::$prototypeKeywords; + if ($throw = $this->isLoadingInstanceof) { + $keywords = self::$instanceofKeywords; + } elseif ($throw = isset($definition['resource'])) { + $keywords = self::$prototypeKeywords; } else { - $keywords = static::$serviceKeywords; + $keywords = self::$serviceKeywords; } foreach ($definition as $key => $value) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index 1bdd2fc915..37da2af19f 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -56,6 +56,7 @@ + @@ -137,6 +138,26 @@ + + + + + + + + + + + + + + + + + + + + From 773eca7794facb9bbd3055db8da1055e37c2b103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sat, 4 Feb 2017 14:46:50 +0100 Subject: [PATCH 2/2] [DependencyInjection] Tests + refacto for "instanceof" definitions --- .../Loader/YamlFileLoader.php | 15 +++++++++++++ .../Tests/Compiler/PassConfigTest.php | 5 +++-- .../Fixtures/xml/services_instanceof.xml | 12 +++++++++++ .../Fixtures/yaml/services_instanceof.yml | 10 +++++++++ .../Tests/Loader/XmlFileLoaderTest.php | 21 +++++++++++++++++++ .../Tests/Loader/YamlFileLoaderTest.php | 21 +++++++++++++++++++ 6 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof.yml diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index f5e97f3cb9..6ad27499e7 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -283,6 +283,21 @@ class YamlFileLoader extends FileLoader return $defaults; } + private function isUnderscoredParamValid($content, $name, $file) + { + if (!isset($content['services'][$name])) { + return false; + } + + if (!is_array($underscoreParam = $content['services'][$name])) { + throw new InvalidArgumentException(sprintf('Service "%s" key must be an array, "%s" given in "%s".', $name, gettype($underscoreParam), $file)); + } + + // @deprecated condition, to be removed in 4.0 + + return !isset($underscoreParam['alias']) && !isset($underscoreParam['class']) && !isset($underscoreParam['factory']); + } + /** * @param array $service * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/PassConfigTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/PassConfigTest.php index 98ae2745e7..23f225f7d0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/PassConfigTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/PassConfigTest.php @@ -22,6 +22,7 @@ class PassConfigTest extends \PHPUnit_Framework_TestCase public function testPassOrdering() { $config = new PassConfig(); + $config->setBeforeOptimizationPasses(array()); $pass1 = $this->getMockBuilder(CompilerPassInterface::class)->getMock(); $config->addPass($pass1, PassConfig::TYPE_BEFORE_OPTIMIZATION, 10); @@ -30,7 +31,7 @@ class PassConfigTest extends \PHPUnit_Framework_TestCase $config->addPass($pass2, PassConfig::TYPE_BEFORE_OPTIMIZATION, 30); $passes = $config->getBeforeOptimizationPasses(); - $this->assertSame($pass2, $passes[1]); - $this->assertSame($pass1, $passes[2]); + $this->assertSame($pass2, $passes[0]); + $this->assertSame($pass1, $passes[1]); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof.xml new file mode 100644 index 0000000000..8ba3ab7c64 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_instanceof.xml @@ -0,0 +1,12 @@ + + + + + set* + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof.yml new file mode 100644 index 0000000000..f0612c6d0a --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_instanceof.yml @@ -0,0 +1,10 @@ +services: + _instanceof: + Symfony\Component\DependencyInjection\Tests\Loader\FooInterface: + autowire: true + lazy: true + tags: + - { name: foo } + - { name: bar } + + Symfony\Component\DependencyInjection\Tests\Loader\Foo: ~ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index f8f02c0eeb..01cc9d4ce7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -719,4 +719,25 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array(null, 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments()); $this->assertEquals(array(array('setApiKey', array('123'))), $container->getDefinition(NamedArgumentsDummy::class)->getMethodCalls()); } + + public function testInstanceof() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_instanceof.xml'); + $container->compile(); + + $definition = $container->getDefinition(Bar::class); + $this->assertSame(array('__construct', 'set*'), $definition->getAutowiredCalls()); + $this->assertTrue($definition->isLazy()); + $this->assertSame(array('foo' => array(array()), 'bar' => array(array())), $definition->getTags()); + } +} + +interface BarInterface +{ +} + +class Bar implements BarInterface +{ } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index ae56e9c223..da86c481b8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -469,6 +469,19 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array(array('setApiKey', array('123'))), $container->getDefinition('another_one')->getMethodCalls()); } + public function testInstanceof() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_instanceof.yml'); + $container->compile(); + + $definition = $container->getDefinition(Foo::class); + $this->assertTrue($definition->isAutowired()); + $this->assertTrue($definition->isLazy()); + $this->assertSame(array('foo' => array(array()), 'bar' => array(array())), $definition->getTags()); + } + /** * @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"). @@ -500,3 +513,11 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase $loader->load('services_underscore.yml'); } } + +interface FooInterface +{ +} + +class Foo implements FooInterface +{ +}