Yaffs site version 1.1
[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\DependencyInjection\Compiler\AutowirePass;
16 use Symfony\Component\DependencyInjection\ContainerBuilder;
17 use Symfony\Component\DependencyInjection\Reference;
18
19 /**
20  * @author Kévin Dunglas <dunglas@gmail.com>
21  */
22 class AutowirePassTest extends TestCase
23 {
24     public function testProcess()
25     {
26         $container = new ContainerBuilder();
27
28         $container->register('foo', __NAMESPACE__.'\Foo');
29         $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
30         $barDefinition->setAutowired(true);
31
32         $pass = new AutowirePass();
33         $pass->process($container);
34
35         $this->assertCount(1, $container->getDefinition('bar')->getArguments());
36         $this->assertEquals('foo', (string) $container->getDefinition('bar')->getArgument(0));
37     }
38
39     public function testProcessAutowireParent()
40     {
41         $container = new ContainerBuilder();
42
43         $container->register('b', __NAMESPACE__.'\B');
44         $cDefinition = $container->register('c', __NAMESPACE__.'\C');
45         $cDefinition->setAutowired(true);
46
47         $pass = new AutowirePass();
48         $pass->process($container);
49
50         $this->assertCount(1, $container->getDefinition('c')->getArguments());
51         $this->assertEquals('b', (string) $container->getDefinition('c')->getArgument(0));
52     }
53
54     public function testProcessAutowireInterface()
55     {
56         $container = new ContainerBuilder();
57
58         $container->register('f', __NAMESPACE__.'\F');
59         $gDefinition = $container->register('g', __NAMESPACE__.'\G');
60         $gDefinition->setAutowired(true);
61
62         $pass = new AutowirePass();
63         $pass->process($container);
64
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));
69     }
70
71     public function testCompleteExistingDefinition()
72     {
73         $container = new ContainerBuilder();
74
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);
79
80         $pass = new AutowirePass();
81         $pass->process($container);
82
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));
86     }
87
88     public function testCompleteExistingDefinitionWithNotDefinedArguments()
89     {
90         $container = new ContainerBuilder();
91
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);
96
97         $pass = new AutowirePass();
98         $pass->process($container);
99
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));
103     }
104
105     /**
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).
108      */
109     public function testTypeCollision()
110     {
111         $container = new ContainerBuilder();
112
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);
118
119         $pass = new AutowirePass();
120         $pass->process($container);
121     }
122
123     /**
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).
126      */
127     public function testTypeNotGuessable()
128     {
129         $container = new ContainerBuilder();
130
131         $container->register('a1', __NAMESPACE__.'\Foo');
132         $container->register('a2', __NAMESPACE__.'\Foo');
133         $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument');
134         $aDefinition->setAutowired(true);
135
136         $pass = new AutowirePass();
137         $pass->process($container);
138     }
139
140     /**
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).
143      */
144     public function testTypeNotGuessableWithSubclass()
145     {
146         $container = new ContainerBuilder();
147
148         $container->register('a1', __NAMESPACE__.'\B');
149         $container->register('a2', __NAMESPACE__.'\B');
150         $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgumentForSubclass');
151         $aDefinition->setAutowired(true);
152
153         $pass = new AutowirePass();
154         $pass->process($container);
155     }
156
157     /**
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.
160      */
161     public function testTypeNotGuessableNoServicesFound()
162     {
163         $container = new ContainerBuilder();
164
165         $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
166         $aDefinition->setAutowired(true);
167
168         $pass = new AutowirePass();
169         $pass->process($container);
170     }
171
172     public function testTypeNotGuessableWithTypeSet()
173     {
174         $container = new ContainerBuilder();
175
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);
181
182         $pass = new AutowirePass();
183         $pass->process($container);
184
185         $this->assertCount(1, $container->getDefinition('a')->getArguments());
186         $this->assertEquals('a3', (string) $container->getDefinition('a')->getArgument(0));
187     }
188
189     public function testWithTypeSet()
190     {
191         $container = new ContainerBuilder();
192
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);
197
198         $pass = new AutowirePass();
199         $pass->process($container);
200
201         $this->assertCount(1, $container->getDefinition('a')->getArguments());
202         $this->assertEquals('c2', (string) $container->getDefinition('a')->getArgument(0));
203     }
204
205     public function testCreateDefinition()
206     {
207         $container = new ContainerBuilder();
208
209         $coopTilleulsDefinition = $container->register('coop_tilleuls', __NAMESPACE__.'\LesTilleuls');
210         $coopTilleulsDefinition->setAutowired(true);
211
212         $pass = new AutowirePass();
213         $pass->process($container);
214
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));
218
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));
224
225         $lilleDefinition = $container->getDefinition('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Lille');
226         $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass());
227     }
228
229     public function testResolveParameter()
230     {
231         $container = new ContainerBuilder();
232
233         $container->setParameter('class_name', __NAMESPACE__.'\Foo');
234         $container->register('foo', '%class_name%');
235         $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
236         $barDefinition->setAutowired(true);
237
238         $pass = new AutowirePass();
239         $pass->process($container);
240
241         $this->assertEquals('foo', $container->getDefinition('bar')->getArgument(0));
242     }
243
244     public function testOptionalParameter()
245     {
246         $container = new ContainerBuilder();
247
248         $container->register('a', __NAMESPACE__.'\A');
249         $container->register('foo', __NAMESPACE__.'\Foo');
250         $optDefinition = $container->register('opt', __NAMESPACE__.'\OptionalParameter');
251         $optDefinition->setAutowired(true);
252
253         $pass = new AutowirePass();
254         $pass->process($container);
255
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));
260     }
261
262     public function testDontTriggerAutowiring()
263     {
264         $container = new ContainerBuilder();
265
266         $container->register('foo', __NAMESPACE__.'\Foo');
267         $container->register('bar', __NAMESPACE__.'\Bar');
268
269         $pass = new AutowirePass();
270         $pass->process($container);
271
272         $this->assertCount(0, $container->getDefinition('bar')->getArguments());
273     }
274
275     /**
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).
278      */
279     public function testClassNotFoundThrowsException()
280     {
281         $container = new ContainerBuilder();
282
283         $aDefinition = $container->register('a', __NAMESPACE__.'\BadTypeHintedArgument');
284         $aDefinition->setAutowired(true);
285
286         $pass = new AutowirePass();
287         $pass->process($container);
288     }
289
290     /**
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).
293      */
294     public function testParentClassNotFoundThrowsException()
295     {
296         $container = new ContainerBuilder();
297
298         $aDefinition = $container->register('a', __NAMESPACE__.'\BadParentTypeHintedArgument');
299         $aDefinition->setAutowired(true);
300
301         $pass = new AutowirePass();
302         $pass->process($container);
303     }
304
305     public function testDontUseAbstractServices()
306     {
307         $container = new ContainerBuilder();
308
309         $container->register('abstract_foo', __NAMESPACE__.'\Foo')->setAbstract(true);
310         $container->register('foo', __NAMESPACE__.'\Foo');
311         $container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true);
312
313         $pass = new AutowirePass();
314         $pass->process($container);
315
316         $arguments = $container->getDefinition('bar')->getArguments();
317         $this->assertSame('foo', (string) $arguments[0]);
318     }
319
320     public function testSomeSpecificArgumentsAreSet()
321     {
322         $container = new ContainerBuilder();
323
324         $container->register('foo', __NAMESPACE__.'\Foo');
325         $container->register('a', __NAMESPACE__.'\A');
326         $container->register('dunglas', __NAMESPACE__.'\Dunglas');
327         $container->register('multiple', __NAMESPACE__.'\MultipleArguments')
328             ->setAutowired(true)
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'),
333             ));
334
335         $pass = new AutowirePass();
336         $pass->process($container);
337
338         $definition = $container->getDefinition('multiple');
339         $this->assertEquals(
340             array(
341                 new Reference('a'),
342                 new Reference('foo'),
343                 new Reference('dunglas'),
344             ),
345             $definition->getArguments()
346         );
347     }
348
349     /**
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.
352      */
353     public function testScalarArgsCannotBeAutowired()
354     {
355         $container = new ContainerBuilder();
356
357         $container->register('a', __NAMESPACE__.'\A');
358         $container->register('dunglas', __NAMESPACE__.'\Dunglas');
359         $container->register('arg_no_type_hint', __NAMESPACE__.'\MultipleArguments')
360             ->setAutowired(true);
361
362         $pass = new AutowirePass();
363         $pass->process($container);
364
365         $container->getDefinition('arg_no_type_hint');
366     }
367
368     /**
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.
371      */
372     public function testOptionalScalarNotReallyOptionalThrowException()
373     {
374         $container = new ContainerBuilder();
375
376         $container->register('a', __NAMESPACE__.'\A');
377         $container->register('lille', __NAMESPACE__.'\Lille');
378         $container->register('not_really_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalarNotReallyOptional')
379             ->setAutowired(true);
380
381         $pass = new AutowirePass();
382         $pass->process($container);
383     }
384
385     public function testOptionalScalarArgsDontMessUpOrder()
386     {
387         $container = new ContainerBuilder();
388
389         $container->register('a', __NAMESPACE__.'\A');
390         $container->register('lille', __NAMESPACE__.'\Lille');
391         $container->register('with_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalar')
392             ->setAutowired(true);
393
394         $pass = new AutowirePass();
395         $pass->process($container);
396
397         $definition = $container->getDefinition('with_optional_scalar');
398         $this->assertEquals(
399             array(
400                 new Reference('a'),
401                 // use the default value
402                 'default_val',
403                 new Reference('lille'),
404             ),
405             $definition->getArguments()
406         );
407     }
408
409     public function testOptionalScalarArgsNotPassedIfLast()
410     {
411         $container = new ContainerBuilder();
412
413         $container->register('a', __NAMESPACE__.'\A');
414         $container->register('lille', __NAMESPACE__.'\Lille');
415         $container->register('with_optional_scalar_last', __NAMESPACE__.'\MultipleArgumentsOptionalScalarLast')
416             ->setAutowired(true);
417
418         $pass = new AutowirePass();
419         $pass->process($container);
420
421         $definition = $container->getDefinition('with_optional_scalar_last');
422         $this->assertEquals(
423             array(
424                 new Reference('a'),
425                 new Reference('lille'),
426             ),
427             $definition->getArguments()
428         );
429     }
430
431     public function testIgnoreServiceWithClassNotExisting()
432     {
433         $container = new ContainerBuilder();
434
435         $container->register('class_not_exist', __NAMESPACE__.'\OptionalServiceClass');
436
437         $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
438         $barDefinition->setAutowired(true);
439
440         $pass = new AutowirePass();
441         $pass->process($container);
442
443         $this->assertTrue($container->hasDefinition('bar'));
444     }
445
446     public function testProcessDoesNotTriggerDeprecations()
447     {
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);
452
453         $pass = new AutowirePass();
454         $pass->process($container);
455
456         $this->assertTrue($container->hasDefinition('deprecated'));
457         $this->assertTrue($container->hasDefinition('foo'));
458         $this->assertTrue($container->hasDefinition('bar'));
459     }
460
461     public function testEmptyStringIsKept()
462     {
463         $container = new ContainerBuilder();
464
465         $container->register('a', __NAMESPACE__.'\A');
466         $container->register('lille', __NAMESPACE__.'\Lille');
467         $container->register('foo', __NAMESPACE__.'\MultipleArgumentsOptionalScalar')
468             ->setAutowired(true)
469             ->setArguments(array('', ''));
470
471         $pass = new AutowirePass();
472         $pass->process($container);
473
474         $this->assertEquals(array(new Reference('a'), '', new Reference('lille')), $container->getDefinition('foo')->getArguments());
475     }
476
477     public function provideAutodiscoveredAutowiringOrder()
478     {
479         return array(
480             array('CannotBeAutowiredForwardOrder'),
481             array('CannotBeAutowiredReverseOrder'),
482         );
483     }
484
485     /**
486      * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
487      * @expectedExceptionMessage Service "a" can use either autowiring or a factory, not both.
488      */
489     public function testWithFactory()
490     {
491         $container = new ContainerBuilder();
492
493         $container->register('a', __NAMESPACE__.'\A')
494             ->setFactory('foo')
495             ->setAutowired(true);
496
497         $pass = new AutowirePass();
498         $pass->process($container);
499     }
500 }
501
502 class Foo
503 {
504 }
505
506 class Bar
507 {
508     public function __construct(Foo $foo)
509     {
510     }
511 }
512
513 class A
514 {
515 }
516
517 class B extends A
518 {
519 }
520
521 class C
522 {
523     public function __construct(A $a)
524     {
525     }
526 }
527
528 interface DInterface
529 {
530 }
531
532 interface EInterface extends DInterface
533 {
534 }
535
536 interface IInterface
537 {
538 }
539
540 class I implements IInterface
541 {
542 }
543
544 class F extends I implements EInterface
545 {
546 }
547
548 class G
549 {
550     public function __construct(DInterface $d, EInterface $e, IInterface $i)
551     {
552     }
553 }
554
555 class H
556 {
557     public function __construct(B $b, DInterface $d)
558     {
559     }
560 }
561
562 interface CollisionInterface
563 {
564 }
565
566 class CollisionA implements CollisionInterface
567 {
568 }
569
570 class CollisionB implements CollisionInterface
571 {
572 }
573
574 class CannotBeAutowired
575 {
576     public function __construct(CollisionInterface $collision)
577     {
578     }
579 }
580
581 class CannotBeAutowiredForwardOrder
582 {
583     public function __construct(CollisionA $a, CollisionInterface $b, CollisionB $c)
584     {
585     }
586 }
587
588 class CannotBeAutowiredReverseOrder
589 {
590     public function __construct(CollisionA $a, CollisionB $c, CollisionInterface $b)
591     {
592     }
593 }
594
595 class Lille
596 {
597 }
598
599 class Dunglas
600 {
601     public function __construct(Lille $l)
602     {
603     }
604 }
605
606 class LesTilleuls
607 {
608     public function __construct(Dunglas $j, Dunglas $k)
609     {
610     }
611 }
612
613 class OptionalParameter
614 {
615     public function __construct(CollisionInterface $c = null, A $a, Foo $f = null)
616     {
617     }
618 }
619
620 class BadTypeHintedArgument
621 {
622     public function __construct(Dunglas $k, NotARealClass $r)
623     {
624     }
625 }
626 class BadParentTypeHintedArgument
627 {
628     public function __construct(Dunglas $k, OptionalServiceClass $r)
629     {
630     }
631 }
632 class NotGuessableArgument
633 {
634     public function __construct(Foo $k)
635     {
636     }
637 }
638 class NotGuessableArgumentForSubclass
639 {
640     public function __construct(A $k)
641     {
642     }
643 }
644 class MultipleArguments
645 {
646     public function __construct(A $k, $foo, Dunglas $dunglas)
647     {
648     }
649 }
650
651 class MultipleArgumentsOptionalScalar
652 {
653     public function __construct(A $a, $foo = 'default_val', Lille $lille = null)
654     {
655     }
656 }
657 class MultipleArgumentsOptionalScalarLast
658 {
659     public function __construct(A $a, Lille $lille, $foo = 'some_val')
660     {
661     }
662 }
663 class MultipleArgumentsOptionalScalarNotReallyOptional
664 {
665     public function __construct(A $a, $foo = 'default_val', Lille $lille)
666     {
667     }
668 }