feature #27806 [DI] Allow autoconfiguring bindings (nicolas-grekas)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[DI] Allow autoconfiguring bindings

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

I've come up with a case where we will need to inject a different service based on which interfaces a consumer service implements: injecting a different token storage for monolog processor than for everything else. Required in #27801.

Commits
-------

7c29977037 [DI] Allow autoconfiguring bindings
This commit is contained in:
Nicolas Grekas 2018-08-08 10:30:15 +02:00
commit 13dc341d7f
6 changed files with 38 additions and 4 deletions

View File

@ -22,10 +22,14 @@ final class BoundArgument implements ArgumentInterface
private $identifier;
private $used;
public function __construct($value)
public function __construct($value, bool $trackUsage = true)
{
$this->value = $value;
$this->identifier = ++self::$sequence;
if ($trackUsage) {
$this->identifier = ++self::$sequence;
} else {
$this->used = true;
}
}
/**

View File

@ -62,6 +62,7 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface
$parent = $shared = null;
$instanceofTags = array();
$instanceofCalls = array();
$instanceofBindings = array();
foreach ($conditionals as $interface => $instanceofDefs) {
if ($interface !== $class && (!$container->getReflectionClass($class, false))) {
@ -79,6 +80,7 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface
$parent = '.instanceof.'.$interface.'.'.$key.'.'.$id;
$container->setDefinition($parent, $instanceofDef);
$instanceofTags[] = $instanceofDef->getTags();
$instanceofBindings = $instanceofDef->getBindings() + $instanceofBindings;
foreach ($instanceofDef->getMethodCalls() as $methodCall) {
$instanceofCalls[] = $methodCall;
@ -86,6 +88,7 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface
$instanceofDef->setTags(array());
$instanceofDef->setMethodCalls(array());
$instanceofDef->setBindings(array());
if (isset($instanceofDef->getChanges()['shared'])) {
$shared = $instanceofDef->isShared();
@ -123,7 +126,7 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface
}
$definition->setMethodCalls(array_merge($instanceofCalls, $definition->getMethodCalls()));
$definition->setBindings($bindings);
$definition->setBindings($bindings + $instanceofBindings);
// reset fields with "merge" behavior
$abstract

View File

@ -26,6 +26,7 @@ class InstanceofConfigurator extends AbstractServiceConfigurator
use Traits\PublicTrait;
use Traits\ShareTrait;
use Traits\TagTrait;
use Traits\BindTrait;
/**
* Defines an instanceof-conditional to be applied to following service definitions.

View File

@ -93,6 +93,7 @@ class YamlFileLoader extends FileLoader
'calls' => 'calls',
'tags' => 'tags',
'autowire' => 'autowire',
'bind' => 'bind',
);
private static $defaultsKeywords = array(

View File

@ -141,6 +141,7 @@
<xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="bind" type="bind" minOccurs="0" maxOccurs="unbounded" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" use="required" />
<xsd:attribute name="shared" type="boolean" />

View File

@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass;
use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class ResolveInstanceofConditionalsPassTest extends TestCase
{
@ -270,7 +271,30 @@ class ResolveInstanceofConditionalsPassTest extends TestCase
$this->assertTrue($abstract->isAbstract());
}
public function testBindings()
public function testProcessForAutoconfiguredBindings()
{
$container = new ContainerBuilder();
$container->registerForAutoconfiguration(self::class)
->setBindings(array(
'$foo' => new BoundArgument(234, false),
parent::class => new BoundArgument(new Reference('foo'), false),
));
$container->register('foo', self::class)
->setAutoconfigured(true)
->setBindings(array('$foo' => new BoundArgument(123, false)));
(new ResolveInstanceofConditionalsPass())->process($container);
$expected = array(
'$foo' => new BoundArgument(123, false),
parent::class => new BoundArgument(new Reference('foo'), false),
);
$this->assertEquals($expected, $container->findDefinition('foo')->getBindings());
}
public function testBindingsOnInstanceofConditionals()
{
$container = new ContainerBuilder();
$def = $container->register('foo', self::class)->setBindings(array('$toto' => 123));