Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / vendor / symfony / dependency-injection / Tests / Compiler / AutowirePassTest.php
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\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;
26
27 require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
28
29 /**
30  * @author Kévin Dunglas <dunglas@gmail.com>
31  */
32 class AutowirePassTest extends TestCase
33 {
34     public function testProcess()
35     {
36         $container = new ContainerBuilder();
37
38         $container->register(Foo::class);
39         $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
40         $barDefinition->setAutowired(true);
41
42         (new ResolveClassPass())->process($container);
43         (new AutowirePass())->process($container);
44
45         $this->assertCount(1, $container->getDefinition('bar')->getArguments());
46         $this->assertEquals(Foo::class, (string) $container->getDefinition('bar')->getArgument(0));
47     }
48
49     /**
50      * @requires PHP 5.6
51      */
52     public function testProcessVariadic()
53     {
54         $container = new ContainerBuilder();
55         $container->register(Foo::class);
56         $definition = $container->register('fooVariadic', FooVariadic::class);
57         $definition->setAutowired(true);
58
59         (new ResolveClassPass())->process($container);
60         (new AutowirePass())->process($container);
61
62         $this->assertCount(1, $container->getDefinition('fooVariadic')->getArguments());
63         $this->assertEquals(Foo::class, (string) $container->getDefinition('fooVariadic')->getArgument(0));
64     }
65
66     /**
67      * @group legacy
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.
71      */
72     public function testProcessAutowireParent()
73     {
74         $container = new ContainerBuilder();
75
76         $container->register(B::class);
77         $cDefinition = $container->register('c', __NAMESPACE__.'\C');
78         $cDefinition->setAutowired(true);
79
80         (new ResolveClassPass())->process($container);
81         (new AutowirePass())->process($container);
82
83         $this->assertCount(1, $container->getDefinition('c')->getArguments());
84         $this->assertEquals(B::class, (string) $container->getDefinition('c')->getArgument(0));
85     }
86
87     /**
88      * @group legacy
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.
92      */
93     public function testProcessLegacyAutowireWithAvailableInterface()
94     {
95         $container = new ContainerBuilder();
96
97         $container->setAlias(AInterface::class, B::class);
98         $container->register(B::class);
99         $cDefinition = $container->register('c', __NAMESPACE__.'\C');
100         $cDefinition->setAutowired(true);
101
102         (new ResolveClassPass())->process($container);
103         (new AutowirePass())->process($container);
104
105         $this->assertCount(1, $container->getDefinition('c')->getArguments());
106         $this->assertEquals(B::class, (string) $container->getDefinition('c')->getArgument(0));
107     }
108
109     /**
110      * @group legacy
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.
114      */
115     public function testProcessAutowireInterface()
116     {
117         $container = new ContainerBuilder();
118
119         $container->register(F::class);
120         $gDefinition = $container->register('g', __NAMESPACE__.'\G');
121         $gDefinition->setAutowired(true);
122
123         (new ResolveClassPass())->process($container);
124         (new AutowirePass())->process($container);
125
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));
130     }
131
132     public function testCompleteExistingDefinition()
133     {
134         $container = new ContainerBuilder();
135
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);
140
141         (new ResolveClassPass())->process($container);
142         (new AutowirePass())->process($container);
143
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));
147     }
148
149     public function testCompleteExistingDefinitionWithNotDefinedArguments()
150     {
151         $container = new ContainerBuilder();
152
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);
157
158         (new ResolveClassPass())->process($container);
159         (new AutowirePass())->process($container);
160
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));
164     }
165
166     /**
167      * @group legacy
168      */
169     public function testExceptionsAreStored()
170     {
171         $container = new ContainerBuilder();
172
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);
178
179         $pass = new AutowirePass(false);
180         $pass->process($container);
181         $this->assertCount(1, $pass->getAutowiringExceptions());
182     }
183
184     /**
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.
187      */
188     public function testPrivateConstructorThrowsAutowireException()
189     {
190         $container = new ContainerBuilder();
191
192         $container->autowire('private_service', __NAMESPACE__.'\PrivateConstructor');
193
194         $pass = new AutowirePass(true);
195         $pass->process($container);
196     }
197
198     /**
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".
201      */
202     public function testTypeCollision()
203     {
204         $container = new ContainerBuilder();
205
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);
211
212         $pass = new AutowirePass();
213         $pass->process($container);
214     }
215
216     /**
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".
219      */
220     public function testTypeNotGuessable()
221     {
222         $container = new ContainerBuilder();
223
224         $container->register('a1', __NAMESPACE__.'\Foo');
225         $container->register('a2', __NAMESPACE__.'\Foo');
226         $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument');
227         $aDefinition->setAutowired(true);
228
229         $pass = new AutowirePass();
230         $pass->process($container);
231     }
232
233     /**
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".
236      */
237     public function testTypeNotGuessableWithSubclass()
238     {
239         $container = new ContainerBuilder();
240
241         $container->register('a1', __NAMESPACE__.'\B');
242         $container->register('a2', __NAMESPACE__.'\B');
243         $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgumentForSubclass');
244         $aDefinition->setAutowired(true);
245
246         $pass = new AutowirePass();
247         $pass->process($container);
248     }
249
250     /**
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.
253      */
254     public function testTypeNotGuessableNoServicesFound()
255     {
256         $container = new ContainerBuilder();
257
258         $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
259         $aDefinition->setAutowired(true);
260
261         $pass = new AutowirePass();
262         $pass->process($container);
263     }
264
265     public function testTypeNotGuessableWithTypeSet()
266     {
267         $container = new ContainerBuilder();
268
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);
274
275         $pass = new AutowirePass();
276         $pass->process($container);
277
278         $this->assertCount(1, $container->getDefinition('a')->getArguments());
279         $this->assertEquals(Foo::class, (string) $container->getDefinition('a')->getArgument(0));
280     }
281
282     public function testWithTypeSet()
283     {
284         $container = new ContainerBuilder();
285
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);
291
292         $pass = new AutowirePass();
293         $pass->process($container);
294
295         $this->assertCount(1, $container->getDefinition('a')->getArguments());
296         $this->assertEquals(CollisionInterface::class, (string) $container->getDefinition('a')->getArgument(0));
297     }
298
299     /**
300      * @group legacy
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.
303      */
304     public function testCreateDefinition()
305     {
306         $container = new ContainerBuilder();
307
308         $coopTilleulsDefinition = $container->register('coop_tilleuls', __NAMESPACE__.'\LesTilleuls');
309         $coopTilleulsDefinition->setAutowired(true);
310
311         $pass = new AutowirePass();
312         $pass->process($container);
313
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));
317
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));
323
324         $lilleDefinition = $container->getDefinition('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Lille');
325         $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass());
326     }
327
328     public function testResolveParameter()
329     {
330         $container = new ContainerBuilder();
331
332         $container->setParameter('class_name', Bar::class);
333         $container->register(Foo::class);
334         $barDefinition = $container->register('bar', '%class_name%');
335         $barDefinition->setAutowired(true);
336
337         (new ResolveClassPass())->process($container);
338         (new AutowirePass())->process($container);
339
340         $this->assertEquals(Foo::class, $container->getDefinition('bar')->getArgument(0));
341     }
342
343     public function testOptionalParameter()
344     {
345         $container = new ContainerBuilder();
346
347         $container->register(A::class);
348         $container->register(Foo::class);
349         $optDefinition = $container->register('opt', __NAMESPACE__.'\OptionalParameter');
350         $optDefinition->setAutowired(true);
351
352         (new ResolveClassPass())->process($container);
353         (new AutowirePass())->process($container);
354
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));
359     }
360
361     public function testDontTriggerAutowiring()
362     {
363         $container = new ContainerBuilder();
364
365         $container->register(Foo::class);
366         $container->register('bar', __NAMESPACE__.'\Bar');
367
368         (new ResolveClassPass())->process($container);
369         (new AutowirePass())->process($container);
370
371         $this->assertCount(0, $container->getDefinition('bar')->getArguments());
372     }
373
374     /**
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.
377      */
378     public function testClassNotFoundThrowsException()
379     {
380         $container = new ContainerBuilder();
381
382         $aDefinition = $container->register('a', __NAMESPACE__.'\BadTypeHintedArgument');
383         $aDefinition->setAutowired(true);
384
385         $container->register(Dunglas::class, Dunglas::class);
386
387         $pass = new AutowirePass();
388         $pass->process($container);
389     }
390
391     /**
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).
394      */
395     public function testParentClassNotFoundThrowsException()
396     {
397         $container = new ContainerBuilder();
398
399         $aDefinition = $container->register('a', __NAMESPACE__.'\BadParentTypeHintedArgument');
400         $aDefinition->setAutowired(true);
401
402         $container->register(Dunglas::class, Dunglas::class);
403
404         $pass = new AutowirePass();
405         $pass->process($container);
406     }
407
408     /**
409      * @group legacy
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.
413      */
414     public function testDontUseAbstractServices()
415     {
416         $container = new ContainerBuilder();
417
418         $container->register(Foo::class)->setAbstract(true);
419         $container->register('foo', __NAMESPACE__.'\Foo');
420         $container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true);
421
422         (new ResolveClassPass())->process($container);
423         (new AutowirePass())->process($container);
424     }
425
426     public function testSomeSpecificArgumentsAreSet()
427     {
428         $container = new ContainerBuilder();
429
430         $container->register('foo', Foo::class);
431         $container->register(A::class);
432         $container->register(Dunglas::class);
433         $container->register('multiple', __NAMESPACE__.'\MultipleArguments')
434             ->setAutowired(true)
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'),
439                 3 => array('bar'),
440             ));
441
442         (new ResolveClassPass())->process($container);
443         (new AutowirePass())->process($container);
444
445         $definition = $container->getDefinition('multiple');
446         $this->assertEquals(
447             array(
448                 new TypedReference(A::class, A::class, MultipleArguments::class),
449                 new Reference('foo'),
450                 new TypedReference(Dunglas::class, Dunglas::class, MultipleArguments::class),
451                 array('bar'),
452             ),
453             $definition->getArguments()
454         );
455     }
456
457     /**
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.
460      */
461     public function testScalarArgsCannotBeAutowired()
462     {
463         $container = new ContainerBuilder();
464
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);
470
471         (new ResolveClassPass())->process($container);
472         (new AutowirePass())->process($container);
473     }
474
475     /**
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.
478      */
479     public function testNoTypeArgsCannotBeAutowired()
480     {
481         $container = new ContainerBuilder();
482
483         $container->register(A::class);
484         $container->register(Dunglas::class);
485         $container->register('arg_no_type_hint', __NAMESPACE__.'\MultipleArguments')
486             ->setAutowired(true);
487
488         (new ResolveClassPass())->process($container);
489         (new AutowirePass())->process($container);
490     }
491
492     public function testOptionalScalarNotReallyOptionalUsesDefaultValue()
493     {
494         $container = new ContainerBuilder();
495
496         $container->register(A::class);
497         $container->register(Lille::class);
498         $definition = $container->register('not_really_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalarNotReallyOptional')
499             ->setAutowired(true);
500
501         (new ResolveClassPass())->process($container);
502         (new AutowirePass())->process($container);
503
504         $this->assertSame('default_val', $definition->getArgument(1));
505     }
506
507     public function testOptionalScalarArgsDontMessUpOrder()
508     {
509         $container = new ContainerBuilder();
510
511         $container->register(A::class);
512         $container->register(Lille::class);
513         $container->register('with_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalar')
514             ->setAutowired(true);
515
516         (new ResolveClassPass())->process($container);
517         (new AutowirePass())->process($container);
518
519         $definition = $container->getDefinition('with_optional_scalar');
520         $this->assertEquals(
521             array(
522                 new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalar::class),
523                 // use the default value
524                 'default_val',
525                 new TypedReference(Lille::class, Lille::class),
526             ),
527             $definition->getArguments()
528         );
529     }
530
531     public function testOptionalScalarArgsNotPassedIfLast()
532     {
533         $container = new ContainerBuilder();
534
535         $container->register(A::class);
536         $container->register(Lille::class);
537         $container->register('with_optional_scalar_last', __NAMESPACE__.'\MultipleArgumentsOptionalScalarLast')
538             ->setAutowired(true);
539
540         (new ResolveClassPass())->process($container);
541         (new AutowirePass())->process($container);
542
543         $definition = $container->getDefinition('with_optional_scalar_last');
544         $this->assertEquals(
545             array(
546                 new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalarLast::class),
547                 new TypedReference(Lille::class, Lille::class, MultipleArgumentsOptionalScalarLast::class),
548             ),
549             $definition->getArguments()
550         );
551     }
552
553     public function testOptionalArgsNoRequiredForCoreClasses()
554     {
555         $container = new ContainerBuilder();
556
557         $container->register('foo', \SplFileObject::class)
558             ->addArgument('foo.txt')
559             ->setAutowired(true);
560
561         (new AutowirePass())->process($container);
562
563         $definition = $container->getDefinition('foo');
564         $this->assertEquals(
565             array('foo.txt'),
566             $definition->getArguments()
567         );
568     }
569
570     public function testSetterInjection()
571     {
572         $container = new ContainerBuilder();
573         $container->register(Foo::class);
574         $container->register(A::class);
575         $container->register(CollisionA::class);
576         $container->register(CollisionB::class);
577
578         // manually configure *one* call, to override autowiring
579         $container
580             ->register('setter_injection', SetterInjection::class)
581             ->setAutowired(true)
582             ->addMethodCall('setWithCallsConfigured', array('manual_arg1', 'manual_arg2'))
583         ;
584
585         (new ResolveClassPass())->process($container);
586         (new AutowireRequiredMethodsPass())->process($container);
587         (new AutowirePass())->process($container);
588
589         $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
590
591         $this->assertEquals(
592             array('setWithCallsConfigured', 'setFoo', 'setDependencies', 'setChildMethodWithoutDocBlock'),
593             array_column($methodCalls, 0)
594         );
595
596         // test setWithCallsConfigured args
597         $this->assertEquals(
598             array('manual_arg1', 'manual_arg2'),
599             $methodCalls[0][1]
600         );
601         // test setFoo args
602         $this->assertEquals(
603             array(new TypedReference(Foo::class, Foo::class, SetterInjection::class)),
604             $methodCalls[1][1]
605         );
606     }
607
608     public function testExplicitMethodInjection()
609     {
610         $container = new ContainerBuilder();
611         $container->register(Foo::class);
612         $container->register(A::class);
613         $container->register(CollisionA::class);
614         $container->register(CollisionB::class);
615
616         $container
617             ->register('setter_injection', SetterInjection::class)
618             ->setAutowired(true)
619             ->addMethodCall('notASetter', array())
620         ;
621
622         (new ResolveClassPass())->process($container);
623         (new AutowireRequiredMethodsPass())->process($container);
624         (new AutowirePass())->process($container);
625
626         $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
627
628         $this->assertEquals(
629             array('notASetter', 'setFoo', 'setDependencies', 'setWithCallsConfigured', 'setChildMethodWithoutDocBlock'),
630             array_column($methodCalls, 0)
631         );
632         $this->assertEquals(
633             array(new TypedReference(A::class, A::class, SetterInjection::class)),
634             $methodCalls[0][1]
635         );
636     }
637
638     /**
639      * @group legacy
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.
641      */
642     public function testTypedReference()
643     {
644         $container = new ContainerBuilder();
645
646         $container
647             ->register('bar', Bar::class)
648             ->setProperty('a', array(new TypedReference(A::class, A::class, Bar::class)))
649         ;
650
651         $pass = new AutowirePass();
652         $pass->process($container);
653
654         $this->assertSame(A::class, $container->getDefinition('autowired.'.A::class)->getClass());
655     }
656
657     /**
658      * @dataProvider getCreateResourceTests
659      * @group legacy
660      */
661     public function testCreateResourceForClass($className, $isEqual)
662     {
663         $startingResource = AutowirePass::createResourceForClass(
664             new \ReflectionClass(__NAMESPACE__.'\ClassForResource')
665         );
666         $newResource = AutowirePass::createResourceForClass(
667             new \ReflectionClass(__NAMESPACE__.'\\'.$className)
668         );
669
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);
675
676         if ($isEqual) {
677             $this->assertEquals($startingResource, $newResource);
678         } else {
679             $this->assertNotEquals($startingResource, $newResource);
680         }
681     }
682
683     public function getCreateResourceTests()
684     {
685         return array(
686             array('IdenticalClassResource', true),
687             array('ClassChangedConstructorArgs', false),
688         );
689     }
690
691     public function testIgnoreServiceWithClassNotExisting()
692     {
693         $container = new ContainerBuilder();
694
695         $container->register('class_not_exist', __NAMESPACE__.'\OptionalServiceClass');
696
697         $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
698         $barDefinition->setAutowired(true);
699
700         $container->register(Foo::class, Foo::class);
701
702         $pass = new AutowirePass();
703         $pass->process($container);
704
705         $this->assertTrue($container->hasDefinition('bar'));
706     }
707
708     public function testSetterInjectionCollisionThrowsException()
709     {
710         $container = new ContainerBuilder();
711
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);
716
717         (new AutowireRequiredMethodsPass())->process($container);
718
719         $pass = new AutowirePass();
720
721         try {
722             $pass->process($container);
723         } catch (AutowiringFailedException $e) {
724         }
725
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());
728     }
729
730     /**
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?
733      */
734     public function testInterfaceWithNoImplementationSuggestToWriteOne()
735     {
736         $container = new ContainerBuilder();
737
738         $aDefinition = $container->register('my_service', K::class);
739         $aDefinition->setAutowired(true);
740
741         (new AutowireRequiredMethodsPass())->process($container);
742
743         $pass = new AutowirePass();
744         $pass->process($container);
745     }
746
747     /**
748      * @group legacy
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.
752      */
753     public function testProcessDoesNotTriggerDeprecations()
754     {
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);
759
760         $pass = new AutowirePass();
761         $pass->process($container);
762
763         $this->assertTrue($container->hasDefinition('deprecated'));
764         $this->assertTrue($container->hasDefinition('foo'));
765         $this->assertTrue($container->hasDefinition('bar'));
766     }
767
768     public function testEmptyStringIsKept()
769     {
770         $container = new ContainerBuilder();
771
772         $container->register(A::class);
773         $container->register(Lille::class);
774         $container->register('foo', __NAMESPACE__.'\MultipleArgumentsOptionalScalar')
775             ->setAutowired(true)
776             ->setArguments(array('', ''));
777
778         (new ResolveClassPass())->process($container);
779         (new AutowirePass())->process($container);
780
781         $this->assertEquals(array(new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalar::class), '', new TypedReference(Lille::class, Lille::class)), $container->getDefinition('foo')->getArguments());
782     }
783
784     public function testWithFactory()
785     {
786         $container = new ContainerBuilder();
787
788         $container->register(Foo::class);
789         $definition = $container->register('a', A::class)
790             ->setFactory(array(A::class, 'create'))
791             ->setAutowired(true);
792
793         (new ResolveClassPass())->process($container);
794         (new AutowirePass())->process($container);
795
796         $this->assertEquals(array(new TypedReference(Foo::class, Foo::class, A::class)), $definition->getArguments());
797     }
798
799     /**
800      * @dataProvider provideNotWireableCalls
801      * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
802      */
803     public function testNotWireableCalls($method, $expectedMsg)
804     {
805         $container = new ContainerBuilder();
806
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())
812         ;
813
814         if ($method) {
815             $foo->addMethodCall($method, array());
816         }
817
818         if (method_exists($this, 'expectException')) {
819             $this->expectException(RuntimeException::class);
820             $this->expectExceptionMessage($expectedMsg);
821         } else {
822             $this->setExpectedException(RuntimeException::class, $expectedMsg);
823         }
824
825         (new ResolveClassPass())->process($container);
826         (new AutowireRequiredMethodsPass())->process($container);
827         (new AutowirePass())->process($container);
828     }
829
830     public function provideNotWireableCalls()
831     {
832         return array(
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.'),
836         );
837     }
838
839     /**
840      * @group legacy
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.
844      */
845     public function testByIdAlternative()
846     {
847         $container = new ContainerBuilder();
848
849         $container->setAlias(IInterface::class, 'i');
850         $container->register('i', I::class);
851         $container->register('j', J::class)
852             ->setAutowired(true);
853
854         $pass = new AutowirePass();
855         $pass->process($container);
856     }
857
858     /**
859      * @group legacy
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.
861      */
862     public function testTypedReferenceDeprecationNotice()
863     {
864         $container = new ContainerBuilder();
865
866         $container->register('aClass', A::class);
867         $container->setAlias(AInterface::class, 'aClass');
868         $container
869             ->register('bar', Bar::class)
870             ->setProperty('a', array(new TypedReference(A::class, A::class, Bar::class)))
871         ;
872
873         $pass = new AutowirePass();
874         $pass->process($container);
875     }
876
877     /**
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.
880      */
881     public function testExceptionWhenAliasExists()
882     {
883         $container = new ContainerBuilder();
884
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);
892
893         $pass = new AutowirePass();
894         $pass->process($container);
895     }
896
897     /**
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".
900      */
901     public function testExceptionWhenAliasDoesNotExist()
902     {
903         $container = new ContainerBuilder();
904
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);
911
912         $pass = new AutowirePass();
913         $pass->process($container);
914     }
915
916     public function testInlineServicesAreNotCandidates()
917     {
918         $container = new ContainerBuilder();
919         $loader = new XmlFileLoader($container, new FileLocator(realpath(__DIR__.'/../Fixtures/xml')));
920         $loader->load('services_inline_not_candidate.xml');
921
922         $pass = new AutowirePass();
923         $pass->process($container);
924
925         $this->assertSame(array(), $container->getDefinition('autowired')->getArguments());
926     }
927 }