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;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\DependencyInjection\Container;
16 use Symfony\Component\DependencyInjection\ContainerInterface;
17 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
19 class ContainerTest extends TestCase
21 public function testConstructor()
23 $sc = new Container();
24 $this->assertSame($sc, $sc->get('service_container'), '__construct() automatically registers itself as a service');
26 $sc = new Container(new ParameterBag(array('foo' => 'bar')));
27 $this->assertEquals(array('foo' => 'bar'), $sc->getParameterBag()->all(), '__construct() takes an array of parameters as its first argument');
31 * @dataProvider dataForTestCamelize
33 public function testCamelize($id, $expected)
35 $this->assertEquals($expected, Container::camelize($id), sprintf('Container::camelize("%s")', $id));
38 public function dataForTestCamelize()
41 array('foo_bar', 'FooBar'),
42 array('foo.bar', 'Foo_Bar'),
43 array('foo.bar_baz', 'Foo_BarBaz'),
44 array('foo._bar', 'Foo_Bar'),
45 array('foo_.bar', 'Foo_Bar'),
47 array('.foo', '_Foo'),
49 array('foo.', 'Foo_'),
50 array('foo\bar', 'Foo_Bar'),
55 * @dataProvider dataForTestUnderscore
57 public function testUnderscore($id, $expected)
59 $this->assertEquals($expected, Container::underscore($id), sprintf('Container::underscore("%s")', $id));
62 public function dataForTestUnderscore()
65 array('FooBar', 'foo_bar'),
66 array('Foo_Bar', 'foo.bar'),
67 array('Foo_BarBaz', 'foo.bar_baz'),
68 array('FooBar_BazQux', 'foo_bar.baz_qux'),
69 array('_Foo', '.foo'),
70 array('Foo_', 'foo.'),
74 public function testCompile()
76 $sc = new Container(new ParameterBag(array('foo' => 'bar')));
77 $this->assertFalse($sc->getParameterBag()->isResolved(), '->compile() resolves the parameter bag');
79 $this->assertTrue($sc->getParameterBag()->isResolved(), '->compile() resolves the parameter bag');
80 $this->assertInstanceOf('Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag', $sc->getParameterBag(), '->compile() changes the parameter bag to a FrozenParameterBag instance');
81 $this->assertEquals(array('foo' => 'bar'), $sc->getParameterBag()->all(), '->compile() copies the current parameters to the new parameter bag');
84 public function testIsFrozen()
86 $sc = new Container(new ParameterBag(array('foo' => 'bar')));
87 $this->assertFalse($sc->isFrozen(), '->isFrozen() returns false if the parameters are not frozen');
89 $this->assertTrue($sc->isFrozen(), '->isFrozen() returns true if the parameters are frozen');
92 public function testGetParameterBag()
94 $sc = new Container();
95 $this->assertEquals(array(), $sc->getParameterBag()->all(), '->getParameterBag() returns an empty array if no parameter has been defined');
98 public function testGetSetParameter()
100 $sc = new Container(new ParameterBag(array('foo' => 'bar')));
101 $sc->setParameter('bar', 'foo');
102 $this->assertEquals('foo', $sc->getParameter('bar'), '->setParameter() sets the value of a new parameter');
104 $sc->setParameter('foo', 'baz');
105 $this->assertEquals('baz', $sc->getParameter('foo'), '->setParameter() overrides previously set parameter');
107 $sc->setParameter('Foo', 'baz1');
108 $this->assertEquals('baz1', $sc->getParameter('foo'), '->setParameter() converts the key to lowercase');
109 $this->assertEquals('baz1', $sc->getParameter('FOO'), '->getParameter() converts the key to lowercase');
112 $sc->getParameter('baba');
113 $this->fail('->getParameter() thrown an \InvalidArgumentException if the key does not exist');
114 } catch (\Exception $e) {
115 $this->assertInstanceOf('\InvalidArgumentException', $e, '->getParameter() thrown an \InvalidArgumentException if the key does not exist');
116 $this->assertEquals('You have requested a non-existent parameter "baba".', $e->getMessage(), '->getParameter() thrown an \InvalidArgumentException if the key does not exist');
120 public function testGetServiceIds()
122 $sc = new Container();
123 $sc->set('foo', $obj = new \stdClass());
124 $sc->set('bar', $obj = new \stdClass());
125 $this->assertEquals(array('service_container', 'foo', 'bar'), $sc->getServiceIds(), '->getServiceIds() returns all defined service ids');
127 $sc = new ProjectServiceContainer();
128 $sc->set('foo', $obj = new \stdClass());
129 $this->assertEquals(array('service_container', 'internal', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'internal_dependency', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by factory methods in the method map, followed by service ids defined by set()');
134 * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.
136 public function testGetLegacyServiceIds()
138 $sc = new LegacyProjectServiceContainer();
139 $sc->set('foo', $obj = new \stdClass());
141 $this->assertEquals(array('internal', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'service_container', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by getXXXService() methods, followed by service ids defined by set()');
144 public function testSet()
146 $sc = new Container();
147 $sc->set('._. \\o/', $foo = new \stdClass());
148 $this->assertSame($foo, $sc->get('._. \\o/'), '->set() sets a service');
151 public function testSetWithNullResetTheService()
153 $sc = new Container();
154 $sc->set('foo', null);
155 $this->assertFalse($sc->has('foo'), '->set() with null service resets the service');
158 public function testSetReplacesAlias()
160 $c = new ProjectServiceContainer();
162 $c->set('alias', $foo = new \stdClass());
163 $this->assertSame($foo, $c->get('alias'), '->set() replaces an existing alias');
166 public function testGet()
168 $sc = new ProjectServiceContainer();
169 $sc->set('foo', $foo = new \stdClass());
170 $this->assertSame($foo, $sc->get('foo'), '->get() returns the service for the given id');
171 $this->assertSame($foo, $sc->get('Foo'), '->get() returns the service for the given id, and converts id to lowercase');
172 $this->assertSame($sc->__bar, $sc->get('bar'), '->get() returns the service for the given id');
173 $this->assertSame($sc->__foo_bar, $sc->get('foo_bar'), '->get() returns the service if a get*Method() is defined');
174 $this->assertSame($sc->__foo_baz, $sc->get('foo.baz'), '->get() returns the service if a get*Method() is defined');
176 $sc->set('bar', $bar = new \stdClass());
177 $this->assertSame($bar, $sc->get('bar'), '->get() prefers to return a service defined with set() than one defined with a getXXXMethod()');
181 $this->fail('->get() throws a \InvalidArgumentException exception if the service is empty');
182 } catch (\Exception $e) {
183 $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws a ServiceNotFoundException exception if the service is empty');
185 $this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service is empty');
190 * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.
191 * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.
192 * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.
193 * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.
195 public function testLegacyGet()
197 $sc = new LegacyProjectServiceContainer();
198 $sc->set('foo', $foo = new \stdClass());
200 $this->assertSame($foo, $sc->get('foo'), '->get() returns the service for the given id');
201 $this->assertSame($foo, $sc->get('Foo'), '->get() returns the service for the given id, and converts id to lowercase');
202 $this->assertSame($sc->__bar, $sc->get('bar'), '->get() returns the service for the given id');
203 $this->assertSame($sc->__foo_bar, $sc->get('foo_bar'), '->get() returns the service if a get*Method() is defined');
204 $this->assertSame($sc->__foo_baz, $sc->get('foo.baz'), '->get() returns the service if a get*Method() is defined');
205 $this->assertSame($sc->__foo_baz, $sc->get('foo\\baz'), '->get() returns the service if a get*Method() is defined');
207 $sc->set('bar', $bar = new \stdClass());
208 $this->assertSame($bar, $sc->get('bar'), '->get() prefers to return a service defined with set() than one defined with a getXXXMethod()');
212 $this->fail('->get() throws a \InvalidArgumentException exception if the service is empty');
213 } catch (\Exception $e) {
214 $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws a ServiceNotFoundException exception if the service is empty');
216 $this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service is empty');
219 public function testGetThrowServiceNotFoundException()
221 $sc = new ProjectServiceContainer();
222 $sc->set('foo', $foo = new \stdClass());
223 $sc->set('baz', $foo = new \stdClass());
227 $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist');
228 } catch (\Exception $e) {
229 $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist');
230 $this->assertEquals('You have requested a non-existent service "foo1". Did you mean this: "foo"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException with some advices');
235 $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist');
236 } catch (\Exception $e) {
237 $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist');
238 $this->assertEquals('You have requested a non-existent service "bag". Did you mean one of these: "bar", "baz"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException with some advices');
242 public function testGetCircularReference()
244 $sc = new ProjectServiceContainer();
246 $sc->get('circular');
247 $this->fail('->get() throws a ServiceCircularReferenceException if it contains circular reference');
248 } catch (\Exception $e) {
249 $this->assertInstanceOf('\Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException', $e, '->get() throws a ServiceCircularReferenceException if it contains circular reference');
250 $this->assertStringStartsWith('Circular reference detected for service "circular"', $e->getMessage(), '->get() throws a \LogicException if it contains circular reference');
255 * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
256 * @expectedExceptionMessage You have requested a synthetic service ("request"). The DIC does not know how to construct this service.
258 public function testGetSyntheticServiceAlwaysThrows()
260 require_once __DIR__.'/Fixtures/php/services9.php';
262 $container = new \ProjectServiceContainer();
263 $container->get('request', ContainerInterface::NULL_ON_INVALID_REFERENCE);
266 public function testHas()
268 $sc = new ProjectServiceContainer();
269 $sc->set('foo', new \stdClass());
270 $this->assertFalse($sc->has('foo1'), '->has() returns false if the service does not exist');
271 $this->assertTrue($sc->has('foo'), '->has() returns true if the service exists');
272 $this->assertTrue($sc->has('bar'), '->has() returns true if a get*Method() is defined');
273 $this->assertTrue($sc->has('foo_bar'), '->has() returns true if a get*Method() is defined');
274 $this->assertTrue($sc->has('foo.baz'), '->has() returns true if a get*Method() is defined');
279 * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.
280 * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.
281 * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.
282 * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.
284 public function testLegacyHas()
286 $sc = new LegacyProjectServiceContainer();
287 $sc->set('foo', new \stdClass());
289 $this->assertFalse($sc->has('foo1'), '->has() returns false if the service does not exist');
290 $this->assertTrue($sc->has('foo'), '->has() returns true if the service exists');
291 $this->assertTrue($sc->has('bar'), '->has() returns true if a get*Method() is defined');
292 $this->assertTrue($sc->has('foo_bar'), '->has() returns true if a get*Method() is defined');
293 $this->assertTrue($sc->has('foo.baz'), '->has() returns true if a get*Method() is defined');
294 $this->assertTrue($sc->has('foo\\baz'), '->has() returns true if a get*Method() is defined');
297 public function testInitialized()
299 $sc = new ProjectServiceContainer();
300 $sc->set('foo', new \stdClass());
301 $this->assertTrue($sc->initialized('foo'), '->initialized() returns true if service is loaded');
302 $this->assertFalse($sc->initialized('foo1'), '->initialized() returns false if service is not loaded');
303 $this->assertFalse($sc->initialized('bar'), '->initialized() returns false if a service is defined, but not currently loaded');
304 $this->assertFalse($sc->initialized('alias'), '->initialized() returns false if an aliased service is not initialized');
306 $sc->set('bar', new \stdClass());
307 $this->assertTrue($sc->initialized('alias'), '->initialized() returns true for alias if aliased service is initialized');
310 public function testReset()
312 $c = new Container();
313 $c->set('bar', new \stdClass());
317 $this->assertNull($c->get('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE));
321 * @expectedException \Exception
322 * @expectedExceptionMessage Something went terribly wrong!
324 public function testGetThrowsException()
326 $c = new ProjectServiceContainer();
329 $c->get('throw_exception');
330 } catch (\Exception $e) {
334 // Retry, to make sure that get*Service() will be called.
335 $c->get('throw_exception');
338 public function testGetThrowsExceptionOnServiceConfiguration()
340 $c = new ProjectServiceContainer();
343 $c->get('throws_exception_on_service_configuration');
344 } catch (\Exception $e) {
348 $this->assertFalse($c->initialized('throws_exception_on_service_configuration'));
350 // Retry, to make sure that get*Service() will be called.
352 $c->get('throws_exception_on_service_configuration');
353 } catch (\Exception $e) {
356 $this->assertFalse($c->initialized('throws_exception_on_service_configuration'));
359 protected function getField($obj, $field)
361 $reflection = new \ReflectionProperty($obj, $field);
362 $reflection->setAccessible(true);
364 return $reflection->getValue($obj);
367 public function testAlias()
369 $c = new ProjectServiceContainer();
371 $this->assertTrue($c->has('alias'));
372 $this->assertSame($c->get('alias'), $c->get('bar'));
375 public function testThatCloningIsNotSupported()
377 $class = new \ReflectionClass('Symfony\Component\DependencyInjection\Container');
378 $clone = $class->getMethod('__clone');
379 $this->assertFalse($class->isCloneable());
380 $this->assertTrue($clone->isPrivate());
385 * @expectedDeprecation Unsetting the "internal" private service is deprecated since Symfony 3.2 and won't be supported anymore in Symfony 4.0.
387 public function testUnsetInternalPrivateServiceIsDeprecated()
389 $c = new ProjectServiceContainer();
390 $c->set('internal', null);
395 * @expectedDeprecation Setting the "internal" private service is deprecated since Symfony 3.2 and won't be supported anymore in Symfony 4.0. A new public service will be created instead.
397 public function testChangeInternalPrivateServiceIsDeprecated()
399 $c = new ProjectServiceContainer();
400 $c->set('internal', $internal = new \stdClass());
401 $this->assertSame($c->get('internal'), $internal);
406 * @expectedDeprecation Checking for the existence of the "internal" private service is deprecated since Symfony 3.2 and won't be supported anymore in Symfony 4.0.
408 public function testCheckExistenceOfAnInternalPrivateServiceIsDeprecated()
410 $c = new ProjectServiceContainer();
411 $c->get('internal_dependency');
412 $this->assertTrue($c->has('internal'));
417 * @expectedDeprecation Requesting the "internal" private service is deprecated since Symfony 3.2 and won't be supported anymore in Symfony 4.0.
419 public function testRequestAnInternalSharedPrivateServiceIsDeprecated()
421 $c = new ProjectServiceContainer();
422 $c->get('internal_dependency');
427 class ProjectServiceContainer extends Container
433 protected $methodMap = array(
434 'internal' => 'getInternalService',
435 'bar' => 'getBarService',
436 'foo_bar' => 'getFooBarService',
437 'foo.baz' => 'getFoo_BazService',
438 'circular' => 'getCircularService',
439 'throw_exception' => 'getThrowExceptionService',
440 'throws_exception_on_service_configuration' => 'getThrowsExceptionOnServiceConfigurationService',
441 'internal_dependency' => 'getInternalDependencyService',
444 public function __construct()
446 parent::__construct();
448 $this->__bar = new \stdClass();
449 $this->__foo_bar = new \stdClass();
450 $this->__foo_baz = new \stdClass();
451 $this->__internal = new \stdClass();
452 $this->privates = array('internal' => true);
453 $this->aliases = array('alias' => 'bar');
456 protected function getInternalService()
458 return $this->services['internal'] = $this->__internal;
461 protected function getBarService()
466 protected function getFooBarService()
468 return $this->__foo_bar;
471 protected function getFoo_BazService()
473 return $this->__foo_baz;
476 protected function getCircularService()
478 return $this->get('circular');
481 protected function getThrowExceptionService()
483 throw new \Exception('Something went terribly wrong!');
486 protected function getThrowsExceptionOnServiceConfigurationService()
488 $this->services['throws_exception_on_service_configuration'] = $instance = new \stdClass();
490 throw new \Exception('Something was terribly wrong while trying to configure the service!');
493 protected function getInternalDependencyService()
495 $this->services['internal_dependency'] = $instance = new \stdClass();
497 $instance->internal = isset($this->services['internal']) ? $this->services['internal'] : $this->getInternalService();
503 class LegacyProjectServiceContainer extends Container
510 public function __construct()
512 parent::__construct();
514 $this->__bar = new \stdClass();
515 $this->__foo_bar = new \stdClass();
516 $this->__foo_baz = new \stdClass();
517 $this->__internal = new \stdClass();
518 $this->privates = array('internal' => true);
519 $this->aliases = array('alias' => 'bar');
522 protected function getInternalService()
524 return $this->__internal;
527 protected function getBarService()
532 protected function getFooBarService()
534 return $this->__foo_bar;
537 protected function getFoo_BazService()
539 return $this->__foo_baz;
542 protected function getCircularService()
544 return $this->get('circular');
547 protected function getThrowExceptionService()
549 throw new \Exception('Something went terribly wrong!');
552 protected function getThrowsExceptionOnServiceConfigurationService()
554 $this->services['throws_exception_on_service_configuration'] = $instance = new \stdClass();
556 throw new \Exception('Something was terribly wrong while trying to configure the service!');