4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\DependencyInjection\Tests\Compiler;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
16 use Symfony\Component\DependencyInjection\ContainerBuilder;
17 use Symfony\Component\DependencyInjection\Reference;
20 * @author Kévin Dunglas <dunglas@gmail.com>
22 class AutowirePassTest extends TestCase
24 public function testProcess()
26 $container = new ContainerBuilder();
28 $container->register('foo', __NAMESPACE__.'\Foo');
29 $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
30 $barDefinition->setAutowired(true);
32 $pass = new AutowirePass();
33 $pass->process($container);
35 $this->assertCount(1, $container->getDefinition('bar')->getArguments());
36 $this->assertEquals('foo', (string) $container->getDefinition('bar')->getArgument(0));
39 public function testProcessAutowireParent()
41 $container = new ContainerBuilder();
43 $container->register('b', __NAMESPACE__.'\B');
44 $cDefinition = $container->register('c', __NAMESPACE__.'\C');
45 $cDefinition->setAutowired(true);
47 $pass = new AutowirePass();
48 $pass->process($container);
50 $this->assertCount(1, $container->getDefinition('c')->getArguments());
51 $this->assertEquals('b', (string) $container->getDefinition('c')->getArgument(0));
54 public function testProcessAutowireInterface()
56 $container = new ContainerBuilder();
58 $container->register('f', __NAMESPACE__.'\F');
59 $gDefinition = $container->register('g', __NAMESPACE__.'\G');
60 $gDefinition->setAutowired(true);
62 $pass = new AutowirePass();
63 $pass->process($container);
65 $this->assertCount(3, $container->getDefinition('g')->getArguments());
66 $this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(0));
67 $this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(1));
68 $this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(2));
71 public function testCompleteExistingDefinition()
73 $container = new ContainerBuilder();
75 $container->register('b', __NAMESPACE__.'\B');
76 $container->register('f', __NAMESPACE__.'\F');
77 $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument(new Reference('b'));
78 $hDefinition->setAutowired(true);
80 $pass = new AutowirePass();
81 $pass->process($container);
83 $this->assertCount(2, $container->getDefinition('h')->getArguments());
84 $this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0));
85 $this->assertEquals('f', (string) $container->getDefinition('h')->getArgument(1));
88 public function testCompleteExistingDefinitionWithNotDefinedArguments()
90 $container = new ContainerBuilder();
92 $container->register('b', __NAMESPACE__.'\B');
93 $container->register('f', __NAMESPACE__.'\F');
94 $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument('')->addArgument('');
95 $hDefinition->setAutowired(true);
97 $pass = new AutowirePass();
98 $pass->process($container);
100 $this->assertCount(2, $container->getDefinition('h')->getArguments());
101 $this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0));
102 $this->assertEquals('f', (string) $container->getDefinition('h')->getArgument(1));
106 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
107 * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". Multiple services exist for this interface (c1, c2, c3).
109 public function testTypeCollision()
111 $container = new ContainerBuilder();
113 $container->register('c1', __NAMESPACE__.'\CollisionA');
114 $container->register('c2', __NAMESPACE__.'\CollisionB');
115 $container->register('c3', __NAMESPACE__.'\CollisionB');
116 $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
117 $aDefinition->setAutowired(true);
119 $pass = new AutowirePass();
120 $pass->process($container);
124 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
125 * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a". Multiple services exist for this class (a1, a2).
127 public function testTypeNotGuessable()
129 $container = new ContainerBuilder();
131 $container->register('a1', __NAMESPACE__.'\Foo');
132 $container->register('a2', __NAMESPACE__.'\Foo');
133 $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument');
134 $aDefinition->setAutowired(true);
136 $pass = new AutowirePass();
137 $pass->process($container);
141 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
142 * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a". Multiple services exist for this class (a1, a2).
144 public function testTypeNotGuessableWithSubclass()
146 $container = new ContainerBuilder();
148 $container->register('a1', __NAMESPACE__.'\B');
149 $container->register('a2', __NAMESPACE__.'\B');
150 $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgumentForSubclass');
151 $aDefinition->setAutowired(true);
153 $pass = new AutowirePass();
154 $pass->process($container);
158 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
159 * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". No services were found matching this interface and it cannot be auto-registered.
161 public function testTypeNotGuessableNoServicesFound()
163 $container = new ContainerBuilder();
165 $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
166 $aDefinition->setAutowired(true);
168 $pass = new AutowirePass();
169 $pass->process($container);
172 public function testTypeNotGuessableWithTypeSet()
174 $container = new ContainerBuilder();
176 $container->register('a1', __NAMESPACE__.'\Foo');
177 $container->register('a2', __NAMESPACE__.'\Foo');
178 $container->register('a3', __NAMESPACE__.'\Foo')->addAutowiringType(__NAMESPACE__.'\Foo');
179 $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument');
180 $aDefinition->setAutowired(true);
182 $pass = new AutowirePass();
183 $pass->process($container);
185 $this->assertCount(1, $container->getDefinition('a')->getArguments());
186 $this->assertEquals('a3', (string) $container->getDefinition('a')->getArgument(0));
189 public function testWithTypeSet()
191 $container = new ContainerBuilder();
193 $container->register('c1', __NAMESPACE__.'\CollisionA');
194 $container->register('c2', __NAMESPACE__.'\CollisionB')->addAutowiringType(__NAMESPACE__.'\CollisionInterface');
195 $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
196 $aDefinition->setAutowired(true);
198 $pass = new AutowirePass();
199 $pass->process($container);
201 $this->assertCount(1, $container->getDefinition('a')->getArguments());
202 $this->assertEquals('c2', (string) $container->getDefinition('a')->getArgument(0));
205 public function testCreateDefinition()
207 $container = new ContainerBuilder();
209 $coopTilleulsDefinition = $container->register('coop_tilleuls', __NAMESPACE__.'\LesTilleuls');
210 $coopTilleulsDefinition->setAutowired(true);
212 $pass = new AutowirePass();
213 $pass->process($container);
215 $this->assertCount(2, $container->getDefinition('coop_tilleuls')->getArguments());
216 $this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\dunglas', $container->getDefinition('coop_tilleuls')->getArgument(0));
217 $this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\dunglas', $container->getDefinition('coop_tilleuls')->getArgument(1));
219 $dunglasDefinition = $container->getDefinition('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas');
220 $this->assertEquals(__NAMESPACE__.'\Dunglas', $dunglasDefinition->getClass());
221 $this->assertFalse($dunglasDefinition->isPublic());
222 $this->assertCount(1, $dunglasDefinition->getArguments());
223 $this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\lille', $dunglasDefinition->getArgument(0));
225 $lilleDefinition = $container->getDefinition('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Lille');
226 $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass());
229 public function testResolveParameter()
231 $container = new ContainerBuilder();
233 $container->setParameter('class_name', __NAMESPACE__.'\Foo');
234 $container->register('foo', '%class_name%');
235 $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
236 $barDefinition->setAutowired(true);
238 $pass = new AutowirePass();
239 $pass->process($container);
241 $this->assertEquals('foo', $container->getDefinition('bar')->getArgument(0));
244 public function testOptionalParameter()
246 $container = new ContainerBuilder();
248 $container->register('a', __NAMESPACE__.'\A');
249 $container->register('foo', __NAMESPACE__.'\Foo');
250 $optDefinition = $container->register('opt', __NAMESPACE__.'\OptionalParameter');
251 $optDefinition->setAutowired(true);
253 $pass = new AutowirePass();
254 $pass->process($container);
256 $definition = $container->getDefinition('opt');
257 $this->assertNull($definition->getArgument(0));
258 $this->assertEquals('a', $definition->getArgument(1));
259 $this->assertEquals('foo', $definition->getArgument(2));
262 public function testDontTriggerAutowiring()
264 $container = new ContainerBuilder();
266 $container->register('foo', __NAMESPACE__.'\Foo');
267 $container->register('bar', __NAMESPACE__.'\Bar');
269 $pass = new AutowirePass();
270 $pass->process($container);
272 $this->assertCount(0, $container->getDefinition('bar')->getArguments());
276 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
277 * @expectedExceptionMessage Cannot autowire argument 2 for Symfony\Component\DependencyInjection\Tests\Compiler\BadTypeHintedArgument because the type-hinted class does not exist (Class Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass does not exist).
279 public function testClassNotFoundThrowsException()
281 $container = new ContainerBuilder();
283 $aDefinition = $container->register('a', __NAMESPACE__.'\BadTypeHintedArgument');
284 $aDefinition->setAutowired(true);
286 $pass = new AutowirePass();
287 $pass->process($container);
291 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
292 * @expectedExceptionMessage Cannot autowire argument 2 for Symfony\Component\DependencyInjection\Tests\Compiler\BadParentTypeHintedArgument because the type-hinted class does not exist (Class Symfony\Component\DependencyInjection\Tests\Compiler\OptionalServiceClass does not exist).
294 public function testParentClassNotFoundThrowsException()
296 $container = new ContainerBuilder();
298 $aDefinition = $container->register('a', __NAMESPACE__.'\BadParentTypeHintedArgument');
299 $aDefinition->setAutowired(true);
301 $pass = new AutowirePass();
302 $pass->process($container);
305 public function testDontUseAbstractServices()
307 $container = new ContainerBuilder();
309 $container->register('abstract_foo', __NAMESPACE__.'\Foo')->setAbstract(true);
310 $container->register('foo', __NAMESPACE__.'\Foo');
311 $container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true);
313 $pass = new AutowirePass();
314 $pass->process($container);
316 $arguments = $container->getDefinition('bar')->getArguments();
317 $this->assertSame('foo', (string) $arguments[0]);
320 public function testSomeSpecificArgumentsAreSet()
322 $container = new ContainerBuilder();
324 $container->register('foo', __NAMESPACE__.'\Foo');
325 $container->register('a', __NAMESPACE__.'\A');
326 $container->register('dunglas', __NAMESPACE__.'\Dunglas');
327 $container->register('multiple', __NAMESPACE__.'\MultipleArguments')
329 // set the 2nd (index 1) argument only: autowire the first and third
330 // args are: A, Foo, Dunglas
331 ->setArguments(array(
332 1 => new Reference('foo'),
335 $pass = new AutowirePass();
336 $pass->process($container);
338 $definition = $container->getDefinition('multiple');
342 new Reference('foo'),
343 new Reference('dunglas'),
345 $definition->getArguments()
350 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
351 * @expectedExceptionMessage Unable to autowire argument index 1 ($foo) for the service "arg_no_type_hint". If this is an object, give it a type-hint. Otherwise, specify this argument's value explicitly.
353 public function testScalarArgsCannotBeAutowired()
355 $container = new ContainerBuilder();
357 $container->register('a', __NAMESPACE__.'\A');
358 $container->register('dunglas', __NAMESPACE__.'\Dunglas');
359 $container->register('arg_no_type_hint', __NAMESPACE__.'\MultipleArguments')
360 ->setAutowired(true);
362 $pass = new AutowirePass();
363 $pass->process($container);
365 $container->getDefinition('arg_no_type_hint');
369 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
370 * @expectedExceptionMessage Unable to autowire argument index 1 ($foo) for the service "not_really_optional_scalar". If this is an object, give it a type-hint. Otherwise, specify this argument's value explicitly.
372 public function testOptionalScalarNotReallyOptionalThrowException()
374 $container = new ContainerBuilder();
376 $container->register('a', __NAMESPACE__.'\A');
377 $container->register('lille', __NAMESPACE__.'\Lille');
378 $container->register('not_really_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalarNotReallyOptional')
379 ->setAutowired(true);
381 $pass = new AutowirePass();
382 $pass->process($container);
385 public function testOptionalScalarArgsDontMessUpOrder()
387 $container = new ContainerBuilder();
389 $container->register('a', __NAMESPACE__.'\A');
390 $container->register('lille', __NAMESPACE__.'\Lille');
391 $container->register('with_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalar')
392 ->setAutowired(true);
394 $pass = new AutowirePass();
395 $pass->process($container);
397 $definition = $container->getDefinition('with_optional_scalar');
401 // use the default value
403 new Reference('lille'),
405 $definition->getArguments()
409 public function testOptionalScalarArgsNotPassedIfLast()
411 $container = new ContainerBuilder();
413 $container->register('a', __NAMESPACE__.'\A');
414 $container->register('lille', __NAMESPACE__.'\Lille');
415 $container->register('with_optional_scalar_last', __NAMESPACE__.'\MultipleArgumentsOptionalScalarLast')
416 ->setAutowired(true);
418 $pass = new AutowirePass();
419 $pass->process($container);
421 $definition = $container->getDefinition('with_optional_scalar_last');
425 new Reference('lille'),
427 $definition->getArguments()
431 public function testIgnoreServiceWithClassNotExisting()
433 $container = new ContainerBuilder();
435 $container->register('class_not_exist', __NAMESPACE__.'\OptionalServiceClass');
437 $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
438 $barDefinition->setAutowired(true);
440 $pass = new AutowirePass();
441 $pass->process($container);
443 $this->assertTrue($container->hasDefinition('bar'));
446 public function testProcessDoesNotTriggerDeprecations()
448 $container = new ContainerBuilder();
449 $container->register('deprecated', 'Symfony\Component\DependencyInjection\Tests\Fixtures\DeprecatedClass')->setDeprecated(true);
450 $container->register('foo', __NAMESPACE__.'\Foo');
451 $container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true);
453 $pass = new AutowirePass();
454 $pass->process($container);
456 $this->assertTrue($container->hasDefinition('deprecated'));
457 $this->assertTrue($container->hasDefinition('foo'));
458 $this->assertTrue($container->hasDefinition('bar'));
461 public function testEmptyStringIsKept()
463 $container = new ContainerBuilder();
465 $container->register('a', __NAMESPACE__.'\A');
466 $container->register('lille', __NAMESPACE__.'\Lille');
467 $container->register('foo', __NAMESPACE__.'\MultipleArgumentsOptionalScalar')
469 ->setArguments(array('', ''));
471 $pass = new AutowirePass();
472 $pass->process($container);
474 $this->assertEquals(array(new Reference('a'), '', new Reference('lille')), $container->getDefinition('foo')->getArguments());
477 public function provideAutodiscoveredAutowiringOrder()
480 array('CannotBeAutowiredForwardOrder'),
481 array('CannotBeAutowiredReverseOrder'),
486 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
487 * @expectedExceptionMessage Service "a" can use either autowiring or a factory, not both.
489 public function testWithFactory()
491 $container = new ContainerBuilder();
493 $container->register('a', __NAMESPACE__.'\A')
495 ->setAutowired(true);
497 $pass = new AutowirePass();
498 $pass->process($container);
508 public function __construct(Foo $foo)
523 public function __construct(A $a)
532 interface EInterface extends DInterface
540 class I implements IInterface
544 class F extends I implements EInterface
550 public function __construct(DInterface $d, EInterface $e, IInterface $i)
557 public function __construct(B $b, DInterface $d)
562 interface CollisionInterface
566 class CollisionA implements CollisionInterface
570 class CollisionB implements CollisionInterface
574 class CannotBeAutowired
576 public function __construct(CollisionInterface $collision)
581 class CannotBeAutowiredForwardOrder
583 public function __construct(CollisionA $a, CollisionInterface $b, CollisionB $c)
588 class CannotBeAutowiredReverseOrder
590 public function __construct(CollisionA $a, CollisionB $c, CollisionInterface $b)
601 public function __construct(Lille $l)
608 public function __construct(Dunglas $j, Dunglas $k)
613 class OptionalParameter
615 public function __construct(CollisionInterface $c = null, A $a, Foo $f = null)
620 class BadTypeHintedArgument
622 public function __construct(Dunglas $k, NotARealClass $r)
626 class BadParentTypeHintedArgument
628 public function __construct(Dunglas $k, OptionalServiceClass $r)
632 class NotGuessableArgument
634 public function __construct(Foo $k)
638 class NotGuessableArgumentForSubclass
640 public function __construct(A $k)
644 class MultipleArguments
646 public function __construct(A $k, $foo, Dunglas $dunglas)
651 class MultipleArgumentsOptionalScalar
653 public function __construct(A $a, $foo = 'default_val', Lille $lille = null)
657 class MultipleArgumentsOptionalScalarLast
659 public function __construct(A $a, Lille $lille, $foo = 'some_val')
663 class MultipleArgumentsOptionalScalarNotReallyOptional
665 public function __construct(A $a, $foo = 'default_val', Lille $lille)