Skip to content

Commit f93e252

Browse files
bug #31541 [DI] fix using bindings with locators of service subscribers (nicolas-grekas)
This PR was merged into the 3.4 branch. Discussion ---------- [DI] fix using bindings with locators of service subscribers | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - Spotted during the workshop in Sao Paulo, with @tsantos84, @andreia and others :) Commits ------- 7146b95 [DI] fix using bindings with locators of service subscribers
2 parents b60872b + 7146b95 commit f93e252

File tree

3 files changed

+167
-1
lines changed

3 files changed

+167
-1
lines changed

src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php

+11-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ protected function processValue($value, $isRoot = false)
4848
if (!$v instanceof Reference) {
4949
throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set, "%s" found for key "%s".', $this->currentId, \is_object($v) ? \get_class($v) : \gettype($v), $k));
5050
}
51+
52+
if (\is_int($k)) {
53+
unset($arguments[0][$k]);
54+
55+
$k = (string) $v;
56+
}
5157
$arguments[0][$k] = new ServiceClosureArgument($v);
5258
}
5359
ksort($arguments[0]);
@@ -91,7 +97,11 @@ public static function register(ContainerBuilder $container, array $refMap, $cal
9197
->setPublic(false)
9298
->addTag('container.service_locator');
9399

94-
if (!$container->has($id = 'service_locator.'.ContainerBuilder::hash($locator))) {
100+
if (null !== $callerId && $container->hasDefinition($callerId)) {
101+
$locator->setBindings($container->getDefinition($callerId)->getBindings());
102+
}
103+
104+
if (!$container->hasDefinition($id = 'service_locator.'.ContainerBuilder::hash($locator))) {
95105
$container->setDefinition($id, $locator);
96106
}
97107

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
16+
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
17+
use Symfony\Component\DependencyInjection\ContainerBuilder;
18+
use Symfony\Component\DependencyInjection\Reference;
19+
use Symfony\Component\DependencyInjection\ServiceLocator;
20+
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
21+
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1;
22+
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition2;
23+
24+
require_once __DIR__.'/../Fixtures/includes/classes.php';
25+
26+
class ServiceLocatorTagPassTest extends TestCase
27+
{
28+
/**
29+
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
30+
* @expectedExceptionMessage Invalid definition for service "foo": an array of references is expected as first argument when the "container.service_locator" tag is set.
31+
*/
32+
public function testNoServices()
33+
{
34+
$container = new ContainerBuilder();
35+
36+
$container->register('foo', ServiceLocator::class)
37+
->addTag('container.service_locator')
38+
;
39+
40+
(new ServiceLocatorTagPass())->process($container);
41+
}
42+
43+
/**
44+
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
45+
* @expectedExceptionMessage Invalid definition for service "foo": an array of references is expected as first argument when the "container.service_locator" tag is set, "string" found for key "0".
46+
*/
47+
public function testInvalidServices()
48+
{
49+
$container = new ContainerBuilder();
50+
51+
$container->register('foo', ServiceLocator::class)
52+
->setArguments([[
53+
'dummy',
54+
]])
55+
->addTag('container.service_locator')
56+
;
57+
58+
(new ServiceLocatorTagPass())->process($container);
59+
}
60+
61+
public function testProcessValue()
62+
{
63+
$container = new ContainerBuilder();
64+
65+
$container->register('bar', CustomDefinition::class);
66+
$container->register('baz', CustomDefinition::class);
67+
68+
$container->register('foo', ServiceLocator::class)
69+
->setArguments([[
70+
new Reference('bar'),
71+
new Reference('baz'),
72+
'some.service' => new Reference('bar'),
73+
]])
74+
->addTag('container.service_locator')
75+
;
76+
77+
(new ServiceLocatorTagPass())->process($container);
78+
79+
/** @var ServiceLocator $locator */
80+
$locator = $container->get('foo');
81+
82+
$this->assertSame(CustomDefinition::class, \get_class($locator('bar')));
83+
$this->assertSame(CustomDefinition::class, \get_class($locator('baz')));
84+
$this->assertSame(CustomDefinition::class, \get_class($locator('some.service')));
85+
}
86+
87+
public function testServiceWithKeyOverwritesPreviousInheritedKey()
88+
{
89+
$container = new ContainerBuilder();
90+
91+
$container->register('bar', TestDefinition1::class);
92+
$container->register('baz', TestDefinition2::class);
93+
94+
$container->register('foo', ServiceLocator::class)
95+
->setArguments([[
96+
new Reference('bar'),
97+
'bar' => new Reference('baz'),
98+
]])
99+
->addTag('container.service_locator')
100+
;
101+
102+
(new ServiceLocatorTagPass())->process($container);
103+
104+
/** @var ServiceLocator $locator */
105+
$locator = $container->get('foo');
106+
107+
$this->assertSame(TestDefinition2::class, \get_class($locator('bar')));
108+
}
109+
110+
public function testInheritedKeyOverwritesPreviousServiceWithKey()
111+
{
112+
$container = new ContainerBuilder();
113+
114+
$container->register('bar', TestDefinition1::class);
115+
$container->register('baz', TestDefinition2::class);
116+
117+
$container->register('foo', ServiceLocator::class)
118+
->setArguments([[
119+
'bar' => new Reference('baz'),
120+
new Reference('bar'),
121+
]])
122+
->addTag('container.service_locator')
123+
;
124+
125+
(new ServiceLocatorTagPass())->process($container);
126+
127+
/** @var ServiceLocator $locator */
128+
$locator = $container->get('foo');
129+
130+
$this->assertSame(TestDefinition1::class, \get_class($locator('bar')));
131+
}
132+
133+
public function testBindingsAreCopied()
134+
{
135+
$container = new ContainerBuilder();
136+
137+
$container->register('foo')
138+
->setBindings(['foo' => 'foo']);
139+
140+
$locator = ServiceLocatorTagPass::register($container, ['foo' => new Reference('foo')], 'foo');
141+
$locator = $container->getDefinition($locator);
142+
$locator = $container->getDefinition($locator->getFactory()[0]);
143+
144+
$this->assertSame(['foo'], array_keys($locator->getBindings()));
145+
$this->assertInstanceOf(BoundArgument::class, $locator->getBindings()['foo']);
146+
}
147+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
4+
5+
use Symfony\Component\DependencyInjection\Definition;
6+
7+
class TestDefinition2 extends Definition
8+
{
9+
}

0 commit comments

Comments
 (0)