4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\DependencyInjection\Tests\Dumper;
14 use PHPUnit\Framework\TestCase;
15 use Psr\Container\ContainerInterface;
16 use Symfony\Component\Config\FileLocator;
17 use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
18 use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
19 use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
20 use Symfony\Component\DependencyInjection\ChildDefinition;
21 use Symfony\Component\DependencyInjection\ContainerBuilder;
22 use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;
23 use Symfony\Component\DependencyInjection\Definition;
24 use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
25 use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
26 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
27 use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
28 use Symfony\Component\DependencyInjection\Parameter;
29 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
30 use Symfony\Component\DependencyInjection\Reference;
31 use Symfony\Component\DependencyInjection\ServiceLocator;
32 use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
33 use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator;
34 use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber;
35 use Symfony\Component\DependencyInjection\TypedReference;
36 use Symfony\Component\DependencyInjection\Variable;
37 use Symfony\Component\ExpressionLanguage\Expression;
39 require_once __DIR__.'/../Fixtures/includes/classes.php';
41 class PhpDumperTest extends TestCase
43 protected static $fixturesPath;
45 public static function setUpBeforeClass()
47 self::$fixturesPath = realpath(__DIR__.'/../Fixtures/');
50 public function testDump()
52 $container = new ContainerBuilder();
53 $container->compile();
54 $dumper = new PhpDumper($container);
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');
60 public function testDumpOptimizationString()
62 $definition = new Definition();
63 $definition->setClass('stdClass');
64 $definition->addArgument(array(
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",
77 $definition->setPublic(true);
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();
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');
90 public function testDumpRelativeDir()
92 $definition = new Definition();
93 $definition->setClass('stdClass');
94 $definition->addArgument('%foo%');
95 $definition->addArgument(array('%foo%' => '%buz%/'));
96 $definition->setPublic(true);
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();
106 $dumper = new PhpDumper($container);
107 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services12.php', $dumper->dump(array('file' => __FILE__)), '->dump() dumps __DIR__ relative strings');
110 public function testDumpCustomContainerClassWithoutConstructor()
112 $container = new ContainerBuilder();
113 $container->compile();
115 $dumper = new PhpDumper($container);
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')));
120 public function testDumpCustomContainerClassConstructorWithoutArguments()
122 $container = new ContainerBuilder();
123 $container->compile();
125 $dumper = new PhpDumper($container);
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')));
130 public function testDumpCustomContainerClassWithOptionalArgumentLessConstructor()
132 $container = new ContainerBuilder();
133 $container->compile();
135 $dumper = new PhpDumper($container);
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')));
140 public function testDumpCustomContainerClassWithMandatoryArgumentLessConstructor()
142 $container = new ContainerBuilder();
143 $container->compile();
145 $dumper = new PhpDumper($container);
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')));
151 * @dataProvider provideInvalidParameters
152 * @expectedException \InvalidArgumentException
154 public function testExportParameters($parameters)
156 $container = new ContainerBuilder(new ParameterBag($parameters));
157 $container->compile();
158 $dumper = new PhpDumper($container);
162 public function provideInvalidParameters()
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'))),
172 public function testAddParameters()
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');
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.
184 public function testAddServiceWithoutCompilation()
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');
191 public function testAddService()
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');
198 $container = new ContainerBuilder();
199 $container->register('foo', 'FooClass')->addArgument(new \stdClass())->setPublic(true);
200 $container->compile();
201 $dumper = new PhpDumper($container);
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');
211 public function testDumpAsFiles()
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);
221 $this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services9_as_files.txt', $dump);
224 public function testServicesWithAnonymousFactories()
226 $container = include self::$fixturesPath.'/containers/container19.php';
227 $container->compile();
228 $dumper = new PhpDumper($container);
230 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services19.php', $dumper->dump(), '->dump() dumps services with anonymous factories');
233 public function testAddServiceIdWithUnsupportedCharacters()
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)));
243 $this->assertTrue(method_exists($class, 'getBarService'));
244 $this->assertTrue(method_exists($class, 'getBar2Service'));
247 public function testConflictingServiceIds()
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)));
257 $this->assertTrue(method_exists($class, 'getFooBarService'));
258 $this->assertTrue(method_exists($class, 'getFoobar2Service'));
261 public function testConflictingMethodsWithParent()
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(
271 'base_class' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\containers\CustomContainer',
274 $this->assertTrue(method_exists($class, 'getBar2Service'));
275 $this->assertTrue(method_exists($class, 'getFoobar2Service'));
279 * @dataProvider provideInvalidFactories
280 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
281 * @expectedExceptionMessage Cannot dump definition
283 public function testInvalidFactories($factory)
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);
295 public function provideInvalidFactories()
298 array(array('', 'method')),
299 array(array('class', '')),
300 array(array('...', 'method')),
301 array(array('class', '...')),
305 public function testAliases()
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')));
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'));
319 public function testFrozenContainerWithoutAliases()
321 $container = new ContainerBuilder();
322 $container->compile();
324 $dumper = new PhpDumper($container);
325 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Frozen_No_Aliases')));
327 $container = new \Symfony_DI_PhpDumper_Test_Frozen_No_Aliases();
328 $this->assertFalse($container->has('foo'));
333 * @expectedDeprecation The "decorator_service" service is already initialized, replacing it is deprecated since Symfony 3.3 and will fail in 4.0.
335 public function testOverrideServiceWhenUsingADumpedContainer()
337 require_once self::$fixturesPath.'/php/services9_compiled.php';
339 $container = new \ProjectServiceContainer();
340 $container->get('decorator_service');
341 $container->set('decorator_service', $decorator = new \stdClass());
343 $this->assertSame($decorator, $container->get('decorator_service'), '->set() overrides an already defined service');
346 public function testDumpAutowireData()
348 $container = include self::$fixturesPath.'/containers/container24.php';
349 $container->compile();
350 $dumper = new PhpDumper($container);
352 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services24.php', $dumper->dump());
355 public function testEnvInId()
357 $container = include self::$fixturesPath.'/containers/container_env_in_id.php';
358 $container->compile();
359 $dumper = new PhpDumper($container);
361 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_env_in_id.php', $dumper->dump());
364 public function testEnvParameter()
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);
375 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services26.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_EnvParameters', 'file' => self::$fixturesPath.'/php/services26.php')));
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'));
385 public function testResolvedBase64EnvParameters()
387 $container = new ContainerBuilder();
388 $container->setParameter('env(foo)', base64_encode('world'));
389 $container->setParameter('hello', '%env(base64:foo)%');
390 $container->compile(true);
393 'env(foo)' => 'd29ybGQ=',
396 $this->assertSame($expected, $container->getParameterBag()->all());
399 public function testDumpedBase64EnvParameters()
401 $container = new ContainerBuilder();
402 $container->setParameter('env(foo)', base64_encode('world'));
403 $container->setParameter('hello', '%env(base64:foo)%');
404 $container->compile();
406 $dumper = new PhpDumper($container);
409 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_base64_env.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Base64Parameters')));
411 require self::$fixturesPath.'/php/services_base64_env.php';
412 $container = new \Symfony_DI_PhpDumper_Test_Base64Parameters();
413 $this->assertSame('world', $container->getParameter('hello'));
416 public function testCustomEnvParameters()
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();
424 $dumper = new PhpDumper($container);
427 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_rot13_env.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Rot13Parameters')));
429 require self::$fixturesPath.'/php/services_rot13_env.php';
430 $container = new \Symfony_DI_PhpDumper_Test_Rot13Parameters();
431 $this->assertSame('world', $container->getParameter('hello'));
434 public function testFileEnvProcessor()
436 $container = new ContainerBuilder();
437 $container->setParameter('env(foo)', __FILE__);
438 $container->setParameter('random', '%env(file:foo)%');
439 $container->compile(true);
441 $this->assertStringEqualsFile(__FILE__, $container->getParameter('random'));
445 * @expectedException \Symfony\Component\DependencyInjection\Exception\EnvParameterException
446 * @expectedExceptionMessage Environment variables "FOO" are never used. Please, check your container's configuration.
448 public function testUnusedEnvParameter()
450 $container = new ContainerBuilder();
451 $container->getParameter('env(FOO)');
452 $container->compile();
453 $dumper = new PhpDumper($container);
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)").
461 public function testCircularDynamicEnv()
463 $container = new ContainerBuilder();
464 $container->setParameter('foo', '%bar%');
465 $container->setParameter('bar', '%env(resolve:DUMMY_ENV_VAR)%');
466 $container->compile();
468 $dumper = new PhpDumper($container);
469 $dump = $dumper->dump(array('class' => $class = __FUNCTION__));
472 $container = new $class();
474 putenv('DUMMY_ENV_VAR=%foo%');
476 $container->getParameter('bar');
478 putenv('DUMMY_ENV_VAR');
482 public function testInlinedDefinitionReferencingServiceContainer()
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();
489 $dumper = new PhpDumper($container);
490 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services13.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container');
493 public function testNonSharedLazyDefinitionReferences()
495 $container = new ContainerBuilder();
496 $container->register('foo', 'stdClass')->setShared(false)->setLazy(true);
497 $container->register('bar', 'stdClass')->addArgument(new Reference('foo', ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, false));
498 $container->compile();
500 $dumper = new PhpDumper($container);
501 $dumper->setProxyDumper(new \DummyProxyDumper());
503 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_non_shared_lazy.php', $dumper->dump());
506 public function testInitializePropertiesBeforeMethodCalls()
508 require_once self::$fixturesPath.'/includes/classes.php';
510 $container = new ContainerBuilder();
511 $container->register('foo', 'stdClass')->setPublic(true);
512 $container->register('bar', 'MethodCallClass')
514 ->setProperty('simple', 'bar')
515 ->setProperty('complex', new Reference('foo'))
516 ->addMethodCall('callMe');
517 $container->compile();
519 $dumper = new PhpDumper($container);
520 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls')));
522 $container = new \Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls();
523 $this->assertTrue($container->get('bar')->callPassed(), '->dump() initializes properties before method calls');
526 public function testCircularReferenceAllowanceForLazyServices()
528 $container = new ContainerBuilder();
529 $container->register('foo', 'stdClass')->addArgument(new Reference('bar'))->setPublic(true);
530 $container->register('bar', 'stdClass')->setLazy(true)->addArgument(new Reference('foo'))->setPublic(true);
531 $container->compile();
533 $dumper = new PhpDumper($container);
534 $dumper->setProxyDumper(new \DummyProxyDumper());
537 $this->addToAssertionCount(1);
539 $dumper = new PhpDumper($container);
541 $message = 'Circular reference detected for service "foo", path: "foo -> bar -> foo". Try running "composer require symfony/proxy-manager-bridge".';
542 if (method_exists($this, 'expectException')) {
543 $this->expectException(ServiceCircularReferenceException::class);
544 $this->expectExceptionMessage($message);
546 $this->setExpectedException(ServiceCircularReferenceException::class, $message);
552 public function testDedupLazyProxy()
554 $container = new ContainerBuilder();
555 $container->register('foo', 'stdClass')->setLazy(true)->setPublic(true);
556 $container->register('bar', 'stdClass')->setLazy(true)->setPublic(true);
557 $container->compile();
559 $dumper = new PhpDumper($container);
560 $dumper->setProxyDumper(new \DummyProxyDumper());
562 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_dedup_lazy_proxy.php', $dumper->dump());
565 public function testLazyArgumentProvideGenerator()
567 require_once self::$fixturesPath.'/includes/classes.php';
569 $container = new ContainerBuilder();
570 $container->register('lazy_referenced', 'stdClass')->setPublic(true);
572 ->register('lazy_context', 'LazyContext')
574 ->setArguments(array(
575 new IteratorArgument(array('k1' => new Reference('lazy_referenced'), 'k2' => new Reference('service_container'))),
576 new IteratorArgument(array()),
579 $container->compile();
581 $dumper = new PhpDumper($container);
582 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator')));
584 $container = new \Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator();
585 $lazyContext = $container->get('lazy_context');
587 $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyValues);
588 $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyEmptyValues);
589 $this->assertCount(2, $lazyContext->lazyValues);
590 $this->assertCount(0, $lazyContext->lazyEmptyValues);
593 foreach ($lazyContext->lazyValues as $k => $v) {
596 $this->assertEquals('k1', $k);
597 $this->assertInstanceOf('stdCLass', $v);
600 $this->assertEquals('k2', $k);
601 $this->assertInstanceOf('Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator', $v);
606 $this->assertEmpty(iterator_to_array($lazyContext->lazyEmptyValues));
609 public function testNormalizedId()
611 $container = include self::$fixturesPath.'/containers/container33.php';
612 $container->compile();
613 $dumper = new PhpDumper($container);
615 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services33.php', $dumper->dump());
618 public function testDumpContainerBuilderWithFrozenConstructorIncludingPrivateServices()
620 $container = new ContainerBuilder();
621 $container->register('foo_service', 'stdClass')->setArguments(array(new Reference('baz_service')))->setPublic(true);
622 $container->register('bar_service', 'stdClass')->setArguments(array(new Reference('baz_service')))->setPublic(true);
623 $container->register('baz_service', 'stdClass')->setPublic(false);
624 $container->compile();
626 $dumper = new PhpDumper($container);
628 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_private_frozen.php', $dumper->dump());
631 public function testServiceLocator()
633 $container = new ContainerBuilder();
634 $container->register('foo_service', ServiceLocator::class)
637 'bar' => new ServiceClosureArgument(new Reference('bar_service')),
638 'baz' => new ServiceClosureArgument(new TypedReference('baz_service', 'stdClass')),
639 'nil' => $nil = new ServiceClosureArgument(new Reference('nil')),
644 $container->register('translator.loader_1', 'stdClass')->setPublic(true);
645 $container->register('translator.loader_1_locator', ServiceLocator::class)
648 'translator.loader_1' => new ServiceClosureArgument(new Reference('translator.loader_1')),
650 $container->register('translator_1', StubbedTranslator::class)
652 ->addArgument(new Reference('translator.loader_1_locator'));
655 $container->register('translator.loader_2', 'stdClass')->setPublic(true);
656 $container->register('translator.loader_2_locator', ServiceLocator::class)
659 'translator.loader_2' => new ServiceClosureArgument(new Reference('translator.loader_2')),
661 $container->register('translator_2', StubbedTranslator::class)
663 ->addArgument(new Reference('translator.loader_2_locator'))
664 ->addMethodCall('addResource', array('db', new Reference('translator.loader_2'), 'nl'));
667 $container->register('translator.loader_3', 'stdClass')->setPublic(true);
668 $container->register('translator.loader_3_locator', ServiceLocator::class)
671 'translator.loader_3' => new ServiceClosureArgument(new Reference('translator.loader_3')),
673 $container->register('translator_3', StubbedTranslator::class)
675 ->addArgument(new Reference('translator.loader_3_locator'))
676 ->addMethodCall('addResource', array('db', new Reference('translator.loader_3'), 'nl'))
677 ->addMethodCall('addResource', array('db', new Reference('translator.loader_3'), 'en'));
679 $nil->setValues(array(null));
680 $container->register('bar_service', 'stdClass')->setArguments(array(new Reference('baz_service')))->setPublic(true);
681 $container->register('baz_service', 'stdClass')->setPublic(false);
682 $container->compile();
684 $dumper = new PhpDumper($container);
686 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_locator.php', $dumper->dump());
689 public function testServiceSubscriber()
691 $container = new ContainerBuilder();
692 $container->register('foo_service', TestServiceSubscriber::class)
695 ->addArgument(new Reference(ContainerInterface::class))
696 ->addTag('container.service_subscriber', array(
698 'id' => TestServiceSubscriber::class,
701 $container->register(TestServiceSubscriber::class, TestServiceSubscriber::class)->setPublic(true);
703 $container->register(CustomDefinition::class, CustomDefinition::class)
705 $container->compile();
707 $dumper = new PhpDumper($container);
709 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_subscriber.php', $dumper->dump());
712 public function testPrivateWithIgnoreOnInvalidReference()
714 require_once self::$fixturesPath.'/includes/classes.php';
716 $container = new ContainerBuilder();
717 $container->register('not_invalid', 'BazClass')
719 $container->register('bar', 'BarClass')
721 ->addMethodCall('setBaz', array(new Reference('not_invalid', SymfonyContainerInterface::IGNORE_ON_INVALID_REFERENCE)));
722 $container->compile();
724 $dumper = new PhpDumper($container);
725 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Private_With_Ignore_On_Invalid_Reference')));
727 $container = new \Symfony_DI_PhpDumper_Test_Private_With_Ignore_On_Invalid_Reference();
728 $this->assertInstanceOf('BazClass', $container->get('bar')->getBaz());
731 public function testArrayParameters()
733 $container = new ContainerBuilder();
734 $container->setParameter('array_1', array(123));
735 $container->setParameter('array_2', array(__DIR__));
736 $container->register('bar', 'BarClass')
738 ->addMethodCall('setBaz', array('%array_1%', '%array_2%', '%%array_1%%', array(123)));
739 $container->compile();
741 $dumper = new PhpDumper($container);
743 $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 public function testExpressionReferencingPrivateService()
748 $container = new ContainerBuilder();
749 $container->register('private_bar', 'stdClass')
751 $container->register('private_foo', 'stdClass')
753 $container->register('public_foo', 'stdClass')
755 ->addArgument(new Expression('service("private_foo")'));
757 $container->compile();
758 $dumper = new PhpDumper($container);
760 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_private_in_expression.php', $dumper->dump());
763 public function testUninitializedReference()
765 $container = include self::$fixturesPath.'/containers/container_uninitialized_ref.php';
766 $container->compile();
767 $dumper = new PhpDumper($container);
769 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_uninitialized_ref.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Uninitialized_Reference')));
771 require self::$fixturesPath.'/php/services_uninitialized_ref.php';
773 $container = new \Symfony_DI_PhpDumper_Test_Uninitialized_Reference();
775 $bar = $container->get('bar');
777 $this->assertNull($bar->foo1);
778 $this->assertNull($bar->foo2);
779 $this->assertNull($bar->foo3);
780 $this->assertNull($bar->closures[0]());
781 $this->assertNull($bar->closures[1]());
782 $this->assertNull($bar->closures[2]());
783 $this->assertSame(array(), iterator_to_array($bar->iter));
785 $container = new \Symfony_DI_PhpDumper_Test_Uninitialized_Reference();
787 $container->get('foo1');
788 $container->get('baz');
790 $bar = $container->get('bar');
792 $this->assertEquals(new \stdClass(), $bar->foo1);
793 $this->assertNull($bar->foo2);
794 $this->assertEquals(new \stdClass(), $bar->foo3);
795 $this->assertEquals(new \stdClass(), $bar->closures[0]());
796 $this->assertNull($bar->closures[1]());
797 $this->assertEquals(new \stdClass(), $bar->closures[2]());
798 $this->assertEquals(array('foo1' => new \stdClass(), 'foo3' => new \stdClass()), iterator_to_array($bar->iter));
802 * @dataProvider provideAlmostCircular
804 public function testAlmostCircular($visibility)
806 $container = include self::$fixturesPath.'/containers/container_almost_circular.php';
807 $container->compile();
808 $dumper = new PhpDumper($container);
810 $container = 'Symfony_DI_PhpDumper_Test_Almost_Circular_'.ucfirst($visibility);
811 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_almost_circular_'.$visibility.'.php', $dumper->dump(array('class' => $container)));
813 require self::$fixturesPath.'/php/services_almost_circular_'.$visibility.'.php';
815 $container = new $container();
817 $foo = $container->get('foo');
818 $this->assertSame($foo, $foo->bar->foobar->foo);
820 $foo2 = $container->get('foo2');
821 $this->assertSame($foo2, $foo2->bar->foobar->foo);
823 $this->assertSame(array(), (array) $container->get('foobar4'));
825 $foo5 = $container->get('foo5');
826 $this->assertSame($foo5, $foo5->bar->foo);
828 $manager = $container->get('manager');
829 $this->assertEquals(new \stdClass(), $manager);
831 $manager = $container->get('manager2');
832 $this->assertEquals(new \stdClass(), $manager);
834 $foo6 = $container->get('foo6');
835 $this->assertEquals((object) array('bar6' => (object) array()), $foo6);
838 public function provideAlmostCircular()
840 yield array('public');
841 yield array('private');
844 public function testDeepServiceGraph()
846 $container = new ContainerBuilder();
848 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
849 $loader->load('services_deep_graph.yml');
851 $container->compile();
853 $dumper = new PhpDumper($container);
856 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_deep_graph.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Deep_Graph')));
858 require self::$fixturesPath.'/php/services_deep_graph.php';
860 $container = new \Symfony_DI_PhpDumper_Test_Deep_Graph();
862 $this->assertInstanceOf(FooForDeepGraph::class, $container->get('foo'));
863 $this->assertEquals((object) array('p2' => (object) array('p3' => (object) array())), $container->get('foo')->bClone);
866 public function testInlineSelfRef()
868 $container = new ContainerBuilder();
870 $bar = (new Definition('App\Bar'))
871 ->setProperty('foo', new Reference('App\Foo'));
873 $baz = (new Definition('App\Baz'))
874 ->setProperty('bar', $bar)
877 $container->register('App\Foo')
881 $passConfig = $container->getCompiler()->getPassConfig();
882 $container->compile();
884 $dumper = new PhpDumper($container);
885 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_inline_self_ref.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Inline_Self_Ref')));
888 public function testHotPathOptimizations()
890 $container = include self::$fixturesPath.'/containers/container_inline_requires.php';
891 $container->setParameter('inline_requires', true);
892 $container->compile();
893 $dumper = new PhpDumper($container);
895 $dump = $dumper->dump(array('hot_path_tag' => 'container.hot_path', 'inline_class_loader_parameter' => 'inline_requires', 'file' => self::$fixturesPath.'/php/services_inline_requires.php'));
896 if ('\\' === \DIRECTORY_SEPARATOR) {
897 $dump = str_replace("'\\\\includes\\\\HotPath\\\\", "'/includes/HotPath/", $dump);
900 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_inline_requires.php', $dump);
903 public function testDumpHandlesLiteralClassWithRootNamespace()
905 $container = new ContainerBuilder();
906 $container->register('foo', '\\stdClass')->setPublic(true);
907 $container->compile();
909 $dumper = new PhpDumper($container);
910 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Literal_Class_With_Root_Namespace')));
912 $container = new \Symfony_DI_PhpDumper_Test_Literal_Class_With_Root_Namespace();
914 $this->assertInstanceOf('stdClass', $container->get('foo'));
917 public function testDumpHandlesObjectClassNames()
919 $container = new ContainerBuilder(new ParameterBag(array(
920 'class' => 'stdClass',
923 $container->setDefinition('foo', new Definition(new Parameter('class')));
924 $container->setDefinition('bar', new Definition('stdClass', array(
925 new Reference('foo'),
926 )))->setPublic(true);
928 $container->setParameter('inline_requires', true);
929 $container->compile();
931 $dumper = new PhpDumper($container);
932 eval('?>'.$dumper->dump(array(
933 'class' => 'Symfony_DI_PhpDumper_Test_Object_Class_Name',
934 'inline_class_loader_parameter' => 'inline_requires',
937 $container = new \Symfony_DI_PhpDumper_Test_Object_Class_Name();
939 $this->assertInstanceOf('stdClass', $container->get('bar'));
942 public function testUninitializedSyntheticReference()
944 $container = new ContainerBuilder();
945 $container->register('foo', 'stdClass')->setPublic(true)->setSynthetic(true);
946 $container->register('bar', 'stdClass')->setPublic(true)->setShared(false)
947 ->setProperty('foo', new Reference('foo', ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE));
949 $container->compile();
951 $dumper = new PhpDumper($container);
952 eval('?>'.$dumper->dump(array(
953 'class' => 'Symfony_DI_PhpDumper_Test_UninitializedSyntheticReference',
954 'inline_class_loader_parameter' => 'inline_requires',
957 $container = new \Symfony_DI_PhpDumper_Test_UninitializedSyntheticReference();
959 $this->assertEquals((object) array('foo' => null), $container->get('bar'));
961 $container->set('foo', (object) array(123));
962 $this->assertEquals((object) array('foo' => (object) array(123)), $container->get('bar'));
965 public function testAdawsonContainer()
967 $container = new ContainerBuilder();
968 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
969 $loader->load('services_adawson.yml');
970 $container->compile();
972 $dumper = new PhpDumper($container);
973 $dump = $dumper->dump();
974 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_adawson.php', $dumper->dump());
979 * @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.
980 * @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.
981 * @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.
982 * @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.
983 * @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.
984 * @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.
985 * @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.
986 * @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.
988 public function testLegacyPrivateServices()
990 $container = new ContainerBuilder();
991 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
992 $loader->load('services_legacy_privates.yml');
994 $container->setDefinition('private_child', new ChildDefinition('foo'));
995 $container->setDefinition('private_parent', new ChildDefinition('private'));
997 $container->getDefinition('private')->setPrivate(true);
998 $container->getDefinition('private_not_inlined')->setPrivate(true);
999 $container->getDefinition('private_not_removed')->setPrivate(true);
1000 $container->getDefinition('decorated_private')->setPrivate(true);
1001 $container->getDefinition('private_child')->setPrivate(true);
1002 $container->getAlias('decorated_private_alias')->setPrivate(true);
1003 $container->getAlias('private_alias')->setPrivate(true);
1005 $container->compile();
1006 $dumper = new PhpDumper($container);
1008 $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')));
1010 require self::$fixturesPath.'/php/services_legacy_privates.php';
1012 $container = new \Symfony_DI_PhpDumper_Test_Legacy_Privates();
1014 $container->get('private');
1015 $container->get('private_alias');
1016 $container->get('alias_to_private');
1017 $container->get('decorated_private');
1018 $container->get('decorated_private_alias');
1019 $container->get('private_not_inlined');
1020 $container->get('private_not_removed');
1021 $container->get('private_child');
1022 $container->get('private_parent');
1023 $container->get('public_child');
1027 * This test checks the trigger of a deprecation note and should not be removed in major releases.
1030 * @expectedDeprecation The "foo" service is deprecated. You should stop using it, as it will soon be removed.
1032 public function testPrivateServiceTriggersDeprecation()
1034 $container = new ContainerBuilder();
1035 $container->register('foo', 'stdClass')
1037 ->setDeprecated(true);
1038 $container->register('bar', 'stdClass')
1040 ->setProperty('foo', new Reference('foo'));
1042 $container->compile();
1044 $dumper = new PhpDumper($container);
1045 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Private_Service_Triggers_Deprecation')));
1047 $container = new \Symfony_DI_PhpDumper_Test_Private_Service_Triggers_Deprecation();
1049 $container->get('bar');
1054 * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "foo" instead of "Foo" is deprecated since Symfony 3.4.
1055 * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "FOO" instead of "Foo" is deprecated since Symfony 3.4.
1056 * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "bar" instead of "BAR" is deprecated since Symfony 3.4.
1058 public function testParameterWithMixedCase()
1060 $container = new ContainerBuilder(new ParameterBag(array('Foo' => 'bar', 'BAR' => 'foo')));
1061 $container->compile();
1063 $dumper = new PhpDumper($container);
1064 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Parameter_With_Mixed_Case')));
1066 $container = new \Symfony_DI_PhpDumper_Test_Parameter_With_Mixed_Case();
1068 $this->assertSame('bar', $container->getParameter('foo'));
1069 $this->assertSame('bar', $container->getParameter('FOO'));
1070 $this->assertSame('foo', $container->getParameter('bar'));
1071 $this->assertSame('foo', $container->getParameter('BAR'));
1076 * @expectedDeprecation Parameter names will be made case sensitive in Symfony 4.0. Using "FOO" instead of "foo" is deprecated since Symfony 3.4.
1078 public function testParameterWithLowerCase()
1080 $container = new ContainerBuilder(new ParameterBag(array('foo' => 'bar')));
1081 $container->compile();
1083 $dumper = new PhpDumper($container);
1084 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Parameter_With_Lower_Case')));
1086 $container = new \Symfony_DI_PhpDumper_Test_Parameter_With_Lower_Case();
1088 $this->assertSame('bar', $container->getParameter('FOO'));
1093 * @expectedDeprecation Service identifiers will be made case sensitive in Symfony 4.0. Using "foo" instead of "Foo" is deprecated since Symfony 3.3.
1094 * @expectedDeprecation The "Foo" service is deprecated. You should stop using it, as it will soon be removed.
1096 public function testReferenceWithLowerCaseId()
1098 $container = new ContainerBuilder();
1099 $container->register('Bar', 'stdClass')->setProperty('foo', new Reference('foo'))->setPublic(true);
1100 $container->register('Foo', 'stdClass')->setDeprecated();
1101 $container->compile();
1103 $dumper = new PhpDumper($container);
1104 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Reference_With_Lower_Case_Id')));
1106 $container = new \Symfony_DI_PhpDumper_Test_Reference_With_Lower_Case_Id();
1108 $this->assertEquals((object) array('foo' => (object) array()), $container->get('Bar'));
1112 class Rot13EnvVarProcessor implements EnvVarProcessorInterface
1114 public function getEnv($prefix, $name, \Closure $getEnv)
1116 return str_rot13($getEnv($name));
1119 public static function getProvidedTypes()
1121 return array('rot13' => 'string');
1125 class FooForDeepGraph
1129 public function __construct(\stdClass $a, \stdClass $b)
1131 // clone to verify that $b has been fully initialized before
1132 $this->bClone = clone $b;