This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php

228 lines
8.9 KiB
PHP
Raw Normal View History

<?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)->addTag('tag')->setAutowired(true)->setChanges(array());
$def->setInstanceofConditionals(array(
parent::class => (new ChildDefinition(''))->setProperty('foo', 'bar')->addTag('baz', array('attr' => 123)),
));
(new ResolveInstanceofConditionalsPass())->process($container);
$parent = 'instanceof.'.parent::class.'.0.foo';
$def = $container->getDefinition('foo');
$this->assertEmpty($def->getInstanceofConditionals());
$this->assertInstanceof(ChildDefinition::class, $def);
$this->assertTrue($def->isAutowired());
$this->assertSame($parent, $def->getParent());
$this->assertSame(array('tag' => array(array()), 'baz' => array(array('attr' => 123))), $def->getTags());
$parent = $container->getDefinition($parent);
$this->assertSame(array('foo' => 'bar'), $parent->getProperties());
$this->assertSame(array(), $parent->getTags());
}
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);
$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());
}
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.
2017-04-27 16:48:07 +01:00
public function testProcessUsesAutoconfiguredInstanceof()
{
$container = new ContainerBuilder();
$def = $container->register('normal_service', self::class);
$def->setInstanceofConditionals(array(
parent::class => (new ChildDefinition(''))
->addTag('local_instanceof_tag')
->setFactory('locally_set_factory'),
));
$def->setAutoconfigured(true);
$container->registerForAutoconfiguration(parent::class)
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.
2017-04-27 16:48:07 +01:00
->addTag('autoconfigured_tag')
->setAutowired(true)
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.
2017-04-27 16:48:07 +01:00
->setFactory('autoconfigured_factory');
(new ResolveInstanceofConditionalsPass())->process($container);
(new ResolveDefinitionTemplatesPass())->process($container);
$def = $container->getDefinition('normal_service');
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.
2017-04-27 16:48:07 +01:00
// 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
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.
2017-04-27 16:48:07 +01:00
$this->assertSame(array('local_instanceof_tag' => array(array()), 'autoconfigured_tag' => array(array())), $def->getTags());
}
public function testAutoconfigureInstanceofDoesNotDuplicateTags()
{
$container = new ContainerBuilder();
$def = $container->register('normal_service', self::class);
$def
->addTag('duplicated_tag')
->addTag('duplicated_tag', array('and_attributes' => 1))
;
$def->setInstanceofConditionals(array(
parent::class => (new ChildDefinition(''))->addTag('duplicated_tag'),
));
$def->setAutoconfigured(true);
$container->registerForAutoconfiguration(parent::class)
->addTag('duplicated_tag', array('and_attributes' => 1))
;
(new ResolveInstanceofConditionalsPass())->process($container);
(new ResolveDefinitionTemplatesPass())->process($container);
$def = $container->getDefinition('normal_service');
$this->assertSame(array('duplicated_tag' => array(array(), array('and_attributes' => 1))), $def->getTags());
}
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.
2017-04-27 16:48:07 +01:00
public function testProcessDoesNotUseAutoconfiguredInstanceofIfNotEnabled()
{
$container = new ContainerBuilder();
$def = $container->register('normal_service', self::class);
$def->setInstanceofConditionals(array(
parent::class => (new ChildDefinition(''))
->addTag('foo_tag'),
));
$container->registerForAutoconfiguration(parent::class)
->setAutowired(true);
(new ResolveInstanceofConditionalsPass())->process($container);
(new ResolveDefinitionTemplatesPass())->process($container);
$def = $container->getDefinition('normal_service');
$this->assertFalse($def->isAutowired());
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @expectedExceptionMessage "App\FakeInterface" is set as an "instanceof" conditional, but it does not exist.
*/
public function testBadInterfaceThrowsException()
{
$container = new ContainerBuilder();
$def = $container->register('normal_service', self::class);
$def->setInstanceofConditionals(array(
'App\\FakeInterface' => (new ChildDefinition(''))
->addTag('foo_tag'),
));
(new ResolveInstanceofConditionalsPass())->process($container);
}
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.
2017-04-27 16:48:07 +01:00
public function testBadInterfaceForAutomaticInstanceofIsOk()
{
$container = new ContainerBuilder();
$container->register('normal_service', self::class)
->setAutoconfigured(true);
$container->registerForAutoconfiguration('App\\FakeInterface')
->setAutowired(true);
(new ResolveInstanceofConditionalsPass())->process($container);
$this->assertTrue($container->hasDefinition('normal_service'));
}
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.
2017-04-27 16:48:07 +01:00
/**
* @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);
}
}