56046e735b998ee0e3fa9b2da688f685c71e8671
[yaffs-website] / vendor / symfony / dependency-injection / Tests / Dumper / PhpDumperTest.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\Dumper;
13
14 use DummyProxyDumper;
15 use PHPUnit\Framework\TestCase;
16 use Psr\Container\ContainerInterface;
17 use Symfony\Component\Config\FileLocator;
18 use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
19 use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
20 use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
21 use Symfony\Component\DependencyInjection\ChildDefinition;
22 use Symfony\Component\DependencyInjection\ContainerBuilder;
23 use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;
24 use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
25 use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
26 use Symfony\Component\DependencyInjection\Parameter;
27 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
28 use Symfony\Component\DependencyInjection\Reference;
29 use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator;
30 use Symfony\Component\DependencyInjection\TypedReference;
31 use Symfony\Component\DependencyInjection\Definition;
32 use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
33 use Symfony\Component\DependencyInjection\ServiceLocator;
34 use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
35 use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber;
36 use Symfony\Component\DependencyInjection\Variable;
37 use Symfony\Component\ExpressionLanguage\Expression;
38
39 require_once __DIR__.'/../Fixtures/includes/classes.php';
40
41 class PhpDumperTest extends TestCase
42 {
43     protected static $fixturesPath;
44
45     public static function setUpBeforeClass()
46     {
47         self::$fixturesPath = realpath(__DIR__.'/../Fixtures/');
48     }
49
50     public function testDump()
51     {
52         $container = new ContainerBuilder();
53         $container->compile();
54         $dumper = new PhpDumper($container);
55
56         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services1.php', $dumper->dump(), '->dump() dumps an empty container as an empty PHP class');
57         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services1-1.php', $dumper->dump(array('class' => 'Container', 'base_class' => 'AbstractContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Dump')), '->dump() takes a class and a base_class options');
58     }
59
60     public function testDumpOptimizationString()
61     {
62         $definition = new Definition();
63         $definition->setClass('stdClass');
64         $definition->addArgument(array(
65             'only dot' => '.',
66             'concatenation as value' => '.\'\'.',
67             'concatenation from the start value' => '\'\'.',
68             '.' => 'dot as a key',
69             '.\'\'.' => 'concatenation as a key',
70             '\'\'.' => 'concatenation from the start key',
71             'optimize concatenation' => 'string1%some_string%string2',
72             'optimize concatenation with empty string' => 'string1%empty_value%string2',
73             'optimize concatenation from the start' => '%empty_value%start',
74             'optimize concatenation at the end' => 'end%empty_value%',
75             'new line' => "string with \nnew line",
76         ));
77         $definition->setPublic(true);
78
79         $container = new ContainerBuilder();
80         $container->setResourceTracking(false);
81         $container->setDefinition('test', $definition);
82         $container->setParameter('empty_value', '');
83         $container->setParameter('some_string', '-');
84         $container->compile();
85
86         $dumper = new PhpDumper($container);
87         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services10.php', $dumper->dump(), '->dump() dumps an empty container as an empty PHP class');
88     }
89
90     public function testDumpRelativeDir()
91     {
92         $definition = new Definition();
93         $definition->setClass('stdClass');
94         $definition->addArgument('%foo%');
95         $definition->addArgument(array('%foo%' => '%buz%/'));
96         $definition->setPublic(true);
97
98         $container = new ContainerBuilder();
99         $container->setDefinition('test', $definition);
100         $container->setParameter('foo', 'wiz'.dirname(__DIR__));
101         $container->setParameter('bar', __DIR__);
102         $container->setParameter('baz', '%bar%/PhpDumperTest.php');
103         $container->setParameter('buz', dirname(dirname(__DIR__)));
104         $container->compile();
105
106         $dumper = new PhpDumper($container);
107         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services12.php', $dumper->dump(array('file' => __FILE__)), '->dump() dumps __DIR__ relative strings');
108     }
109
110     public function testDumpCustomContainerClassWithoutConstructor()
111     {
112         $container = new ContainerBuilder();
113         $container->compile();
114
115         $dumper = new PhpDumper($container);
116
117         $this->assertStringEqualsFile(self::$fixturesPath.'/php/custom_container_class_without_constructor.php', $dumper->dump(array('base_class' => 'NoConstructorContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\Container')));
118     }
119
120     public function testDumpCustomContainerClassConstructorWithoutArguments()
121     {
122         $container = new ContainerBuilder();
123         $container->compile();
124
125         $dumper = new PhpDumper($container);
126
127         $this->assertStringEqualsFile(self::$fixturesPath.'/php/custom_container_class_constructor_without_arguments.php', $dumper->dump(array('base_class' => 'ConstructorWithoutArgumentsContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\Container')));
128     }
129
130     public function testDumpCustomContainerClassWithOptionalArgumentLessConstructor()
131     {
132         $container = new ContainerBuilder();
133         $container->compile();
134
135         $dumper = new PhpDumper($container);
136
137         $this->assertStringEqualsFile(self::$fixturesPath.'/php/custom_container_class_with_optional_constructor_arguments.php', $dumper->dump(array('base_class' => 'ConstructorWithOptionalArgumentsContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\Container')));
138     }
139
140     public function testDumpCustomContainerClassWithMandatoryArgumentLessConstructor()
141     {
142         $container = new ContainerBuilder();
143         $container->compile();
144
145         $dumper = new PhpDumper($container);
146
147         $this->assertStringEqualsFile(self::$fixturesPath.'/php/custom_container_class_with_mandatory_constructor_arguments.php', $dumper->dump(array('base_class' => 'ConstructorWithMandatoryArgumentsContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\Container')));
148     }
149
150     /**
151      * @dataProvider provideInvalidParameters
152      * @expectedException \InvalidArgumentException
153      */
154     public function testExportParameters($parameters)
155     {
156         $container = new ContainerBuilder(new ParameterBag($parameters));
157         $container->compile();
158         $dumper = new PhpDumper($container);
159         $dumper->dump();
160     }
161
162     public function provideInvalidParameters()
163     {
164         return array(
165             array(array('foo' => new Definition('stdClass'))),
166             array(array('foo' => new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'))),
167             array(array('foo' => new Reference('foo'))),
168             array(array('foo' => new Variable('foo'))),
169         );
170     }
171
172     public function testAddParameters()
173     {
174         $container = include self::$fixturesPath.'/containers/container8.php';
175         $container->compile();
176         $dumper = new PhpDumper($container);
177         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services8.php', $dumper->dump(), '->dump() dumps parameters');
178     }
179
180     /**
181      * @group legacy
182      * @expectedDeprecation Dumping an uncompiled ContainerBuilder is deprecated since Symfony 3.3 and will not be supported anymore in 4.0. Compile the container beforehand.
183      */
184     public function testAddServiceWithoutCompilation()
185     {
186         $container = include self::$fixturesPath.'/containers/container9.php';
187         $dumper = new PhpDumper($container);
188         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services9.php', str_replace(str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), '%path%', $dumper->dump()), '->dump() dumps services');
189     }
190
191     public function testAddService()
192     {
193         $container = include self::$fixturesPath.'/containers/container9.php';
194         $container->compile();
195         $dumper = new PhpDumper($container);
196         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services9_compiled.php', str_replace(str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), '%path%', $dumper->dump()), '->dump() dumps services');
197
198         $container = new ContainerBuilder();
199         $container->register('foo', 'FooClass')->addArgument(new \stdClass())->setPublic(true);
200         $container->compile();
201         $dumper = new PhpDumper($container);
202         try {
203             $dumper->dump();
204             $this->fail('->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources');
205         } catch (\Exception $e) {
206             $this->assertInstanceOf('\Symfony\Component\DependencyInjection\Exception\RuntimeException', $e, '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources');
207             $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources');
208         }
209     }
210
211     public function testDumpAsFiles()
212     {
213         $container = include self::$fixturesPath.'/containers/container9.php';
214         $container->getDefinition('bar')->addTag('hot');
215         $container->compile();
216         $dumper = new PhpDumper($container);
217         $dump = print_r($dumper->dump(array('as_files' => true, 'file' => __DIR__, 'hot_path_tag' => 'hot')), true);
218         if ('\\' === DIRECTORY_SEPARATOR) {
219             $dump = str_replace('\\\\Fixtures\\\\includes\\\\foo.php', '/Fixtures/includes/foo.php', $dump);
220         }
221         $this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services9_as_files.txt', $dump);
222     }
223
224     public function testServicesWithAnonymousFactories()
225     {
226         $container = include self::$fixturesPath.'/containers/container19.php';
227         $container->compile();
228         $dumper = new PhpDumper($container);
229
230         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services19.php', $dumper->dump(), '->dump() dumps services with anonymous factories');
231     }
232
233     public function testAddServiceIdWithUnsupportedCharacters()
234     {
235         $class = 'Symfony_DI_PhpDumper_Test_Unsupported_Characters';
236         $container = new ContainerBuilder();
237         $container->register('bar$', 'FooClass')->setPublic(true);
238         $container->register('bar$!', 'FooClass')->setPublic(true);
239         $container->compile();
240         $dumper = new PhpDumper($container);
241         eval('?>'.$dumper->dump(array('class' => $class)));
242
243         $this->assertTrue(method_exists($class, 'getBarService'));
244         $this->assertTrue(method_exists($class, 'getBar2Service'));
245     }
246
247     public function testConflictingServiceIds()
248     {
249         $class = 'Symfony_DI_PhpDumper_Test_Conflicting_Service_Ids';
250         $container = new ContainerBuilder();
251         $container->register('foo_bar', 'FooClass')->setPublic(true);
252         $container->register('foobar', 'FooClass')->setPublic(true);
253         $container->compile();
254         $dumper = new PhpDumper($container);
255         eval('?>'.$dumper->dump(array('class' => $class)));
256
257         $this->assertTrue(method_exists($class, 'getFooBarService'));
258         $this->assertTrue(method_exists($class, 'getFoobar2Service'));
259     }
260
261     public function testConflictingMethodsWithParent()
262     {
263         $class = 'Symfony_DI_PhpDumper_Test_Conflicting_Method_With_Parent';
264         $container = new ContainerBuilder();
265         $container->register('bar', 'FooClass')->setPublic(true);
266         $container->register('foo_bar', 'FooClass')->setPublic(true);
267         $container->compile();
268         $dumper = new PhpDumper($container);
269         eval('?>'.$dumper->dump(array(
270             'class' => $class,
271             'base_class' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\containers\CustomContainer',
272         )));
273
274         $this->assertTrue(method_exists($class, 'getBar2Service'));
275         $this->assertTrue(method_exists($class, 'getFoobar2Service'));
276     }
277
278     /**
279      * @dataProvider provideInvalidFactories
280      * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
281      * @expectedExceptionMessage Cannot dump definition
282      */
283     public function testInvalidFactories($factory)
284     {
285         $container = new ContainerBuilder();
286         $def = new Definition('stdClass');
287         $def->setPublic(true);
288         $def->setFactory($factory);
289         $container->setDefinition('bar', $def);
290         $container->compile();
291         $dumper = new PhpDumper($container);
292         $dumper->dump();
293     }
294
295     public function provideInvalidFactories()
296     {
297         return array(
298             array(array('', 'method')),
299             array(array('class', '')),
300             array(array('...', 'method')),
301             array(array('class', '...')),
302         );
303     }
304
305     public function testAliases()
306     {
307         $container = include self::$fixturesPath.'/containers/container9.php';
308         $container->setParameter('foo_bar', 'foo_bar');
309         $container->compile();
310         $dumper = new PhpDumper($container);
311         eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Aliases')));
312
313         $container = new \Symfony_DI_PhpDumper_Test_Aliases();
314         $foo = $container->get('foo');
315         $this->assertSame($foo, $container->get('alias_for_foo'));
316         $this->assertSame($foo, $container->get('alias_for_alias'));
317     }
318
319     public function testFrozenContainerWithoutAliases()
320     {
321         $container = new ContainerBuilder();
322         $container->compile();
323
324         $dumper = new PhpDumper($container);
325         eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Frozen_No_Aliases')));
326
327         $container = new \Symfony_DI_PhpDumper_Test_Frozen_No_Aliases();
328         $this->assertFalse($container->has('foo'));
329     }
330
331     /**
332      * @group legacy
333      * @expectedDeprecation The "decorator_service" service is already initialized, replacing it is deprecated since Symfony 3.3 and will fail in 4.0.
334      */
335     public function testOverrideServiceWhenUsingADumpedContainer()
336     {
337         require_once self::$fixturesPath.'/php/services9_compiled.php';
338
339         $container = new \ProjectServiceContainer();
340         $container->get('decorator_service');
341         $container->set('decorator_service', $decorator = new \stdClass());
342
343         $this->assertSame($decorator, $container->get('decorator_service'), '->set() overrides an already defined service');
344     }
345
346     public function testDumpAutowireData()
347     {
348         $container = include self::$fixturesPath.'/containers/container24.php';
349         $container->compile();
350         $dumper = new PhpDumper($container);
351
352         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services24.php', $dumper->dump());
353     }
354
355     public function testEnvInId()
356     {
357         $container = include self::$fixturesPath.'/containers/container_env_in_id.php';
358         $container->compile();
359         $dumper = new PhpDumper($container);
360
361         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_env_in_id.php', $dumper->dump());
362     }
363
364     public function testEnvParameter()
365     {
366         $rand = mt_rand();
367         putenv('Baz='.$rand);
368         $container = new ContainerBuilder();
369         $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
370         $loader->load('services26.yml');
371         $container->setParameter('env(json_file)', self::$fixturesPath.'/array.json');
372         $container->compile();
373         $dumper = new PhpDumper($container);
374
375         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services26.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_EnvParameters', 'file' => self::$fixturesPath.'/php/services26.php')));
376
377         require self::$fixturesPath.'/php/services26.php';
378         $container = new \Symfony_DI_PhpDumper_Test_EnvParameters();
379         $this->assertSame($rand, $container->getParameter('baz'));
380         $this->assertSame(array(123, 'abc'), $container->getParameter('json'));
381         $this->assertSame('sqlite:///foo/bar/var/data.db', $container->getParameter('db_dsn'));
382         putenv('Baz');
383     }
384
385     public function testResolvedBase64EnvParameters()
386     {
387         $container = new ContainerBuilder();
388         $container->setParameter('env(foo)', base64_encode('world'));
389         $container->setParameter('hello', '%env(base64:foo)%');
390         $container->compile(true);
391
392         $expected = array(
393           'env(foo)' => 'd29ybGQ=',
394           'hello' => 'world',
395         );
396         $this->assertSame($expected, $container->getParameterBag()->all());
397     }
398
399     public function testDumpedBase64EnvParameters()
400     {
401         $container = new ContainerBuilder();
402         $container->setParameter('env(foo)', base64_encode('world'));
403         $container->setParameter('hello', '%env(base64:foo)%');
404         $container->compile();
405
406         $dumper = new PhpDumper($container);
407         $dumper->dump();
408
409         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_base64_env.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Base64Parameters')));
410
411         require self::$fixturesPath.'/php/services_base64_env.php';
412         $container = new \Symfony_DI_PhpDumper_Test_Base64Parameters();
413         $this->assertSame('world', $container->getParameter('hello'));
414     }
415
416     public function testCustomEnvParameters()
417     {
418         $container = new ContainerBuilder();
419         $container->setParameter('env(foo)', str_rot13('world'));
420         $container->setParameter('hello', '%env(rot13:foo)%');
421         $container->register(Rot13EnvVarProcessor::class)->addTag('container.env_var_processor')->setPublic(true);
422         $container->compile();
423
424         $dumper = new PhpDumper($container);
425         $dumper->dump();
426
427         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_rot13_env.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Rot13Parameters')));
428
429         require self::$fixturesPath.'/php/services_rot13_env.php';
430         $container = new \Symfony_DI_PhpDumper_Test_Rot13Parameters();
431         $this->assertSame('world', $container->getParameter('hello'));
432     }
433
434     public function testFileEnvProcessor()
435     {
436         $container = new ContainerBuilder();
437         $container->setParameter('env(foo)', __FILE__);
438         $container->setParameter('random', '%env(file:foo)%');
439         $container->compile(true);
440
441         $this->assertStringEqualsFile(__FILE__, $container->getParameter('random'));
442     }
443
444     /**
445      * @expectedException \Symfony\Component\DependencyInjection\Exception\EnvParameterException
446      * @expectedExceptionMessage Environment variables "FOO" are never used. Please, check your container's configuration.
447      */
448     public function testUnusedEnvParameter()
449     {
450         $container = new ContainerBuilder();
451         $container->getParameter('env(FOO)');
452         $container->compile();
453         $dumper = new PhpDumper($container);
454         $dumper->dump();
455     }
456
457     /**
458      * @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException
459      * @expectedExceptionMessage Circular reference detected for parameter "env(resolve:DUMMY_ENV_VAR)" ("env(resolve:DUMMY_ENV_VAR)" > "env(resolve:DUMMY_ENV_VAR)").
460      */
461     public function testCircularDynamicEnv()
462     {
463         $container = new ContainerBuilder();
464         $container->setParameter('foo', '%bar%');
465         $container->setParameter('bar', '%env(resolve:DUMMY_ENV_VAR)%');
466         $container->compile();
467
468         $dumper = new PhpDumper($container);
469         $dump = $dumper->dump(array('class' => $class = __FUNCTION__));
470
471         eval('?>'.$dump);
472         $container = new $class();
473
474         putenv('DUMMY_ENV_VAR=%foo%');
475         try {
476             $container->getParameter('bar');
477         } finally {
478             putenv('DUMMY_ENV_VAR');
479         }
480     }
481
482     public function testInlinedDefinitionReferencingServiceContainer()
483     {
484         $container = new ContainerBuilder();
485         $container->register('foo', 'stdClass')->addMethodCall('add', array(new Reference('service_container')))->setPublic(false);
486         $container->register('bar', 'stdClass')->addArgument(new Reference('foo'))->setPublic(true);
487         $container->compile();
488
489         $dumper = new PhpDumper($container);
490         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services13.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container');
491     }
492
493     public function testInitializePropertiesBeforeMethodCalls()
494     {
495         require_once self::$fixturesPath.'/includes/classes.php';
496
497         $container = new ContainerBuilder();
498         $container->register('foo', 'stdClass')->setPublic(true);
499         $container->register('bar', 'MethodCallClass')
500             ->setPublic(true)
501             ->setProperty('simple', 'bar')
502             ->setProperty('complex', new Reference('foo'))
503             ->addMethodCall('callMe');
504         $container->compile();
505
506         $dumper = new PhpDumper($container);
507         eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls')));
508
509         $container = new \Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls();
510         $this->assertTrue($container->get('bar')->callPassed(), '->dump() initializes properties before method calls');
511     }
512
513     public function testCircularReferenceAllowanceForLazyServices()
514     {
515         $container = new ContainerBuilder();
516         $container->register('foo', 'stdClass')->addArgument(new Reference('bar'))->setPublic(true);
517         $container->register('bar', 'stdClass')->setLazy(true)->addArgument(new Reference('foo'))->setPublic(true);
518         $container->compile();
519
520         $dumper = new PhpDumper($container);
521         $dumper->dump();
522
523         $this->addToAssertionCount(1);
524     }
525
526     public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServices()
527     {
528         /*
529          *   test graph:
530          *              [connection] -> [event_manager] --> [entity_manager](lazy)
531          *                                                           |
532          *                                                           --(call)- addEventListener ("@lazy_service")
533          *
534          *              [lazy_service](lazy) -> [entity_manager](lazy)
535          *
536          */
537
538         $container = new ContainerBuilder();
539
540         $eventManagerDefinition = new Definition('stdClass');
541
542         $connectionDefinition = $container->register('connection', 'stdClass')->setPublic(true);
543         $connectionDefinition->addArgument($eventManagerDefinition);
544
545         $container->register('entity_manager', 'stdClass')
546             ->setPublic(true)
547             ->setLazy(true)
548             ->addArgument(new Reference('connection'));
549
550         $lazyServiceDefinition = $container->register('lazy_service', 'stdClass');
551         $lazyServiceDefinition->setPublic(true);
552         $lazyServiceDefinition->setLazy(true);
553         $lazyServiceDefinition->addArgument(new Reference('entity_manager'));
554
555         $eventManagerDefinition->addMethodCall('addEventListener', array(new Reference('lazy_service')));
556
557         $container->compile();
558
559         $dumper = new PhpDumper($container);
560
561         $dumper->setProxyDumper(new DummyProxyDumper());
562         $dumper->dump();
563
564         $this->addToAssertionCount(1);
565     }
566
567     public function testLazyArgumentProvideGenerator()
568     {
569         require_once self::$fixturesPath.'/includes/classes.php';
570
571         $container = new ContainerBuilder();
572         $container->register('lazy_referenced', 'stdClass')->setPublic(true);
573         $container
574             ->register('lazy_context', 'LazyContext')
575             ->setPublic(true)
576             ->setArguments(array(
577                 new IteratorArgument(array('k1' => new Reference('lazy_referenced'), 'k2' => new Reference('service_container'))),
578                 new IteratorArgument(array()),
579             ))
580         ;
581         $container->compile();
582
583         $dumper = new PhpDumper($container);
584         eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator')));
585
586         $container = new \Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator();
587         $lazyContext = $container->get('lazy_context');
588
589         $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyValues);
590         $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyEmptyValues);
591         $this->assertCount(2, $lazyContext->lazyValues);
592         $this->assertCount(0, $lazyContext->lazyEmptyValues);
593
594         $i = -1;
595         foreach ($lazyContext->lazyValues as $k => $v) {
596             switch (++$i) {
597                 case 0:
598                     $this->assertEquals('k1', $k);
599                     $this->assertInstanceOf('stdCLass', $v);
600                     break;
601                 case 1:
602                     $this->assertEquals('k2', $k);
603                     $this->assertInstanceOf('Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator', $v);
604                     break;
605             }
606         }
607
608         $this->assertEmpty(iterator_to_array($lazyContext->lazyEmptyValues));
609     }
610
611     public function testNormalizedId()
612     {
613         $container = include self::$fixturesPath.'/containers/container33.php';
614         $container->compile();
615         $dumper = new PhpDumper($container);
616
617         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services33.php', $dumper->dump());
618     }
619
620     public function testDumpContainerBuilderWithFrozenConstructorIncludingPrivateServices()
621     {
622         $container = new ContainerBuilder();
623         $container->register('foo_service', 'stdClass')->setArguments(array(new Reference('baz_service')))->setPublic(true);
624         $container->register('bar_service', 'stdClass')->setArguments(array(new Reference('baz_service')))->setPublic(true);
625         $container->register('baz_service', 'stdClass')->setPublic(false);
626         $container->compile();
627
628         $dumper = new PhpDumper($container);
629
630         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_private_frozen.php', $dumper->dump());
631     }
632
633     public function testServiceLocator()
634     {
635         $container = new ContainerBuilder();
636         $container->register('foo_service', ServiceLocator::class)
637             ->setPublic(true)
638             ->addArgument(array(
639                 'bar' => new ServiceClosureArgument(new Reference('bar_service')),
640                 'baz' => new ServiceClosureArgument(new TypedReference('baz_service', 'stdClass')),
641                 'nil' => $nil = new ServiceClosureArgument(new Reference('nil')),
642             ))
643         ;
644
645         // no method calls
646         $container->register('translator.loader_1', 'stdClass')->setPublic(true);
647         $container->register('translator.loader_1_locator', ServiceLocator::class)
648             ->setPublic(false)
649             ->addArgument(array(
650                 'translator.loader_1' => new ServiceClosureArgument(new Reference('translator.loader_1')),
651             ));
652         $container->register('translator_1', StubbedTranslator::class)
653             ->setPublic(true)
654             ->addArgument(new Reference('translator.loader_1_locator'));
655
656         // one method calls
657         $container->register('translator.loader_2', 'stdClass')->setPublic(true);
658         $container->register('translator.loader_2_locator', ServiceLocator::class)
659             ->setPublic(false)
660             ->addArgument(array(
661                 'translator.loader_2' => new ServiceClosureArgument(new Reference('translator.loader_2')),
662             ));
663         $container->register('translator_2', StubbedTranslator::class)
664             ->setPublic(true)
665             ->addArgument(new Reference('translator.loader_2_locator'))
666             ->addMethodCall('addResource', array('db', new Reference('translator.loader_2'), 'nl'));
667
668         // two method calls
669         $container->register('translator.loader_3', 'stdClass')->setPublic(true);
670         $container->register('translator.loader_3_locator', ServiceLocator::class)
671             ->setPublic(false)
672             ->addArgument(array(
673                 'translator.loader_3' => new ServiceClosureArgument(new Reference('translator.loader_3')),
674             ));
675         $container->register('translator_3', StubbedTranslator::class)
676             ->setPublic(true)
677             ->addArgument(new Reference('translator.loader_3_locator'))
678             ->addMethodCall('addResource', array('db', new Reference('translator.loader_3'), 'nl'))
679             ->addMethodCall('addResource', array('db', new Reference('translator.loader_3'), 'en'));
680
681         $nil->setValues(array(null));
682         $container->register('bar_service', 'stdClass')->setArguments(array(new Reference('baz_service')))->setPublic(true);
683         $container->register('baz_service', 'stdClass')->setPublic(false);
684         $container->compile();
685
686         $dumper = new PhpDumper($container);
687
688         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_locator.php', $dumper->dump());
689     }
690
691     public function testServiceSubscriber()
692     {
693         $container = new ContainerBuilder();
694         $container->register('foo_service', TestServiceSubscriber::class)
695             ->setPublic(true)
696             ->setAutowired(true)
697             ->addArgument(new Reference(ContainerInterface::class))
698             ->addTag('container.service_subscriber', array(
699                 'key' => 'bar',
700                 'id' => TestServiceSubscriber::class,
701             ))
702         ;
703         $container->register(TestServiceSubscriber::class, TestServiceSubscriber::class)->setPublic(true);
704
705         $container->register(CustomDefinition::class, CustomDefinition::class)
706             ->setPublic(false);
707         $container->compile();
708
709         $dumper = new PhpDumper($container);
710
711         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_subscriber.php', $dumper->dump());
712     }
713
714     public function testPrivateWithIgnoreOnInvalidReference()
715     {
716         require_once self::$fixturesPath.'/includes/classes.php';
717
718         $container = new ContainerBuilder();
719         $container->register('not_invalid', 'BazClass')
720             ->setPublic(false);
721         $container->register('bar', 'BarClass')
722             ->setPublic(true)
723             ->addMethodCall('setBaz', array(new Reference('not_invalid', SymfonyContainerInterface::IGNORE_ON_INVALID_REFERENCE)));
724         $container->compile();
725
726         $dumper = new PhpDumper($container);
727         eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Private_With_Ignore_On_Invalid_Reference')));
728
729         $container = new \Symfony_DI_PhpDumper_Test_Private_With_Ignore_On_Invalid_Reference();
730         $this->assertInstanceOf('BazClass', $container->get('bar')->getBaz());
731     }
732
733     public function testArrayParameters()
734     {
735         $container = new ContainerBuilder();
736         $container->setParameter('array_1', array(123));
737         $container->setParameter('array_2', array(__DIR__));
738         $container->register('bar', 'BarClass')
739             ->setPublic(true)
740             ->addMethodCall('setBaz', array('%array_1%', '%array_2%', '%%array_1%%', array(123)));
741         $container->compile();
742
743         $dumper = new PhpDumper($container);
744
745         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_array_params.php', str_replace('\\\\Dumper', '/Dumper', $dumper->dump(array('file' => self::$fixturesPath.'/php/services_array_params.php'))));
746     }
747
748     public function testExpressionReferencingPrivateService()
749     {
750         $container = new ContainerBuilder();
751         $container->register('private_bar', 'stdClass')
752             ->setPublic(false);
753         $container->register('private_foo', 'stdClass')
754             ->setPublic(false);
755         $container->register('public_foo', 'stdClass')
756             ->setPublic(true)
757             ->addArgument(new Expression('service("private_foo")'));
758
759         $container->compile();
760         $dumper = new PhpDumper($container);
761
762         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_private_in_expression.php', $dumper->dump());
763     }
764
765     public function testUninitializedReference()
766     {
767         $container = include self::$fixturesPath.'/containers/container_uninitialized_ref.php';
768         $container->compile();
769         $dumper = new PhpDumper($container);
770
771         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_uninitialized_ref.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Uninitialized_Reference')));
772
773         require self::$fixturesPath.'/php/services_uninitialized_ref.php';
774
775         $container = new \Symfony_DI_PhpDumper_Test_Uninitialized_Reference();
776
777         $bar = $container->get('bar');
778
779         $this->assertNull($bar->foo1);
780         $this->assertNull($bar->foo2);
781         $this->assertNull($bar->foo3);
782         $this->assertNull($bar->closures[0]());
783         $this->assertNull($bar->closures[1]());
784         $this->assertNull($bar->closures[2]());
785         $this->assertSame(array(), iterator_to_array($bar->iter));
786
787         $container = new \Symfony_DI_PhpDumper_Test_Uninitialized_Reference();
788
789         $container->get('foo1');
790         $container->get('baz');
791
792         $bar = $container->get('bar');
793
794         $this->assertEquals(new \stdClass(), $bar->foo1);
795         $this->assertNull($bar->foo2);
796         $this->assertEquals(new \stdClass(), $bar->foo3);
797         $this->assertEquals(new \stdClass(), $bar->closures[0]());
798         $this->assertNull($bar->closures[1]());
799         $this->assertEquals(new \stdClass(), $bar->closures[2]());
800         $this->assertEquals(array('foo1' => new \stdClass(), 'foo3' => new \stdClass()), iterator_to_array($bar->iter));
801     }
802
803     /**
804      * @dataProvider provideAlmostCircular
805      */
806     public function testAlmostCircular($visibility)
807     {
808         $container = include self::$fixturesPath.'/containers/container_almost_circular.php';
809         $container->compile();
810         $dumper = new PhpDumper($container);
811
812         $container = 'Symfony_DI_PhpDumper_Test_Almost_Circular_'.ucfirst($visibility);
813         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_almost_circular_'.$visibility.'.php', $dumper->dump(array('class' => $container)));
814
815         require self::$fixturesPath.'/php/services_almost_circular_'.$visibility.'.php';
816
817         $container = new $container();
818
819         $foo = $container->get('foo');
820         $this->assertSame($foo, $foo->bar->foobar->foo);
821
822         $foo2 = $container->get('foo2');
823         $this->assertSame($foo2, $foo2->bar->foobar->foo);
824
825         $this->assertSame(array(), (array) $container->get('foobar4'));
826
827         $foo5 = $container->get('foo5');
828         $this->assertSame($foo5, $foo5->bar->foo);
829     }
830
831     public function provideAlmostCircular()
832     {
833         yield array('public');
834         yield array('private');
835     }
836
837     public function testHotPathOptimizations()
838     {
839         $container = include self::$fixturesPath.'/containers/container_inline_requires.php';
840         $container->setParameter('inline_requires', true);
841         $container->compile();
842         $dumper = new PhpDumper($container);
843
844         $dump = $dumper->dump(array('hot_path_tag' => 'container.hot_path', 'inline_class_loader_parameter' => 'inline_requires', 'file' => self::$fixturesPath.'/php/services_inline_requires.php'));
845         if ('\\' === DIRECTORY_SEPARATOR) {
846             $dump = str_replace("'\\\\includes\\\\HotPath\\\\", "'/includes/HotPath/", $dump);
847         }
848
849         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_inline_requires.php', $dump);
850     }
851
852     public function testDumpHandlesLiteralClassWithRootNamespace()
853     {
854         $container = new ContainerBuilder();
855         $container->register('foo', '\\stdClass')->setPublic(true);
856         $container->compile();
857
858         $dumper = new PhpDumper($container);
859         eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Literal_Class_With_Root_Namespace')));
860
861         $container = new \Symfony_DI_PhpDumper_Test_Literal_Class_With_Root_Namespace();
862
863         $this->assertInstanceOf('stdClass', $container->get('foo'));
864     }
865
866     public function testDumpHandlesObjectClassNames()
867     {
868         $container = new ContainerBuilder(new ParameterBag(array(
869             'class' => 'stdClass',
870         )));
871
872         $container->setDefinition('foo', new Definition(new Parameter('class')));
873         $container->setDefinition('bar', new Definition('stdClass', array(
874             new Reference('foo'),
875         )))->setPublic(true);
876
877         $container->setParameter('inline_requires', true);
878         $container->compile();
879
880         $dumper = new PhpDumper($container);
881         eval('?>'.$dumper->dump(array(
882             'class' => 'Symfony_DI_PhpDumper_Test_Object_Class_Name',
883             'inline_class_loader_parameter' => 'inline_requires',
884         )));
885
886         $container = new \Symfony_DI_PhpDumper_Test_Object_Class_Name();
887
888         $this->assertInstanceOf('stdClass', $container->get('bar'));
889     }
890
891     /**
892      * @group legacy
893      * @expectedDeprecation The "private" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.
894      * @expectedDeprecation The "private_alias" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.
895      * @expectedDeprecation The "decorated_private" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.
896      * @expectedDeprecation The "decorated_private_alias" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.
897      * @expectedDeprecation The "private_not_inlined" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.
898      * @expectedDeprecation The "private_not_removed" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.
899      * @expectedDeprecation The "private_child" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.
900      * @expectedDeprecation The "private_parent" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.
901      */
902     public function testLegacyPrivateServices()
903     {
904         $container = new ContainerBuilder();
905         $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
906         $loader->load('services_legacy_privates.yml');
907
908         $container->setDefinition('private_child', new ChildDefinition('foo'));
909         $container->setDefinition('private_parent', new ChildDefinition('private'));
910
911         $container->getDefinition('private')->setPrivate(true);
912         $container->getDefinition('private_not_inlined')->setPrivate(true);
913         $container->getDefinition('private_not_removed')->setPrivate(true);
914         $container->getDefinition('decorated_private')->setPrivate(true);
915         $container->getDefinition('private_child')->setPrivate(true);
916         $container->getAlias('decorated_private_alias')->setPrivate(true);
917         $container->getAlias('private_alias')->setPrivate(true);
918
919         $container->compile();
920         $dumper = new PhpDumper($container);
921
922         $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_legacy_privates.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Legacy_Privates', 'file' => self::$fixturesPath.'/php/services_legacy_privates.php')));
923
924         require self::$fixturesPath.'/php/services_legacy_privates.php';
925
926         $container = new \Symfony_DI_PhpDumper_Test_Legacy_Privates();
927
928         $container->get('private');
929         $container->get('private_alias');
930         $container->get('alias_to_private');
931         $container->get('decorated_private');
932         $container->get('decorated_private_alias');
933         $container->get('private_not_inlined');
934         $container->get('private_not_removed');
935         $container->get('private_child');
936         $container->get('private_parent');
937         $container->get('public_child');
938     }
939
940     /**
941      * This test checks the trigger of a deprecation note and should not be removed in major releases.
942      *
943      * @group legacy
944      * @expectedDeprecation The "foo" service is deprecated. You should stop using it, as it will soon be removed.
945      */
946     public function testPrivateServiceTriggersDeprecation()
947     {
948         $container = new ContainerBuilder();
949         $container->register('foo', 'stdClass')
950             ->setPublic(false)
951             ->setDeprecated(true);
952         $container->register('bar', 'stdClass')
953             ->setPublic(true)
954             ->setProperty('foo', new Reference('foo'));
955
956         $container->compile();
957
958         $dumper = new PhpDumper($container);
959         eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Private_Service_Triggers_Deprecation')));
960
961         $container = new \Symfony_DI_PhpDumper_Test_Private_Service_Triggers_Deprecation();
962
963         $container->get('bar');
964     }
965
966     /**
967      * @group legacy
968      * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "foo" instead of "Foo" is deprecated since Symfony 3.4.
969      * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "FOO" instead of "Foo" is deprecated since Symfony 3.4.
970      * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "bar" instead of "BAR" is deprecated since Symfony 3.4.
971      */
972     public function testParameterWithMixedCase()
973     {
974         $container = new ContainerBuilder(new ParameterBag(array('Foo' => 'bar', 'BAR' => 'foo')));
975         $container->compile();
976
977         $dumper = new PhpDumper($container);
978         eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Parameter_With_Mixed_Case')));
979
980         $container = new \Symfony_DI_PhpDumper_Test_Parameter_With_Mixed_Case();
981
982         $this->assertSame('bar', $container->getParameter('foo'));
983         $this->assertSame('bar', $container->getParameter('FOO'));
984         $this->assertSame('foo', $container->getParameter('bar'));
985         $this->assertSame('foo', $container->getParameter('BAR'));
986     }
987
988     /**
989      * @group legacy
990      * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "FOO" instead of "foo" is deprecated since Symfony 3.4.
991      */
992     public function testParameterWithLowerCase()
993     {
994         $container = new ContainerBuilder(new ParameterBag(array('foo' => 'bar')));
995         $container->compile();
996
997         $dumper = new PhpDumper($container);
998         eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Parameter_With_Lower_Case')));
999
1000         $container = new \Symfony_DI_PhpDumper_Test_Parameter_With_Lower_Case();
1001
1002         $this->assertSame('bar', $container->getParameter('FOO'));
1003     }
1004
1005     /**
1006      * @group legacy
1007      * @expectedDeprecation Service identifiers will be made case sensitive in Symfony 4.0. Using "foo" instead of "Foo" is deprecated since Symfony 3.3.
1008      * @expectedDeprecation The "Foo" service is deprecated. You should stop using it, as it will soon be removed.
1009      */
1010     public function testReferenceWithLowerCaseId()
1011     {
1012         $container = new ContainerBuilder();
1013         $container->register('Bar', 'stdClass')->setProperty('foo', new Reference('foo'))->setPublic(true);
1014         $container->register('Foo', 'stdClass')->setDeprecated();
1015         $container->compile();
1016
1017         $dumper = new PhpDumper($container);
1018         eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Reference_With_Lower_Case_Id')));
1019
1020         $container = new \Symfony_DI_PhpDumper_Test_Reference_With_Lower_Case_Id();
1021
1022         $this->assertEquals((object) array('foo' => (object) array()), $container->get('Bar'));
1023     }
1024 }
1025
1026 class Rot13EnvVarProcessor implements EnvVarProcessorInterface
1027 {
1028     public function getEnv($prefix, $name, \Closure $getEnv)
1029     {
1030         return str_rot13($getEnv($name));
1031     }
1032
1033     public static function getProvidedTypes()
1034     {
1035         return array('rot13' => 'string');
1036     }
1037 }