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:
commit
13dc341d7f
@ -22,10 +22,14 @@ final class BoundArgument implements ArgumentInterface
|
|||||||
private $identifier;
|
private $identifier;
|
||||||
private $used;
|
private $used;
|
||||||
|
|
||||||
public function __construct($value)
|
public function __construct($value, bool $trackUsage = true)
|
||||||
{
|
{
|
||||||
$this->value = $value;
|
$this->value = $value;
|
||||||
$this->identifier = ++self::$sequence;
|
if ($trackUsage) {
|
||||||
|
$this->identifier = ++self::$sequence;
|
||||||
|
} else {
|
||||||
|
$this->used = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,6 +62,7 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface
|
|||||||
$parent = $shared = null;
|
$parent = $shared = null;
|
||||||
$instanceofTags = array();
|
$instanceofTags = array();
|
||||||
$instanceofCalls = array();
|
$instanceofCalls = array();
|
||||||
|
$instanceofBindings = array();
|
||||||
|
|
||||||
foreach ($conditionals as $interface => $instanceofDefs) {
|
foreach ($conditionals as $interface => $instanceofDefs) {
|
||||||
if ($interface !== $class && (!$container->getReflectionClass($class, false))) {
|
if ($interface !== $class && (!$container->getReflectionClass($class, false))) {
|
||||||
@ -79,6 +80,7 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface
|
|||||||
$parent = '.instanceof.'.$interface.'.'.$key.'.'.$id;
|
$parent = '.instanceof.'.$interface.'.'.$key.'.'.$id;
|
||||||
$container->setDefinition($parent, $instanceofDef);
|
$container->setDefinition($parent, $instanceofDef);
|
||||||
$instanceofTags[] = $instanceofDef->getTags();
|
$instanceofTags[] = $instanceofDef->getTags();
|
||||||
|
$instanceofBindings = $instanceofDef->getBindings() + $instanceofBindings;
|
||||||
|
|
||||||
foreach ($instanceofDef->getMethodCalls() as $methodCall) {
|
foreach ($instanceofDef->getMethodCalls() as $methodCall) {
|
||||||
$instanceofCalls[] = $methodCall;
|
$instanceofCalls[] = $methodCall;
|
||||||
@ -86,6 +88,7 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface
|
|||||||
|
|
||||||
$instanceofDef->setTags(array());
|
$instanceofDef->setTags(array());
|
||||||
$instanceofDef->setMethodCalls(array());
|
$instanceofDef->setMethodCalls(array());
|
||||||
|
$instanceofDef->setBindings(array());
|
||||||
|
|
||||||
if (isset($instanceofDef->getChanges()['shared'])) {
|
if (isset($instanceofDef->getChanges()['shared'])) {
|
||||||
$shared = $instanceofDef->isShared();
|
$shared = $instanceofDef->isShared();
|
||||||
@ -123,7 +126,7 @@ class ResolveInstanceofConditionalsPass implements CompilerPassInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$definition->setMethodCalls(array_merge($instanceofCalls, $definition->getMethodCalls()));
|
$definition->setMethodCalls(array_merge($instanceofCalls, $definition->getMethodCalls()));
|
||||||
$definition->setBindings($bindings);
|
$definition->setBindings($bindings + $instanceofBindings);
|
||||||
|
|
||||||
// reset fields with "merge" behavior
|
// reset fields with "merge" behavior
|
||||||
$abstract
|
$abstract
|
||||||
|
@ -26,6 +26,7 @@ class InstanceofConfigurator extends AbstractServiceConfigurator
|
|||||||
use Traits\PublicTrait;
|
use Traits\PublicTrait;
|
||||||
use Traits\ShareTrait;
|
use Traits\ShareTrait;
|
||||||
use Traits\TagTrait;
|
use Traits\TagTrait;
|
||||||
|
use Traits\BindTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines an instanceof-conditional to be applied to following service definitions.
|
* Defines an instanceof-conditional to be applied to following service definitions.
|
||||||
|
@ -93,6 +93,7 @@ class YamlFileLoader extends FileLoader
|
|||||||
'calls' => 'calls',
|
'calls' => 'calls',
|
||||||
'tags' => 'tags',
|
'tags' => 'tags',
|
||||||
'autowire' => 'autowire',
|
'autowire' => 'autowire',
|
||||||
|
'bind' => 'bind',
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $defaultsKeywords = array(
|
private static $defaultsKeywords = array(
|
||||||
|
@ -141,6 +141,7 @@
|
|||||||
<xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
|
<xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
|
||||||
<xsd:element name="tag" type="tag" 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="property" type="property" minOccurs="0" maxOccurs="unbounded" />
|
||||||
|
<xsd:element name="bind" type="bind" minOccurs="0" maxOccurs="unbounded" />
|
||||||
</xsd:choice>
|
</xsd:choice>
|
||||||
<xsd:attribute name="id" type="xsd:string" use="required" />
|
<xsd:attribute name="id" type="xsd:string" use="required" />
|
||||||
<xsd:attribute name="shared" type="boolean" />
|
<xsd:attribute name="shared" type="boolean" />
|
||||||
|
@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\ChildDefinition;
|
|||||||
use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass;
|
use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass;
|
||||||
use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass;
|
use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
|
||||||
class ResolveInstanceofConditionalsPassTest extends TestCase
|
class ResolveInstanceofConditionalsPassTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -270,7 +271,30 @@ class ResolveInstanceofConditionalsPassTest extends TestCase
|
|||||||
$this->assertTrue($abstract->isAbstract());
|
$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();
|
$container = new ContainerBuilder();
|
||||||
$def = $container->register('foo', self::class)->setBindings(array('$toto' => 123));
|
$def = $container->register('foo', self::class)->setBindings(array('$toto' => 123));
|
||||||
|
Reference in New Issue
Block a user