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;
15 use PHPUnit\Framework\TestCase;
16 use Symfony\Component\Config\FileLocator;
17 use Symfony\Component\DependencyInjection\ContainerBuilder;
18 use Symfony\Component\DependencyInjection\ContainerInterface;
19 use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
20 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
21 use Symfony\Component\DependencyInjection\Reference;
22 use Symfony\Component\DependencyInjection\Definition;
23 use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
24 use Symfony\Component\DependencyInjection\Variable;
25 use Symfony\Component\ExpressionLanguage\Expression;
27 require_once __DIR__.'/../Fixtures/includes/classes.php';
29 class PhpDumperTest extends TestCase
31 protected static $fixturesPath;
33 public static function setUpBeforeClass()
35 self::$fixturesPath = realpath(__DIR__.'/../Fixtures/');
38 public function testDump()
40 $dumper = new PhpDumper(new ContainerBuilder());
42 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services1.php', $dumper->dump(), '->dump() dumps an empty container as an empty PHP class');
43 $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');
46 public function testDumpOptimizationString()
48 $definition = new Definition();
49 $definition->setClass('stdClass');
50 $definition->addArgument(array(
52 'concatenation as value' => '.\'\'.',
53 'concatenation from the start value' => '\'\'.',
54 '.' => 'dot as a key',
55 '.\'\'.' => 'concatenation as a key',
56 '\'\'.' => 'concatenation from the start key',
57 'optimize concatenation' => 'string1%some_string%string2',
58 'optimize concatenation with empty string' => 'string1%empty_value%string2',
59 'optimize concatenation from the start' => '%empty_value%start',
60 'optimize concatenation at the end' => 'end%empty_value%',
63 $container = new ContainerBuilder();
64 $container->setResourceTracking(false);
65 $container->setDefinition('test', $definition);
66 $container->setParameter('empty_value', '');
67 $container->setParameter('some_string', '-');
68 $container->compile();
70 $dumper = new PhpDumper($container);
71 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services10.php', $dumper->dump(), '->dump() dumps an empty container as an empty PHP class');
74 public function testDumpRelativeDir()
76 $definition = new Definition();
77 $definition->setClass('stdClass');
78 $definition->addArgument('%foo%');
79 $definition->addArgument(array('%foo%' => '%buz%/'));
81 $container = new ContainerBuilder();
82 $container->setDefinition('test', $definition);
83 $container->setParameter('foo', 'wiz'.dirname(__DIR__));
84 $container->setParameter('bar', __DIR__);
85 $container->setParameter('baz', '%bar%/PhpDumperTest.php');
86 $container->setParameter('buz', dirname(dirname(__DIR__)));
87 $container->compile();
89 $dumper = new PhpDumper($container);
90 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services12.php', $dumper->dump(array('file' => __FILE__)), '->dump() dumps __DIR__ relative strings');
94 * @dataProvider provideInvalidParameters
95 * @expectedException \InvalidArgumentException
97 public function testExportParameters($parameters)
99 $dumper = new PhpDumper(new ContainerBuilder(new ParameterBag($parameters)));
103 public function provideInvalidParameters()
106 array(array('foo' => new Definition('stdClass'))),
107 array(array('foo' => new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'))),
108 array(array('foo' => new Reference('foo'))),
109 array(array('foo' => new Variable('foo'))),
113 public function testAddParameters()
115 $container = include self::$fixturesPath.'/containers/container8.php';
116 $dumper = new PhpDumper($container);
117 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services8.php', $dumper->dump(), '->dump() dumps parameters');
120 public function testAddService()
122 // without compilation
123 $container = include self::$fixturesPath.'/containers/container9.php';
124 $dumper = new PhpDumper($container);
125 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services9.php', str_replace(str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), '%path%', $dumper->dump()), '->dump() dumps services');
128 $container = include self::$fixturesPath.'/containers/container9.php';
129 $container->compile();
130 $dumper = new PhpDumper($container);
131 $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');
133 $dumper = new PhpDumper($container = new ContainerBuilder());
134 $container->register('foo', 'FooClass')->addArgument(new \stdClass());
137 $this->fail('->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources');
138 } catch (\Exception $e) {
139 $this->assertInstanceOf('\Symfony\Component\DependencyInjection\Exception\RuntimeException', $e, '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources');
140 $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');
144 public function testServicesWithAnonymousFactories()
146 $container = include self::$fixturesPath.'/containers/container19.php';
147 $dumper = new PhpDumper($container);
149 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services19.php', $dumper->dump(), '->dump() dumps services with anonymous factories');
152 public function testAddServiceIdWithUnsupportedCharacters()
154 $class = 'Symfony_DI_PhpDumper_Test_Unsupported_Characters';
155 $container = new ContainerBuilder();
156 $container->register('bar$', 'FooClass');
157 $container->register('bar$!', 'FooClass');
158 $dumper = new PhpDumper($container);
159 eval('?>'.$dumper->dump(array('class' => $class)));
161 $this->assertTrue(method_exists($class, 'getBarService'));
162 $this->assertTrue(method_exists($class, 'getBar2Service'));
165 public function testConflictingServiceIds()
167 $class = 'Symfony_DI_PhpDumper_Test_Conflicting_Service_Ids';
168 $container = new ContainerBuilder();
169 $container->register('foo_bar', 'FooClass');
170 $container->register('foobar', 'FooClass');
171 $dumper = new PhpDumper($container);
172 eval('?>'.$dumper->dump(array('class' => $class)));
174 $this->assertTrue(method_exists($class, 'getFooBarService'));
175 $this->assertTrue(method_exists($class, 'getFoobar2Service'));
178 public function testConflictingMethodsWithParent()
180 $class = 'Symfony_DI_PhpDumper_Test_Conflicting_Method_With_Parent';
181 $container = new ContainerBuilder();
182 $container->register('bar', 'FooClass');
183 $container->register('foo_bar', 'FooClass');
184 $dumper = new PhpDumper($container);
185 eval('?>'.$dumper->dump(array(
187 'base_class' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\containers\CustomContainer',
190 $this->assertTrue(method_exists($class, 'getBar2Service'));
191 $this->assertTrue(method_exists($class, 'getFoobar2Service'));
195 * @dataProvider provideInvalidFactories
196 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
197 * @expectedExceptionMessage Cannot dump definition
199 public function testInvalidFactories($factory)
201 $container = new ContainerBuilder();
202 $def = new Definition('stdClass');
203 $def->setFactory($factory);
204 $container->setDefinition('bar', $def);
205 $dumper = new PhpDumper($container);
209 public function provideInvalidFactories()
212 array(array('', 'method')),
213 array(array('class', '')),
214 array(array('...', 'method')),
215 array(array('class', '...')),
219 public function testAliases()
221 $container = include self::$fixturesPath.'/containers/container9.php';
222 $container->compile();
223 $dumper = new PhpDumper($container);
224 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Aliases')));
226 $container = new \Symfony_DI_PhpDumper_Test_Aliases();
227 $container->set('foo', $foo = new \stdClass());
228 $this->assertSame($foo, $container->get('foo'));
229 $this->assertSame($foo, $container->get('alias_for_foo'));
230 $this->assertSame($foo, $container->get('alias_for_alias'));
233 public function testFrozenContainerWithoutAliases()
235 $container = new ContainerBuilder();
236 $container->compile();
238 $dumper = new PhpDumper($container);
239 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Frozen_No_Aliases')));
241 $container = new \Symfony_DI_PhpDumper_Test_Frozen_No_Aliases();
242 $this->assertFalse($container->has('foo'));
245 public function testOverrideServiceWhenUsingADumpedContainer()
247 require_once self::$fixturesPath.'/php/services9.php';
248 require_once self::$fixturesPath.'/includes/foo.php';
250 $container = new \ProjectServiceContainer();
251 $container->set('bar', $bar = new \stdClass());
252 $container->setParameter('foo_bar', 'foo_bar');
254 $this->assertSame($bar, $container->get('bar'), '->set() overrides an already defined service');
257 public function testOverrideServiceWhenUsingADumpedContainerAndServiceIsUsedFromAnotherOne()
259 require_once self::$fixturesPath.'/php/services9.php';
260 require_once self::$fixturesPath.'/includes/foo.php';
261 require_once self::$fixturesPath.'/includes/classes.php';
263 $container = new \ProjectServiceContainer();
264 $container->set('bar', $bar = new \stdClass());
266 $this->assertSame($bar, $container->get('foo')->bar, '->set() overrides an already defined service');
270 * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
272 public function testCircularReference()
274 $container = new ContainerBuilder();
275 $container->register('foo', 'stdClass')->addArgument(new Reference('bar'));
276 $container->register('bar', 'stdClass')->setPublic(false)->addMethodCall('setA', array(new Reference('baz')));
277 $container->register('baz', 'stdClass')->addMethodCall('setA', array(new Reference('foo')));
278 $container->compile();
280 $dumper = new PhpDumper($container);
284 public function testDumpAutowireData()
286 $container = include self::$fixturesPath.'/containers/container24.php';
287 $dumper = new PhpDumper($container);
289 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services24.php', $dumper->dump());
292 public function testEnvParameter()
294 $container = new ContainerBuilder();
295 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
296 $loader->load('services26.yml');
297 $container->compile();
298 $dumper = new PhpDumper($container);
300 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services26.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container');
304 * @expectedException \Symfony\Component\DependencyInjection\Exception\EnvParameterException
305 * @expectedExceptionMessage Environment variables "FOO" are never used. Please, check your container's configuration.
307 public function testUnusedEnvParameter()
309 $container = new ContainerBuilder();
310 $container->getParameter('env(FOO)');
311 $container->compile();
312 $dumper = new PhpDumper($container);
316 public function testInlinedDefinitionReferencingServiceContainer()
318 $container = new ContainerBuilder();
319 $container->register('foo', 'stdClass')->addMethodCall('add', array(new Reference('service_container')))->setPublic(false);
320 $container->register('bar', 'stdClass')->addArgument(new Reference('foo'));
321 $container->compile();
323 $dumper = new PhpDumper($container);
324 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services13.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container');
327 public function testInitializePropertiesBeforeMethodCalls()
329 require_once self::$fixturesPath.'/includes/classes.php';
331 $container = new ContainerBuilder();
332 $container->register('foo', 'stdClass');
333 $container->register('bar', 'MethodCallClass')
334 ->setProperty('simple', 'bar')
335 ->setProperty('complex', new Reference('foo'))
336 ->addMethodCall('callMe');
337 $container->compile();
339 $dumper = new PhpDumper($container);
340 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls')));
342 $container = new \Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls();
343 $this->assertTrue($container->get('bar')->callPassed(), '->dump() initializes properties before method calls');
346 public function testCircularReferenceAllowanceForLazyServices()
348 $container = new ContainerBuilder();
349 $container->register('foo', 'stdClass')->addArgument(new Reference('bar'));
350 $container->register('bar', 'stdClass')->setLazy(true)->addArgument(new Reference('foo'));
351 $container->compile();
353 $dumper = new PhpDumper($container);
356 $this->addToAssertionCount(1);
359 public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServices()
363 * [connection] -> [event_manager] --> [entity_manager](lazy)
365 * --(call)- addEventListener ("@lazy_service")
367 * [lazy_service](lazy) -> [entity_manager](lazy)
371 $container = new ContainerBuilder();
373 $eventManagerDefinition = new Definition('stdClass');
375 $connectionDefinition = $container->register('connection', 'stdClass');
376 $connectionDefinition->addArgument($eventManagerDefinition);
378 $container->register('entity_manager', 'stdClass')
380 ->addArgument(new Reference('connection'));
382 $lazyServiceDefinition = $container->register('lazy_service', 'stdClass');
383 $lazyServiceDefinition->setLazy(true);
384 $lazyServiceDefinition->addArgument(new Reference('entity_manager'));
386 $eventManagerDefinition->addMethodCall('addEventListener', array(new Reference('lazy_service')));
388 $container->compile();
390 $dumper = new PhpDumper($container);
392 $dumper->setProxyDumper(new DummyProxyDumper());
395 $this->addToAssertionCount(1);
398 public function testDumpContainerBuilderWithFrozenConstructorIncludingPrivateServices()
400 $container = new ContainerBuilder();
401 $container->register('foo_service', 'stdClass')->setArguments(array(new Reference('baz_service')));
402 $container->register('bar_service', 'stdClass')->setArguments(array(new Reference('baz_service')));
403 $container->register('baz_service', 'stdClass')->setPublic(false);
404 $container->compile();
406 $dumper = new PhpDumper($container);
408 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_private_frozen.php', $dumper->dump());
411 public function testPrivateWithIgnoreOnInvalidReference()
413 require_once self::$fixturesPath.'/includes/classes.php';
415 $container = new ContainerBuilder();
416 $container->register('not_invalid', 'BazClass')
418 $container->register('bar', 'BarClass')
419 ->addMethodCall('setBaz', array(new Reference('not_invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)));
421 $dumper = new PhpDumper($container);
422 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Private_With_Ignore_On_Invalid_Reference')));
424 $container = new \Symfony_DI_PhpDumper_Test_Private_With_Ignore_On_Invalid_Reference();
425 $this->assertInstanceOf('BazClass', $container->get('bar')->getBaz());
428 public function testExpressionReferencingPrivateService()
430 $container = new ContainerBuilder();
431 $container->register('private_bar', 'stdClass')
433 $container->register('private_foo', 'stdClass')
435 $container->register('public_foo', 'stdClass')
436 ->addArgument(new Expression('service("private_foo")'));
438 $container->compile();
439 $dumper = new PhpDumper($container);
441 $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_private_in_expression.php', $dumper->dump());
444 public function testDumpHandlesLiteralClassWithRootNamespace()
446 $container = new ContainerBuilder();
447 $container->register('foo', '\\stdClass');
449 $dumper = new PhpDumper($container);
450 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Literal_Class_With_Root_Namespace')));
452 $container = new \Symfony_DI_PhpDumper_Test_Literal_Class_With_Root_Namespace();
454 $this->assertInstanceOf('stdClass', $container->get('foo'));
458 * This test checks the trigger of a deprecation note and should not be removed in major releases.
461 * @expectedDeprecation The "foo" service is deprecated. You should stop using it, as it will soon be removed.
463 public function testPrivateServiceTriggersDeprecation()
465 $container = new ContainerBuilder();
466 $container->register('foo', 'stdClass')
468 ->setDeprecated(true);
469 $container->register('bar', 'stdClass')
471 ->setProperty('foo', new Reference('foo'));
473 $container->compile();
475 $dumper = new PhpDumper($container);
476 eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Private_Service_Triggers_Deprecation')));
478 $container = new \Symfony_DI_PhpDumper_Test_Private_Service_Triggers_Deprecation();
480 $container->get('bar');