9cc7d1af71813e7ddffce4934d544db55545e9e7
[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(1, $container->getDefinition('coop_tilleuls')->getArguments());
216         $this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\dunglas', $container->getDefinition('coop_tilleuls')->getArgument(0));
217
218         $dunglasDefinition = $container->getDefinition('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas');
219         $this->assertEquals(__NAMESPACE__.'\Dunglas', $dunglasDefinition->getClass());
220         $this->assertFalse($dunglasDefinition->isPublic());
221         $this->assertCount(1, $dunglasDefinition->getArguments());
222         $this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\lille', $dunglasDefinition->getArgument(0));
223
224         $lilleDefinition = $container->getDefinition('autowired.Symfony\Component\DependencyInjection\Tests\Compiler\Lille');
225         $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass());
226     }
227
228     public function testResolveParameter()
229     {
230         $container = new ContainerBuilder();
231
232         $container->setParameter('class_name', __NAMESPACE__.'\Foo');
233         $container->register('foo', '%class_name%');
234         $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
235         $barDefinition->setAutowired(true);
236
237         $pass = new AutowirePass();
238         $pass->process($container);
239
240         $this->assertEquals('foo', $container->getDefinition('bar')->getArgument(0));
241     }
242
243     public function testOptionalParameter()
244     {
245         $container = new ContainerBuilder();
246
247         $container->register('a', __NAMESPACE__.'\A');
248         $container->register('foo', __NAMESPACE__.'\Foo');
249         $optDefinition = $container->register('opt', __NAMESPACE__.'\OptionalParameter');
250         $optDefinition->setAutowired(true);
251
252         $pass = new AutowirePass();
253         $pass->process($container);
254
255         $definition = $container->getDefinition('opt');
256         $this->assertNull($definition->getArgument(0));
257         $this->assertEquals('a', $definition->getArgument(1));
258         $this->assertEquals('foo', $definition->getArgument(2));
259     }
260
261     public function testDontTriggerAutowiring()
262     {
263         $container = new ContainerBuilder();
264
265         $container->register('foo', __NAMESPACE__.'\Foo');
266         $container->register('bar', __NAMESPACE__.'\Bar');
267
268         $pass = new AutowirePass();
269         $pass->process($container);
270
271         $this->assertCount(0, $container->getDefinition('bar')->getArguments());
272     }
273
274     /**
275      * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
276      * @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).
277      */
278     public function testClassNotFoundThrowsException()
279     {
280         $container = new ContainerBuilder();
281
282         $aDefinition = $container->register('a', __NAMESPACE__.'\BadTypeHintedArgument');
283         $aDefinition->setAutowired(true);
284
285         $pass = new AutowirePass();
286         $pass->process($container);
287     }
288
289     /**
290      * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
291      * @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).
292      */
293     public function testParentClassNotFoundThrowsException()
294     {
295         $container = new ContainerBuilder();
296
297         $aDefinition = $container->register('a', __NAMESPACE__.'\BadParentTypeHintedArgument');
298         $aDefinition->setAutowired(true);
299
300         $pass = new AutowirePass();
301         $pass->process($container);
302     }
303
304     public function testDontUseAbstractServices()
305     {
306         $container = new ContainerBuilder();
307
308         $container->register('abstract_foo', __NAMESPACE__.'\Foo')->setAbstract(true);
309         $container->register('foo', __NAMESPACE__.'\Foo');
310         $container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true);
311
312         $pass = new AutowirePass();
313         $pass->process($container);
314
315         $arguments = $container->getDefinition('bar')->getArguments();
316         $this->assertSame('foo', (string) $arguments[0]);
317     }
318
319     public function testSomeSpecificArgumentsAreSet()
320     {
321         $container = new ContainerBuilder();
322
323         $container->register('foo', __NAMESPACE__.'\Foo');
324         $container->register('a', __NAMESPACE__.'\A');
325         $container->register('dunglas', __NAMESPACE__.'\Dunglas');
326         $container->register('multiple', __NAMESPACE__.'\MultipleArguments')
327             ->setAutowired(true)
328             // set the 2nd (index 1) argument only: autowire the first and third
329             // args are: A, Foo, Dunglas
330             ->setArguments(array(
331                 1 => new Reference('foo'),
332             ));
333
334         $pass = new AutowirePass();
335         $pass->process($container);
336
337         $definition = $container->getDefinition('multiple');
338         $this->assertEquals(
339             array(
340                 new Reference('a'),
341                 new Reference('foo'),
342                 new Reference('dunglas'),
343             ),
344             $definition->getArguments()
345         );
346     }
347
348     /**
349      * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
350      * @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.
351      */
352     public function testScalarArgsCannotBeAutowired()
353     {
354         $container = new ContainerBuilder();
355
356         $container->register('a', __NAMESPACE__.'\A');
357         $container->register('dunglas', __NAMESPACE__.'\Dunglas');
358         $container->register('arg_no_type_hint', __NAMESPACE__.'\MultipleArguments')
359             ->setAutowired(true);
360
361         $pass = new AutowirePass();
362         $pass->process($container);
363
364         $container->getDefinition('arg_no_type_hint');
365     }
366
367     /**
368      * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
369      * @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.
370      */
371     public function testOptionalScalarNotReallyOptionalThrowException()
372     {
373         $container = new ContainerBuilder();
374
375         $container->register('a', __NAMESPACE__.'\A');
376         $container->register('lille', __NAMESPACE__.'\Lille');
377         $container->register('not_really_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalarNotReallyOptional')
378             ->setAutowired(true);
379
380         $pass = new AutowirePass();
381         $pass->process($container);
382     }
383
384     public function testOptionalScalarArgsDontMessUpOrder()
385     {
386         $container = new ContainerBuilder();
387
388         $container->register('a', __NAMESPACE__.'\A');
389         $container->register('lille', __NAMESPACE__.'\Lille');
390         $container->register('with_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalar')
391             ->setAutowired(true);
392
393         $pass = new AutowirePass();
394         $pass->process($container);
395
396         $definition = $container->getDefinition('with_optional_scalar');
397         $this->assertEquals(
398             array(
399                 new Reference('a'),
400                 // use the default value
401                 'default_val',
402                 new Reference('lille'),
403             ),
404             $definition->getArguments()
405         );
406     }
407
408     public function testOptionalScalarArgsNotPassedIfLast()
409     {
410         $container = new ContainerBuilder();
411
412         $container->register('a', __NAMESPACE__.'\A');
413         $container->register('lille', __NAMESPACE__.'\Lille');
414         $container->register('with_optional_scalar_last', __NAMESPACE__.'\MultipleArgumentsOptionalScalarLast')
415             ->setAutowired(true);
416
417         $pass = new AutowirePass();
418         $pass->process($container);
419
420         $definition = $container->getDefinition('with_optional_scalar_last');
421         $this->assertEquals(
422             array(
423                 new Reference('a'),
424                 new Reference('lille'),
425             ),
426             $definition->getArguments()
427         );
428     }
429
430     public function testIgnoreServiceWithClassNotExisting()
431     {
432         $container = new ContainerBuilder();
433
434         $container->register('class_not_exist', __NAMESPACE__.'\OptionalServiceClass');
435
436         $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar');
437         $barDefinition->setAutowired(true);
438
439         $pass = new AutowirePass();
440         $pass->process($container);
441
442         $this->assertTrue($container->hasDefinition('bar'));
443     }
444
445     public function testEmptyStringIsKept()
446     {
447         $container = new ContainerBuilder();
448
449         $container->register('a', __NAMESPACE__.'\A');
450         $container->register('lille', __NAMESPACE__.'\Lille');
451         $container->register('foo', __NAMESPACE__.'\MultipleArgumentsOptionalScalar')
452             ->setAutowired(true)
453             ->setArguments(array('', ''));
454
455         $pass = new AutowirePass();
456         $pass->process($container);
457
458         $this->assertEquals(array(new Reference('a'), '', new Reference('lille')), $container->getDefinition('foo')->getArguments());
459     }
460
461     public function provideAutodiscoveredAutowiringOrder()
462     {
463         return array(
464             array('CannotBeAutowiredForwardOrder'),
465             array('CannotBeAutowiredReverseOrder'),
466         );
467     }
468
469     /**
470      * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
471      * @expectedExceptionMessage Service "a" can use either autowiring or a factory, not both.
472      */
473     public function testWithFactory()
474     {
475         $container = new ContainerBuilder();
476
477         $container->register('a', __NAMESPACE__.'\A')
478             ->setFactory('foo')
479             ->setAutowired(true);
480
481         $pass = new AutowirePass();
482         $pass->process($container);
483     }
484 }
485
486 class Foo
487 {
488 }
489
490 class Bar
491 {
492     public function __construct(Foo $foo)
493     {
494     }
495 }
496
497 class A
498 {
499 }
500
501 class B extends A
502 {
503 }
504
505 class C
506 {
507     public function __construct(A $a)
508     {
509     }
510 }
511
512 interface DInterface
513 {
514 }
515
516 interface EInterface extends DInterface
517 {
518 }
519
520 interface IInterface
521 {
522 }
523
524 class I implements IInterface
525 {
526 }
527
528 class F extends I implements EInterface
529 {
530 }
531
532 class G
533 {
534     public function __construct(DInterface $d, EInterface $e, IInterface $i)
535     {
536     }
537 }
538
539 class H
540 {
541     public function __construct(B $b, DInterface $d)
542     {
543     }
544 }
545
546 interface CollisionInterface
547 {
548 }
549
550 class CollisionA implements CollisionInterface
551 {
552 }
553
554 class CollisionB implements CollisionInterface
555 {
556 }
557
558 class CannotBeAutowired
559 {
560     public function __construct(CollisionInterface $collision)
561     {
562     }
563 }
564
565 class CannotBeAutowiredForwardOrder
566 {
567     public function __construct(CollisionA $a, CollisionInterface $b, CollisionB $c)
568     {
569     }
570 }
571
572 class CannotBeAutowiredReverseOrder
573 {
574     public function __construct(CollisionA $a, CollisionB $c, CollisionInterface $b)
575     {
576     }
577 }
578
579 class Lille
580 {
581 }
582
583 class Dunglas
584 {
585     public function __construct(Lille $l)
586     {
587     }
588 }
589
590 class LesTilleuls
591 {
592     public function __construct(Dunglas $k)
593     {
594     }
595 }
596
597 class OptionalParameter
598 {
599     public function __construct(CollisionInterface $c = null, A $a, Foo $f = null)
600     {
601     }
602 }
603
604 class BadTypeHintedArgument
605 {
606     public function __construct(Dunglas $k, NotARealClass $r)
607     {
608     }
609 }
610 class BadParentTypeHintedArgument
611 {
612     public function __construct(Dunglas $k, OptionalServiceClass $r)
613     {
614     }
615 }
616 class NotGuessableArgument
617 {
618     public function __construct(Foo $k)
619     {
620     }
621 }
622 class NotGuessableArgumentForSubclass
623 {
624     public function __construct(A $k)
625     {
626     }
627 }
628 class MultipleArguments
629 {
630     public function __construct(A $k, $foo, Dunglas $dunglas)
631     {
632     }
633 }
634
635 class MultipleArgumentsOptionalScalar
636 {
637     public function __construct(A $a, $foo = 'default_val', Lille $lille = null)
638     {
639     }
640 }
641 class MultipleArgumentsOptionalScalarLast
642 {
643     public function __construct(A $a, Lille $lille, $foo = 'some_val')
644     {
645     }
646 }
647 class MultipleArgumentsOptionalScalarNotReallyOptional
648 {
649     public function __construct(A $a, $foo = 'default_val', Lille $lille)
650     {
651     }
652 }