[DI] Rework config hierarchy: defaults > instanceof > service config
This commit is contained in:
parent
cbaee55223
commit
ab86457b12
@ -42,7 +42,8 @@ class PassConfig
|
||||
$this->beforeOptimizationPasses = array(
|
||||
100 => array(
|
||||
$resolveClassPass = new ResolveClassPass(),
|
||||
new ResolveDefinitionInheritancePass(),
|
||||
new ResolveInstanceofConditionalsPass(),
|
||||
new ResolveTagsInheritancePass(),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -1,99 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 <p@tchwork.com>
|
||||
*/
|
||||
class ResolveDefinitionInheritancePass extends AbstractRecursivePass
|
||||
{
|
||||
protected function processValue($value, $isRoot = false)
|
||||
{
|
||||
if (!$value instanceof Definition) {
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
|
||||
$class = $value instanceof ChildDefinition ? $this->resolveDefinition($value) : $value->getClass();
|
||||
|
||||
if (!$class || false !== strpos($class, '%') || !$instanceof = $value->getInstanceofConditionals()) {
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
$value->setInstanceofConditionals(array());
|
||||
|
||||
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);
|
||||
$class = $parentDef instanceof ChildDefinition ? $this->resolveDefinition($parentDef) : $parentDef->getClass();
|
||||
$class = $definition->getClass() ?: $class;
|
||||
|
||||
// append parent tags when inheriting is enabled
|
||||
if ($definition->getInheritTags()) {
|
||||
$definition->setInheritTags(false);
|
||||
|
||||
foreach ($parentDef->getTags() as $k => $v) {
|
||||
foreach ($v as $v) {
|
||||
$definition->addTag($k, $v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
ResolveDefinitionTemplatesPass::mergeDefinition($def, $definition);
|
||||
|
||||
// prepend instanceof tags
|
||||
$tailTags = $def->getTags();
|
||||
if ($headTags = $definition->getTags()) {
|
||||
$def->setTags($headTags);
|
||||
foreach ($tailTags as $k => $v) {
|
||||
foreach ($v as $v) {
|
||||
$def->addTag($k, $v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -84,7 +84,7 @@ class ResolveDefinitionTemplatesPass extends AbstractRecursivePass
|
||||
$def = new Definition();
|
||||
|
||||
// merge in parent definition
|
||||
// purposely ignored attributes: abstract, tags
|
||||
// purposely ignored attributes: abstract, shared, tags
|
||||
$def->setClass($parentDef->getClass());
|
||||
$def->setArguments($parentDef->getArguments());
|
||||
$def->setMethodCalls($parentDef->getMethodCalls());
|
||||
@ -101,27 +101,8 @@ class ResolveDefinitionTemplatesPass extends AbstractRecursivePass
|
||||
$def->setPublic($parentDef->isPublic());
|
||||
$def->setLazy($parentDef->isLazy());
|
||||
$def->setAutowired($parentDef->isAutowired());
|
||||
$def->setChanges($parentDef->getChanges());
|
||||
|
||||
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'])) {
|
||||
@ -148,6 +129,9 @@ class ResolveDefinitionTemplatesPass extends AbstractRecursivePass
|
||||
if (isset($changes['autowired'])) {
|
||||
$def->setAutowired($definition->isAutowired());
|
||||
}
|
||||
if (isset($changes['shared'])) {
|
||||
$def->setShared($definition->isShared());
|
||||
}
|
||||
if (isset($changes['decorated_service'])) {
|
||||
$decoratedService = $definition->getDecoratedService();
|
||||
if (null === $decoratedService) {
|
||||
@ -182,5 +166,16 @@ class ResolveDefinitionTemplatesPass extends AbstractRecursivePass
|
||||
if ($calls = $definition->getMethodCalls()) {
|
||||
$def->setMethodCalls(array_merge($def->getMethodCalls(), $calls));
|
||||
}
|
||||
|
||||
// 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->setTags($definition->getTags());
|
||||
|
||||
return $def;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
|
||||
/**
|
||||
* Applies instanceof conditionals to definitions.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ResolveInstanceofConditionalsPass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$didProcess = false;
|
||||
foreach ($container->getDefinitions() as $id => $definition) {
|
||||
if ($definition instanceof ChildDefinition) {
|
||||
continue;
|
||||
}
|
||||
if ($definition !== $processedDefinition = $this->processDefinition($container, $id, $definition)) {
|
||||
$didProcess = true;
|
||||
$container->setDefinition($id, $processedDefinition);
|
||||
}
|
||||
}
|
||||
if ($didProcess) {
|
||||
$container->register('abstract.'.__CLASS__, '')->setAbstract(true);
|
||||
}
|
||||
}
|
||||
|
||||
private function processDefinition(ContainerBuilder $container, $id, Definition $definition)
|
||||
{
|
||||
if (!$instanceofConditionals = $definition->getInstanceofConditionals()) {
|
||||
return $definition;
|
||||
}
|
||||
if (!$class = $container->getParameterBag()->resolveValue($definition->getClass())) {
|
||||
return $definition;
|
||||
}
|
||||
|
||||
$definition->setInstanceofConditionals(array());
|
||||
$instanceofParent = null;
|
||||
$parent = 'abstract.'.__CLASS__;
|
||||
$shared = null;
|
||||
|
||||
foreach ($instanceofConditionals as $interface => $instanceofDef) {
|
||||
if ($interface !== $class && (!$container->getReflectionClass($interface) || !$container->getReflectionClass($class))) {
|
||||
continue;
|
||||
}
|
||||
if ($interface === $class || is_subclass_of($class, $interface)) {
|
||||
$instanceofParent = clone $instanceofDef;
|
||||
$instanceofParent->setAbstract(true)->setInheritTags(true)->setParent($parent);
|
||||
$parent = 'instanceof.'.$interface.'.'.$id;
|
||||
$container->setDefinition($parent, $instanceofParent);
|
||||
|
||||
if (isset($instanceofParent->getChanges()['shared'])) {
|
||||
$shared = $instanceofParent->isShared();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($instanceofParent) {
|
||||
// cast Definition to ChildDefinition
|
||||
$definition = serialize($definition);
|
||||
$definition = substr_replace($definition, '53', 2, 2);
|
||||
$definition = substr_replace($definition, 'Child', 44, 0);
|
||||
$definition = unserialize($definition);
|
||||
$definition->setInheritTags(true)->setParent($parent);
|
||||
|
||||
if (null !== $shared && !isset($definition->getChanges()['shared'])) {
|
||||
$definition->setShared($shared);
|
||||
}
|
||||
}
|
||||
|
||||
return $definition;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Applies tags inheritance to definitions.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ResolveTagsInheritancePass extends AbstractRecursivePass
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function processValue($value, $isRoot = false)
|
||||
{
|
||||
if (!$value instanceof ChildDefinition || !$value->getInheritTags()) {
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
$value->setInheritTags(false);
|
||||
|
||||
if (!$this->container->has($parent = $value->getParent())) {
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
|
||||
$parentDef = $this->container->findDefinition($parent);
|
||||
|
||||
if ($parentDef instanceof ChildDefinition) {
|
||||
$this->processValue($parentDef);
|
||||
}
|
||||
|
||||
foreach ($parentDef->getTags() as $k => $v) {
|
||||
foreach ($v as $v) {
|
||||
$value->addTag($k, $v);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionInheritancePass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class ResolveDefinitionInheritancePassTest extends TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$def = $container->register('parent', self::class)->setArguments(array('moo', 'b'))->setProperty('foo', 'moo');
|
||||
$def->setInstanceofConditionals(array(
|
||||
parent::class => (new ChildDefinition(''))
|
||||
->replaceArgument(0, 'a')
|
||||
->setProperty('foo', 'bar')
|
||||
->setClass('bar'),
|
||||
));
|
||||
|
||||
$this->process($container);
|
||||
|
||||
$this->assertEmpty($def->getInstanceofConditionals());
|
||||
$this->assertSame($def, $container->getDefinition('parent'));
|
||||
$this->assertEquals('bar', $def->getClass());
|
||||
$this->assertEquals(array('a', 'b'), $def->getArguments());
|
||||
$this->assertEquals(array('foo' => 'bar'), $def->getProperties());
|
||||
}
|
||||
|
||||
public function testProcessAppendsMethodCallsAlways()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$def = $container
|
||||
->register('parent', self::class)
|
||||
->addMethodCall('foo', array('bar'));
|
||||
|
||||
$def->setInstanceofConditionals(array(
|
||||
parent::class => (new ChildDefinition(''))
|
||||
->addMethodCall('bar', array('foo')),
|
||||
));
|
||||
|
||||
$this->process($container);
|
||||
|
||||
$this->assertEquals(array(
|
||||
array('foo', array('bar')),
|
||||
array('bar', array('foo')),
|
||||
), $container->getDefinition('parent')->getMethodCalls());
|
||||
}
|
||||
|
||||
public function testProcessDoesReplaceShared()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$def = $container->register('parent', 'stdClass');
|
||||
|
||||
$def->setInstanceofConditionals(array(
|
||||
'stdClass' => (new ChildDefinition(''))->setShared(false),
|
||||
));
|
||||
|
||||
$this->process($container);
|
||||
|
||||
$this->assertFalse($def->isShared());
|
||||
}
|
||||
|
||||
public function testProcessHandlesMultipleInheritance()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$def = $container
|
||||
->register('parent', self::class)
|
||||
->setArguments(array('foo', 'bar', 'c'))
|
||||
;
|
||||
|
||||
$def->setInstanceofConditionals(array(
|
||||
parent::class => (new ChildDefinition(''))->replaceArgument(1, 'b'),
|
||||
self::class => (new ChildDefinition(''))->replaceArgument(0, 'a'),
|
||||
));
|
||||
|
||||
$this->process($container);
|
||||
|
||||
$this->assertEquals(array('a', 'b', 'c'), $def->getArguments());
|
||||
}
|
||||
|
||||
public function testSetLazyOnServiceHasParent()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$def = $container->register('parent', 'stdClass');
|
||||
|
||||
$def->setInstanceofConditionals(array(
|
||||
'stdClass' => (new ChildDefinition(''))->setLazy(true),
|
||||
));
|
||||
|
||||
$this->process($container);
|
||||
|
||||
$this->assertTrue($container->getDefinition('parent')->isLazy());
|
||||
}
|
||||
|
||||
public function testProcessInheritTags()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('parent', self::class)->addTag('parent');
|
||||
|
||||
$def = $container->setDefinition('child', new ChildDefinition('parent'))
|
||||
->addTag('child')
|
||||
->setInheritTags(true)
|
||||
;
|
||||
|
||||
$def->setInstanceofConditionals(array(
|
||||
parent::class => (new ChildDefinition(''))->addTag('foo'),
|
||||
));
|
||||
|
||||
$this->process($container);
|
||||
|
||||
$t = array(array());
|
||||
$this->assertSame(array('foo' => $t, 'child' => $t, 'parent' => $t), $def->getTags());
|
||||
}
|
||||
|
||||
public function testProcessResolvesAliasesAndTags()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('parent', self::class);
|
||||
$container->setAlias('parent_alias', 'parent');
|
||||
$def = $container->setDefinition('child', new ChildDefinition('parent_alias'));
|
||||
$def->setInstanceofConditionals(array(
|
||||
parent::class => (new ChildDefinition(''))->addTag('foo'),
|
||||
));
|
||||
|
||||
$this->process($container);
|
||||
|
||||
$this->assertSame(array('foo' => array(array())), $def->getTags());
|
||||
$this->assertSame($def, $container->getDefinition('child'));
|
||||
$this->assertEmpty($def->getClass());
|
||||
}
|
||||
|
||||
protected function process(ContainerBuilder $container)
|
||||
{
|
||||
$pass = new ResolveDefinitionInheritancePass();
|
||||
$pass->process($container);
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class ResolveInstanceofConditionalsPassTest extends TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$def = $container->register('foo', self::class);
|
||||
$def->setInstanceofConditionals(array(
|
||||
parent::class => (new ChildDefinition(''))->setProperty('foo', 'bar'),
|
||||
));
|
||||
|
||||
(new ResolveInstanceofConditionalsPass())->process($container);
|
||||
|
||||
$parent = 'instanceof.'.parent::class.'.foo';
|
||||
$def = $container->getDefinition('foo');
|
||||
$this->assertEmpty($def->getInstanceofConditionals());
|
||||
$this->assertInstanceof(ChildDefinition::class, $def);
|
||||
$this->assertTrue($def->getInheritTags());
|
||||
$this->assertSame($parent, $def->getParent());
|
||||
$this->assertEquals(array('foo' => 'bar'), $container->getDefinition($parent)->getProperties());
|
||||
}
|
||||
|
||||
public function testProcessInheritance()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$def = $container
|
||||
->register('parent', parent::class)
|
||||
->addMethodCall('foo', array('foo'));
|
||||
$def->setInstanceofConditionals(array(
|
||||
parent::class => (new ChildDefinition(''))->addMethodCall('foo', array('bar')),
|
||||
));
|
||||
|
||||
$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);
|
||||
(new ResolveDefinitionTemplatesPass())->process($container);
|
||||
|
||||
$expected = array(
|
||||
array('foo', array('bar')),
|
||||
array('foo', array('foo')),
|
||||
);
|
||||
|
||||
$this->assertSame($expected, $container->getDefinition('parent')->getMethodCalls());
|
||||
$this->assertSame($expected, $container->getDefinition('child')->getMethodCalls());
|
||||
}
|
||||
|
||||
public function testProcessDoesReplaceShared()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$def = $container->register('foo', 'stdClass');
|
||||
$def->setInstanceofConditionals(array(
|
||||
'stdClass' => (new ChildDefinition(''))->setShared(false),
|
||||
));
|
||||
|
||||
(new ResolveInstanceofConditionalsPass())->process($container);
|
||||
|
||||
$def = $container->getDefinition('foo');
|
||||
$this->assertFalse($def->isShared());
|
||||
}
|
||||
|
||||
public function testProcessHandlesMultipleInheritance()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$def = $container->register('foo', self::class)->setShared(true);
|
||||
|
||||
$def->setInstanceofConditionals(array(
|
||||
parent::class => (new ChildDefinition(''))->setLazy(true)->setShared(false),
|
||||
self::class => (new ChildDefinition(''))->setAutowired(true),
|
||||
));
|
||||
|
||||
(new ResolveInstanceofConditionalsPass())->process($container);
|
||||
(new ResolveDefinitionTemplatesPass())->process($container);
|
||||
|
||||
$def = $container->getDefinition('foo');
|
||||
$this->assertTrue($def->isAutowired());
|
||||
$this->assertTrue($def->isLazy());
|
||||
$this->assertTrue($def->isShared());
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveTagsInheritancePass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class ResolveTagsInheritancePassTest extends TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('grandpa', self::class)->addTag('g');
|
||||
$container->setDefinition('parent', new ChildDefinition('grandpa'))->addTag('p')->setInheritTags(true);
|
||||
$container->setDefinition('child', new ChildDefinition('parent'))->setInheritTags(true);
|
||||
|
||||
(new ResolveTagsInheritancePass())->process($container);
|
||||
|
||||
$expected = array('p' => array(array()), 'g' => array(array()));
|
||||
$this->assertSame($expected, $container->getDefinition('parent')->getTags());
|
||||
$this->assertSame($expected, $container->getDefinition('child')->getTags());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user