bug #29944 [DI] Overriding services autowired by name under _defaults bind not working (przemyslaw-bogusz, renanbr)
This PR was merged into the 3.4 branch. Discussion ---------- [DI] Overriding services autowired by name under _defaults bind not working | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #28326 | License | MIT This is an implementation of ideas and suggestions of @nicolas-grekas and @GuilhemN. Commits -------7e805eae2b
more tests35a40ace6f
[DI] Fixes: #28326 - Overriding services autowired by name under _defaults bind not working
This commit is contained in:
commit
3403a8e65a
|
@ -34,6 +34,8 @@ class ResolveBindingsPass extends AbstractRecursivePass
|
|||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$this->usedBindings = $container->getRemovedBindingIds();
|
||||
|
||||
try {
|
||||
parent::process($container);
|
||||
|
||||
|
|
|
@ -123,6 +123,8 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
|||
|
||||
private $removedIds = [];
|
||||
|
||||
private $removedBindingIds = [];
|
||||
|
||||
private static $internalTypes = [
|
||||
'int' => true,
|
||||
'float' => true,
|
||||
|
@ -1504,6 +1506,35 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
|||
return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || isset($this->removedIds[$id]) ? $id : parent::normalizeId($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets removed binding ids.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getRemovedBindingIds()
|
||||
{
|
||||
return $this->removedBindingIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a removed binding id.
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function addRemovedBindingIds($id)
|
||||
{
|
||||
if ($this->hasDefinition($id)) {
|
||||
foreach ($this->getDefinition($id)->getBindings() as $key => $binding) {
|
||||
list(, $bindingId) = $binding->getValues();
|
||||
$this->removedBindingIds[(int) $bindingId] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Service Conditionals.
|
||||
*
|
||||
|
|
|
@ -59,6 +59,8 @@ class ServiceConfigurator extends AbstractServiceConfigurator
|
|||
{
|
||||
parent::__destruct();
|
||||
|
||||
$this->container->addRemovedBindingIds($this->id);
|
||||
|
||||
if (!$this->definition instanceof ChildDefinition) {
|
||||
$this->container->setDefinition($this->id, $this->definition->setInstanceofConditionals($this->instanceof));
|
||||
} else {
|
||||
|
|
|
@ -91,6 +91,8 @@ abstract class FileLoader extends BaseFileLoader
|
|||
*/
|
||||
protected function setDefinition($id, Definition $definition)
|
||||
{
|
||||
$this->container->addRemovedBindingIds($id);
|
||||
|
||||
if ($this->isLoadingInstanceof) {
|
||||
if (!$definition instanceof ChildDefinition) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid type definition "%s": ChildDefinition expected, "%s" given.', $id, \get_class($definition)));
|
||||
|
|
|
@ -13,8 +13,11 @@ namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
|
|||
|
||||
class Bar implements BarInterface
|
||||
{
|
||||
public $quz;
|
||||
|
||||
public function __construct($quz = null, \NonExistent $nonExistent = null, BarInterface $decorated = null, array $foo = [])
|
||||
{
|
||||
$this->quz = $quz;
|
||||
}
|
||||
|
||||
public static function create(\NonExistent $nonExistent = null, $factory = null)
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<defaults>
|
||||
<bind key="$quz">value</bind>
|
||||
<bind key="$foo" type="collection">
|
||||
<bind>value</bind>
|
||||
</bind>
|
||||
</defaults>
|
||||
|
||||
<service id="bar" class="Symfony\Component\DependencyInjection\Tests\Fixtures\Bar"/>
|
||||
|
||||
<service id="foo" class="stdClass"/>
|
||||
</services>
|
||||
</container>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<defaults>
|
||||
<bind key="$quz">overridden</bind>
|
||||
</defaults>
|
||||
|
||||
<service id="bar" class="Symfony\Component\DependencyInjection\Tests\Fixtures\Bar"/>
|
||||
</services>
|
||||
</container>
|
|
@ -0,0 +1,11 @@
|
|||
services:
|
||||
_defaults:
|
||||
bind:
|
||||
$quz: value
|
||||
$foo: [value]
|
||||
|
||||
bar:
|
||||
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Bar
|
||||
|
||||
foo:
|
||||
class: stdClass
|
|
@ -0,0 +1,7 @@
|
|||
services:
|
||||
_defaults:
|
||||
bind:
|
||||
$quz: overridden
|
||||
|
||||
bar:
|
||||
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Bar
|
|
@ -17,6 +17,7 @@ use Symfony\Component\Config\Loader\LoaderResolver;
|
|||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Config\Resource\GlobResource;
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
|
||||
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
|
||||
|
@ -820,4 +821,20 @@ class XmlFileLoaderTest extends TestCase
|
|||
$dump = $dumper->dump();
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_tsantos.php', $dumper->dump());
|
||||
}
|
||||
|
||||
/**
|
||||
* The pass may throw an exception, which will cause the test to fail.
|
||||
*/
|
||||
public function testOverriddenDefaultsBindings()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
|
||||
$loader->load('defaults_bindings.xml');
|
||||
$loader->load('defaults_bindings2.xml');
|
||||
|
||||
(new ResolveBindingsPass())->process($container);
|
||||
|
||||
$this->assertSame('overridden', $container->get('bar')->quz);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ use Symfony\Component\Config\Loader\LoaderResolver;
|
|||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Config\Resource\GlobResource;
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
|
@ -732,4 +733,20 @@ class YamlFileLoaderTest extends TestCase
|
|||
'$factory' => 'factory',
|
||||
], array_map(function ($v) { return $v->getValues()[0]; }, $definition->getBindings()));
|
||||
}
|
||||
|
||||
/**
|
||||
* The pass may throw an exception, which will cause the test to fail.
|
||||
*/
|
||||
public function testOverriddenDefaultsBindings()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
|
||||
$loader->load('defaults_bindings.yml');
|
||||
$loader->load('defaults_bindings2.yml');
|
||||
|
||||
(new ResolveBindingsPass())->process($container);
|
||||
|
||||
$this->assertSame('overridden', $container->get('bar')->quz);
|
||||
}
|
||||
}
|
||||
|
|
Reference in New Issue