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\Config\FileLocator;
16 use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
17 use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
18 use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
19 use Symfony\Component\DependencyInjection\ContainerBuilder;
20 use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException;
21 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
22 use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
23 use Symfony\Component\DependencyInjection\Reference;
24 use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic;
25 use Symfony\Component\DependencyInjection\TypedReference;
27 require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
30 * @author Kévin Dunglas <dunglas@gmail.com>
32 class AutowirePassTest extends TestCase
34 public function testProcess()
36 $container = new ContainerBuilder();
38 $container->register(Foo::class);
39 $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
40 $barDefinition->setAutowired(true);
42 (new ResolveClassPass())->process($container);
43 (new AutowirePass())->process($container);
45 $this->assertCount(1, $container->getDefinition('bar')->getArguments());
46 $this->assertEquals(Foo::class, (string) $container->getDefinition('bar')->getArgument(0));
52 public function testProcessVariadic()
54 $container = new ContainerBuilder();
55 $container->register(Foo::class);
56 $definition = $container->register('fooVariadic', FooVariadic::class);
57 $definition->setAutowired(true);
59 (new ResolveClassPass())->process($container);
60 (new AutowirePass())->process($container);
62 $this->assertCount(1, $container->getDefinition('fooVariadic')->getArguments());
63 $this->assertEquals(Foo::class, (string) $container->getDefinition('fooVariadic')->getArgument(0));
68 * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should alias the "Symfony\Component\DependencyInjection\Tests\Compiler\B" service to "Symfony\Component\DependencyInjection\Tests\Compiler\A" instead.
69 * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\RuntimeException
70 * @expectedExceptionMessageInSymfony4 Cannot autowire service "c": argument "$a" of method "Symfony\Component\DependencyInjection\Tests\Compiler\C::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\A" but no such service exists. You should maybe alias this class to the existing "Symfony\Component\DependencyInjection\Tests\Compiler\B" service.
72 public function testProcessAutowireParent()
74 $container = new ContainerBuilder();
76 $container->register(B::class);
77 $cDefinition = $container->register('c', __NAMESPACE__.'\C');
78 $cDefinition->setAutowired(true);
80 (new ResolveClassPass())->process($container);
81 (new AutowirePass())->process($container);
83 $this->assertCount(1, $container->getDefinition('c')->getArguments());
84 $this->assertEquals(B::class, (string) $container->getDefinition('c')->getArgument(0));
89 * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. Try changing the type-hint for argument "$a" of method "Symfony\Component\DependencyInjection\Tests\Compiler\C::__construct()" to "Symfony\Component\DependencyInjection\Tests\Compiler\AInterface" instead.
90 * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\RuntimeException
91 * @expectedExceptionMessageInSymfony4 Cannot autowire service "c": argument "$a" of method "Symfony\Component\DependencyInjection\Tests\Compiler\C::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\A" but no such service exists. You should maybe alias this class to the existing "Symfony\Component\DependencyInjection\Tests\Compiler\B" service.
93 public function testProcessLegacyAutowireWithAvailableInterface()
95 $container = new ContainerBuilder();
97 $container->setAlias(AInterface::class, B::class);
98 $container->register(B::class);
99 $cDefinition = $container->register('c', __NAMESPACE__.'\C');
100 $cDefinition->setAutowired(true);
102 (new ResolveClassPass())->process($container);
103 (new AutowirePass())->process($container);
105 $this->assertCount(1, $container->getDefinition('c')->getArguments());
106 $this->assertEquals(B::class, (string) $container->getDefinition('c')->getArgument(0));
111 * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should alias the "Symfony\Component\DependencyInjection\Tests\Compiler\F" service to "Symfony\Component\DependencyInjection\Tests\Compiler\DInterface" instead.
112 * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\RuntimeException
113 * @expectedExceptionMessageInSymfony4 Cannot autowire service "g": argument "$d" of method "Symfony\Component\DependencyInjection\Tests\Compiler\G::__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\DInterface" but no such service exists. You should maybe alias this interface to the existing "Symfony\Component\DependencyInjection\Tests\Compiler\F" service.
115 public function testProcessAutowireInterface()
117 $container = new ContainerBuilder();
119 $container->register(F::class);
120 $gDefinition = $container->register('g', __NAMESPACE__.'\G');
121 $gDefinition->setAutowired(true);
123 (new ResolveClassPass())->process($container);
124 (new AutowirePass())->process($container);
126 $this->assertCount(3, $container->getDefinition('g')->getArguments());
127 $this->assertEquals(F::class, (string) $container->getDefinition('g')->getArgument(0));
128 $this->assertEquals(F::class, (string) $container->getDefinition('g')->getArgument(1));
129 $this->assertEquals(F::class, (string) $container->getDefinition('g')->getArgument(2));
132 public function testCompleteExistingDefinition()
134 $container = new ContainerBuilder();
136 $container->register('b', __NAMESPACE__.'\B');
137 $container->register(DInterface::class, F::class);
138 $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument(new Reference('b'));
139 $hDefinition->setAutowired(true);
141 (new ResolveClassPass())->process($container);
142 (new AutowirePass())->process($container);
144 $this->assertCount(2, $container->getDefinition('h')->getArguments());
145 $this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0));
146 $this->assertEquals(DInterface::class, (string) $container->getDefinition('h')->getArgument(1));
149 public function testCompleteExistingDefinitionWithNotDefinedArguments()
151 $container = new ContainerBuilder();
153 $container->register(B::class);
154 $container->register(DInterface::class, F::class);
155 $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument('')->addArgument('');
156 $hDefinition->setAutowired(true);
158 (new ResolveClassPass())->process($container);
159 (new AutowirePass())->process($container);
161 $this->assertCount(2, $container->getDefinition('h')->getArguments());
162 $this->assertEquals(B::class, (string) $container->getDefinition('h')->getArgument(0));
163 $this->assertEquals(DInterface::class, (string) $container->getDefinition('h')->getArgument(1));
169 public function testExceptionsAreStored()
171 $container = new ContainerBuilder();
173 $container->register('c1', __NAMESPACE__.'\CollisionA');
174 $container->register('c2', __NAMESPACE__.'\CollisionB');
175 $container->register('c3', __NAMESPACE__.'\CollisionB');
176 $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
177 $aDefinition->setAutowired(true);
179 $pass = new AutowirePass(false);
180 $pass->process($container);
181 $this->assertCount(1, $pass->getAutowiringExceptions());
185 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
186 * @expectedExceptionMessage Invalid service "private_service": constructor of class "Symfony\Component\DependencyInjection\Tests\Compiler\PrivateConstructor" must be public.
188 public function testPrivateConstructorThrowsAutowireException()
190 $container = new ContainerBuilder();
192 $container->autowire('private_service', __NAMESPACE__.'\PrivateConstructor');
194 $pass = new AutowirePass(true);
195 $pass->process($container);
199 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
200 * @expectedExceptionMessage Cannot autowire service "a": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\CannotBeAutowired::__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" but no such service exists. You should maybe alias this interface to one of these existing services: "c1", "c2", "c3".
202 public function testTypeCollision()
204 $container = new ContainerBuilder();
206 $container->register('c1', __NAMESPACE__.'\CollisionA');
207 $container->register('c2', __NAMESPACE__.'\CollisionB');
208 $container->register('c3', __NAMESPACE__.'\CollisionB');
209 $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
210 $aDefinition->setAutowired(true);
212 $pass = new AutowirePass();
213 $pass->process($container);
217 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
218 * @expectedExceptionMessage Cannot autowire service "a": argument "$k" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotGuessableArgument::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" but no such service exists. You should maybe alias this class to one of these existing services: "a1", "a2".
220 public function testTypeNotGuessable()
222 $container = new ContainerBuilder();
224 $container->register('a1', __NAMESPACE__.'\Foo');
225 $container->register('a2', __NAMESPACE__.'\Foo');
226 $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument');
227 $aDefinition->setAutowired(true);
229 $pass = new AutowirePass();
230 $pass->process($container);
234 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
235 * @expectedExceptionMessage Cannot autowire service "a": argument "$k" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotGuessableArgumentForSubclass::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\A" but no such service exists. You should maybe alias this class to one of these existing services: "a1", "a2".
237 public function testTypeNotGuessableWithSubclass()
239 $container = new ContainerBuilder();
241 $container->register('a1', __NAMESPACE__.'\B');
242 $container->register('a2', __NAMESPACE__.'\B');
243 $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgumentForSubclass');
244 $aDefinition->setAutowired(true);
246 $pass = new AutowirePass();
247 $pass->process($container);
251 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
252 * @expectedExceptionMessage Cannot autowire service "a": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\CannotBeAutowired::__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" but no such service exists.
254 public function testTypeNotGuessableNoServicesFound()
256 $container = new ContainerBuilder();
258 $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
259 $aDefinition->setAutowired(true);
261 $pass = new AutowirePass();
262 $pass->process($container);
265 public function testTypeNotGuessableWithTypeSet()
267 $container = new ContainerBuilder();
269 $container->register('a1', __NAMESPACE__.'\Foo');
270 $container->register('a2', __NAMESPACE__.'\Foo');
271 $container->register(Foo::class, Foo::class);
272 $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument');
273 $aDefinition->setAutowired(true);
275 $pass = new AutowirePass();
276 $pass->process($container);
278 $this->assertCount(1, $container->getDefinition('a')->getArguments());
279 $this->assertEquals(Foo::class, (string) $container->getDefinition('a')->getArgument(0));
282 public function testWithTypeSet()
284 $container = new ContainerBuilder();
286 $container->register('c1', __NAMESPACE__.'\CollisionA');
287 $container->register('c2', __NAMESPACE__.'\CollisionB');
288 $container->setAlias(CollisionInterface::class, 'c2');
289 $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
290 $aDefinition->setAutowired(true);
292 $pass = new AutowirePass();
293 $pass->process($container);
295 $this->assertCount(1, $container->getDefinition('a')->getArguments());
296 $this->assertEquals(CollisionInterface::class, (string) $container->getDefinition('a')->getArgument(0));
301 * @expectedDeprecation Relying on service auto-registration for type "Symfony\Component\DependencyInjection\Tests\Compiler\Lille" is deprecated since Symfony 3.4 and won't be supported in 4.0. Create a service named "Symfony\Component\DependencyInjection\Tests\Compiler\Lille" instead.
302 * @expectedDeprecation Relying on service auto-registration for type "Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas" is deprecated since Symfony 3.4 and won't be supported in 4.0. Create a service named "Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas" instead.
304 public function testCreateDefinition()
306 $container = new ContainerBuilder();
308 $coopTilleulsDefinition = $container->register('coop_tilleuls', __NAMESPACE__.'\LesTilleuls');
309 $coopTilleulsDefinition->setAutowired(true);
311 $pass = new AutowirePass();
312 $pass->process($container);
314 $this->assertCount(2, $container->getDefinition('coop_tilleuls')->getArguments());
315 $this->assertEquals('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas', $container->getDefinition('coop_tilleuls')->getArgument(0));
316 $this->assertEquals('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas', $container->getDefinition('coop_tilleuls')->getArgument(1));
318 $dunglasDefinition = $container->getDefinition('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas');
319 $this->assertEquals(__NAMESPACE__.'\Dunglas', $dunglasDefinition->getClass());
320 $this->assertFalse($dunglasDefinition->isPublic());
321 $this->assertCount(1, $dunglasDefinition->getArguments());
322 $this->assertEquals('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Lille', $dunglasDefinition->getArgument(0));
324 $lilleDefinition = $container->getDefinition('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Lille');
325 $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass());
328 public function testResolveParameter()
330 $container = new ContainerBuilder();
332 $container->setParameter('class_name', Bar::class);
333 $container->register(Foo::class);
334 $barDefinition = $container->register('bar', '%class_name%');
335 $barDefinition->setAutowired(true);
337 (new ResolveClassPass())->process($container);
338 (new AutowirePass())->process($container);
340 $this->assertEquals(Foo::class, $container->getDefinition('bar')->getArgument(0));
343 public function testOptionalParameter()
345 $container = new ContainerBuilder();
347 $container->register(A::class);
348 $container->register(Foo::class);
349 $optDefinition = $container->register('opt', __NAMESPACE__.'\OptionalParameter');
350 $optDefinition->setAutowired(true);
352 (new ResolveClassPass())->process($container);
353 (new AutowirePass())->process($container);
355 $definition = $container->getDefinition('opt');
356 $this->assertNull($definition->getArgument(0));
357 $this->assertEquals(A::class, $definition->getArgument(1));
358 $this->assertEquals(Foo::class, $definition->getArgument(2));
361 public function testDontTriggerAutowiring()
363 $container = new ContainerBuilder();
365 $container->register(Foo::class);
366 $container->register('bar', __NAMESPACE__.'\Bar');
368 (new ResolveClassPass())->process($container);
369 (new AutowirePass())->process($container);
371 $this->assertCount(0, $container->getDefinition('bar')->getArguments());
375 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
376 * @expectedExceptionMessage Cannot autowire service "a": argument "$r" of method "Symfony\Component\DependencyInjection\Tests\Compiler\BadTypeHintedArgument::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass" but this class was not found.
378 public function testClassNotFoundThrowsException()
380 $container = new ContainerBuilder();
382 $aDefinition = $container->register('a', __NAMESPACE__.'\BadTypeHintedArgument');
383 $aDefinition->setAutowired(true);
385 $container->register(Dunglas::class, Dunglas::class);
387 $pass = new AutowirePass();
388 $pass->process($container);
392 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
393 * @expectedExceptionMessage Cannot autowire service "a": argument "$r" of method "Symfony\Component\DependencyInjection\Tests\Compiler\BadParentTypeHintedArgument::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\OptionalServiceClass" but this class is missing a parent class (Class Symfony\Bug\NotExistClass not found).
395 public function testParentClassNotFoundThrowsException()
397 $container = new ContainerBuilder();
399 $aDefinition = $container->register('a', __NAMESPACE__.'\BadParentTypeHintedArgument');
400 $aDefinition->setAutowired(true);
402 $container->register(Dunglas::class, Dunglas::class);
404 $pass = new AutowirePass();
405 $pass->process($container);
410 * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should rename (or alias) the "foo" service to "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" instead.
411 * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
412 * @expectedExceptionMessageInSymfony4 Cannot autowire service "bar": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\Bar::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" but this service is abstract. You should maybe alias this class to the existing "foo" service.
414 public function testDontUseAbstractServices()
416 $container = new ContainerBuilder();
418 $container->register(Foo::class)->setAbstract(true);
419 $container->register('foo', __NAMESPACE__.'\Foo');
420 $container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true);
422 (new ResolveClassPass())->process($container);
423 (new AutowirePass())->process($container);
426 public function testSomeSpecificArgumentsAreSet()
428 $container = new ContainerBuilder();
430 $container->register('foo', Foo::class);
431 $container->register(A::class);
432 $container->register(Dunglas::class);
433 $container->register('multiple', __NAMESPACE__.'\MultipleArguments')
435 // set the 2nd (index 1) argument only: autowire the first and third
436 // args are: A, Foo, Dunglas
437 ->setArguments(array(
438 1 => new Reference('foo'),
442 (new ResolveClassPass())->process($container);
443 (new AutowirePass())->process($container);
445 $definition = $container->getDefinition('multiple');
448 new TypedReference(A::class, A::class, MultipleArguments::class),
449 new Reference('foo'),
450 new TypedReference(Dunglas::class, Dunglas::class, MultipleArguments::class),
453 $definition->getArguments()
458 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
459 * @expectedExceptionMessage Cannot autowire service "arg_no_type_hint": argument "$bar" of method "Symfony\Component\DependencyInjection\Tests\Compiler\MultipleArguments::__construct()" is type-hinted "array", you should configure its value explicitly.
461 public function testScalarArgsCannotBeAutowired()
463 $container = new ContainerBuilder();
465 $container->register(A::class);
466 $container->register(Dunglas::class);
467 $container->register('arg_no_type_hint', __NAMESPACE__.'\MultipleArguments')
468 ->setArguments(array(1 => 'foo'))
469 ->setAutowired(true);
471 (new ResolveClassPass())->process($container);
472 (new AutowirePass())->process($container);
476 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
477 * @expectedExceptionMessage Cannot autowire service "arg_no_type_hint": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\MultipleArguments::__construct()" has no type-hint, you should configure its value explicitly.
479 public function testNoTypeArgsCannotBeAutowired()
481 $container = new ContainerBuilder();
483 $container->register(A::class);
484 $container->register(Dunglas::class);
485 $container->register('arg_no_type_hint', __NAMESPACE__.'\MultipleArguments')
486 ->setAutowired(true);
488 (new ResolveClassPass())->process($container);
489 (new AutowirePass())->process($container);
492 public function testOptionalScalarNotReallyOptionalUsesDefaultValue()
494 $container = new ContainerBuilder();
496 $container->register(A::class);
497 $container->register(Lille::class);
498 $definition = $container->register('not_really_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalarNotReallyOptional')
499 ->setAutowired(true);
501 (new ResolveClassPass())->process($container);
502 (new AutowirePass())->process($container);
504 $this->assertSame('default_val', $definition->getArgument(1));
507 public function testOptionalScalarArgsDontMessUpOrder()
509 $container = new ContainerBuilder();
511 $container->register(A::class);
512 $container->register(Lille::class);
513 $container->register('with_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalar')
514 ->setAutowired(true);
516 (new ResolveClassPass())->process($container);
517 (new AutowirePass())->process($container);
519 $definition = $container->getDefinition('with_optional_scalar');
522 new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalar::class),
523 // use the default value
525 new TypedReference(Lille::class, Lille::class),
527 $definition->getArguments()
531 public function testOptionalScalarArgsNotPassedIfLast()
533 $container = new ContainerBuilder();
535 $container->register(A::class);
536 $container->register(Lille::class);
537 $container->register('with_optional_scalar_last', __NAMESPACE__.'\MultipleArgumentsOptionalScalarLast')
538 ->setAutowired(true);
540 (new ResolveClassPass())->process($container);
541 (new AutowirePass())->process($container);
543 $definition = $container->getDefinition('with_optional_scalar_last');
546 new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalarLast::class),
547 new TypedReference(Lille::class, Lille::class, MultipleArgumentsOptionalScalarLast::class),
549 $definition->getArguments()
553 public function testOptionalArgsNoRequiredForCoreClasses()
555 $container = new ContainerBuilder();
557 $container->register('foo', \SplFileObject::class)
558 ->addArgument('foo.txt')
559 ->setAutowired(true);
561 (new AutowirePass())->process($container);
563 $definition = $container->getDefinition('foo');
566 $definition->getArguments()
570 public function testSetterInjection()
572 $container = new ContainerBuilder();
573 $container->register(Foo::class);
574 $container->register(A::class);
575 $container->register(CollisionA::class);
576 $container->register(CollisionB::class);
578 // manually configure *one* call, to override autowiring
580 ->register('setter_injection', SetterInjection::class)
582 ->addMethodCall('setWithCallsConfigured', array('manual_arg1', 'manual_arg2'))
585 (new ResolveClassPass())->process($container);
586 (new AutowireRequiredMethodsPass())->process($container);
587 (new AutowirePass())->process($container);
589 $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
592 array('setWithCallsConfigured', 'setFoo', 'setDependencies', 'setChildMethodWithoutDocBlock'),
593 array_column($methodCalls, 0)
596 // test setWithCallsConfigured args
598 array('manual_arg1', 'manual_arg2'),
603 array(new TypedReference(Foo::class, Foo::class, SetterInjection::class)),
608 public function testExplicitMethodInjection()
610 $container = new ContainerBuilder();
611 $container->register(Foo::class);
612 $container->register(A::class);
613 $container->register(CollisionA::class);
614 $container->register(CollisionB::class);
617 ->register('setter_injection', SetterInjection::class)
619 ->addMethodCall('notASetter', array())
622 (new ResolveClassPass())->process($container);
623 (new AutowireRequiredMethodsPass())->process($container);
624 (new AutowirePass())->process($container);
626 $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
629 array('notASetter', 'setFoo', 'setDependencies', 'setWithCallsConfigured', 'setChildMethodWithoutDocBlock'),
630 array_column($methodCalls, 0)
633 array(new TypedReference(A::class, A::class, SetterInjection::class)),
640 * @expectedDeprecation Relying on service auto-registration for type "Symfony\Component\DependencyInjection\Tests\Compiler\A" is deprecated since Symfony 3.4 and won't be supported in 4.0. Create a service named "Symfony\Component\DependencyInjection\Tests\Compiler\A" instead.
642 public function testTypedReference()
644 $container = new ContainerBuilder();
647 ->register('bar', Bar::class)
648 ->setProperty('a', array(new TypedReference(A::class, A::class, Bar::class)))
651 $pass = new AutowirePass();
652 $pass->process($container);
654 $this->assertSame(A::class, $container->getDefinition('autowired.'.A::class)->getClass());
658 * @dataProvider getCreateResourceTests
661 public function testCreateResourceForClass($className, $isEqual)
663 $startingResource = AutowirePass::createResourceForClass(
664 new \ReflectionClass(__NAMESPACE__.'\ClassForResource')
666 $newResource = AutowirePass::createResourceForClass(
667 new \ReflectionClass(__NAMESPACE__.'\\'.$className)
670 // hack so the objects don't differ by the class name
671 $startingReflObject = new \ReflectionObject($startingResource);
672 $reflProp = $startingReflObject->getProperty('class');
673 $reflProp->setAccessible(true);
674 $reflProp->setValue($startingResource, __NAMESPACE__.'\\'.$className);
677 $this->assertEquals($startingResource, $newResource);
679 $this->assertNotEquals($startingResource, $newResource);
683 public function getCreateResourceTests()
686 array('IdenticalClassResource', true),
687 array('ClassChangedConstructorArgs', false),
691 public function testIgnoreServiceWithClassNotExisting()
693 $container = new ContainerBuilder();
695 $container->register('class_not_exist', __NAMESPACE__.'\OptionalServiceClass');
697 $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
698 $barDefinition->setAutowired(true);
700 $container->register(Foo::class, Foo::class);
702 $pass = new AutowirePass();
703 $pass->process($container);
705 $this->assertTrue($container->hasDefinition('bar'));
708 public function testSetterInjectionCollisionThrowsException()
710 $container = new ContainerBuilder();
712 $container->register('c1', CollisionA::class);
713 $container->register('c2', CollisionB::class);
714 $aDefinition = $container->register('setter_injection_collision', SetterInjectionCollision::class);
715 $aDefinition->setAutowired(true);
717 (new AutowireRequiredMethodsPass())->process($container);
719 $pass = new AutowirePass();
722 $pass->process($container);
723 } catch (AutowiringFailedException $e) {
726 $this->assertNotNull($e);
727 $this->assertSame('Cannot autowire service "setter_injection_collision": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionCollision::setMultipleInstancesForOneArg()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" but no such service exists. You should maybe alias this interface to one of these existing services: "c1", "c2".', $e->getMessage());
731 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
732 * @expectedExceptionMessage Cannot autowire service "my_service": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\K::__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" but no such service exists. Did you create a class that implements this interface?
734 public function testInterfaceWithNoImplementationSuggestToWriteOne()
736 $container = new ContainerBuilder();
738 $aDefinition = $container->register('my_service', K::class);
739 $aDefinition->setAutowired(true);
741 (new AutowireRequiredMethodsPass())->process($container);
743 $pass = new AutowirePass();
744 $pass->process($container);
749 * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. You should rename (or alias) the "foo" service to "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" instead.
750 * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
751 * @expectedExceptionMessageInSymfony4 Cannot autowire service "bar": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\Bar::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" but no such service exists. You should maybe alias this class to the existing "foo" service.
753 public function testProcessDoesNotTriggerDeprecations()
755 $container = new ContainerBuilder();
756 $container->register('deprecated', 'Symfony\Component\DependencyInjection\Tests\Fixtures\DeprecatedClass')->setDeprecated(true);
757 $container->register('foo', __NAMESPACE__.'\Foo');
758 $container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true);
760 $pass = new AutowirePass();
761 $pass->process($container);
763 $this->assertTrue($container->hasDefinition('deprecated'));
764 $this->assertTrue($container->hasDefinition('foo'));
765 $this->assertTrue($container->hasDefinition('bar'));
768 public function testEmptyStringIsKept()
770 $container = new ContainerBuilder();
772 $container->register(A::class);
773 $container->register(Lille::class);
774 $container->register('foo', __NAMESPACE__.'\MultipleArgumentsOptionalScalar')
776 ->setArguments(array('', ''));
778 (new ResolveClassPass())->process($container);
779 (new AutowirePass())->process($container);
781 $this->assertEquals(array(new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalar::class), '', new TypedReference(Lille::class, Lille::class)), $container->getDefinition('foo')->getArguments());
784 public function testWithFactory()
786 $container = new ContainerBuilder();
788 $container->register(Foo::class);
789 $definition = $container->register('a', A::class)
790 ->setFactory(array(A::class, 'create'))
791 ->setAutowired(true);
793 (new ResolveClassPass())->process($container);
794 (new AutowirePass())->process($container);
796 $this->assertEquals(array(new TypedReference(Foo::class, Foo::class, A::class)), $definition->getArguments());
800 * @dataProvider provideNotWireableCalls
801 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
803 public function testNotWireableCalls($method, $expectedMsg)
805 $container = new ContainerBuilder();
807 $foo = $container->register('foo', NotWireable::class)->setAutowired(true)
808 ->addMethodCall('setBar', array())
809 ->addMethodCall('setOptionalNotAutowireable', array())
810 ->addMethodCall('setOptionalNoTypeHint', array())
811 ->addMethodCall('setOptionalArgNoAutowireable', array())
815 $foo->addMethodCall($method, array());
818 if (method_exists($this, 'expectException')) {
819 $this->expectException(RuntimeException::class);
820 $this->expectExceptionMessage($expectedMsg);
822 $this->setExpectedException(RuntimeException::class, $expectedMsg);
825 (new ResolveClassPass())->process($container);
826 (new AutowireRequiredMethodsPass())->process($container);
827 (new AutowirePass())->process($container);
830 public function provideNotWireableCalls()
833 array('setNotAutowireable', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setNotAutowireable()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass" but this class was not found.'),
834 array('setDifferentNamespace', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setDifferentNamespace()" references class "stdClass" but no such service exists. It cannot be auto-registered because it is from a different root namespace.'),
835 array(null, 'Invalid service "foo": method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setProtectedMethod()" must be public.'),
841 * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. Try changing the type-hint for argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead.
842 * @expectedExceptionInSymfony4 \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
843 * @expectedExceptionMessageInSymfony4 Cannot autowire service "j": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\I" but no such service exists. Try changing the type-hint to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead.
845 public function testByIdAlternative()
847 $container = new ContainerBuilder();
849 $container->setAlias(IInterface::class, 'i');
850 $container->register('i', I::class);
851 $container->register('j', J::class)
852 ->setAutowired(true);
854 $pass = new AutowirePass();
855 $pass->process($container);
860 * @expectedDeprecation Autowiring services based on the types they implement is deprecated since Symfony 3.3 and won't be supported in version 4.0. Try changing the type-hint for "Symfony\Component\DependencyInjection\Tests\Compiler\A" in "Symfony\Component\DependencyInjection\Tests\Compiler\Bar" to "Symfony\Component\DependencyInjection\Tests\Compiler\AInterface" instead.
862 public function testTypedReferenceDeprecationNotice()
864 $container = new ContainerBuilder();
866 $container->register('aClass', A::class);
867 $container->setAlias(AInterface::class, 'aClass');
869 ->register('bar', Bar::class)
870 ->setProperty('a', array(new TypedReference(A::class, A::class, Bar::class)))
873 $pass = new AutowirePass();
874 $pass->process($container);
878 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
879 * @expectedExceptionMessage Cannot autowire service "j": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\I" but no such service exists. Try changing the type-hint to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead.
881 public function testExceptionWhenAliasExists()
883 $container = new ContainerBuilder();
885 // multiple I services... but there *is* IInterface available
886 $container->setAlias(IInterface::class, 'i');
887 $container->register('i', I::class);
888 $container->register('i2', I::class);
889 // J type-hints against I concretely
890 $container->register('j', J::class)
891 ->setAutowired(true);
893 $pass = new AutowirePass();
894 $pass->process($container);
898 * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
899 * @expectedExceptionMessage Cannot autowire service "j": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\I" but no such service exists. You should maybe alias this class to one of these existing services: "i", "i2".
901 public function testExceptionWhenAliasDoesNotExist()
903 $container = new ContainerBuilder();
905 // multiple I instances... but no IInterface alias
906 $container->register('i', I::class);
907 $container->register('i2', I::class);
908 // J type-hints against I concretely
909 $container->register('j', J::class)
910 ->setAutowired(true);
912 $pass = new AutowirePass();
913 $pass->process($container);
916 public function testInlineServicesAreNotCandidates()
918 $container = new ContainerBuilder();
919 $loader = new XmlFileLoader($container, new FileLocator(realpath(__DIR__.'/../Fixtures/xml')));
920 $loader->load('services_inline_not_candidate.xml');
922 $pass = new AutowirePass();
923 $pass->process($container);
925 $this->assertSame(array(), $container->getDefinition('autowired')->getArguments());