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\Loader;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\DependencyInjection\ContainerBuilder;
16 use Symfony\Component\DependencyInjection\Reference;
17 use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
18 use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
19 use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
20 use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
21 use Symfony\Component\Config\Loader\LoaderResolver;
22 use Symfony\Component\Config\FileLocator;
23 use Symfony\Component\ExpressionLanguage\Expression;
25 class YamlFileLoaderTest extends TestCase
27 protected static $fixturesPath;
29 public static function setUpBeforeClass()
31 self::$fixturesPath = realpath(__DIR__.'/../Fixtures/');
32 require_once self::$fixturesPath.'/includes/foo.php';
33 require_once self::$fixturesPath.'/includes/ProjectExtension.php';
37 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
38 * @expectedExceptionMessageRegExp /The file ".+" does not exist./
40 public function testLoadUnExistFile()
42 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini'));
43 $r = new \ReflectionObject($loader);
44 $m = $r->getMethod('loadFile');
45 $m->setAccessible(true);
47 $m->invoke($loader, 'foo.yml');
51 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
52 * @expectedExceptionMessageRegExp /The file ".+" does not contain valid YAML./
54 public function testLoadInvalidYamlFile()
56 $path = self::$fixturesPath.'/ini';
57 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator($path));
58 $r = new \ReflectionObject($loader);
59 $m = $r->getMethod('loadFile');
60 $m->setAccessible(true);
62 $m->invoke($loader, $path.'/parameters.ini');
66 * @dataProvider provideInvalidFiles
67 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
69 public function testLoadInvalidFile($file)
71 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
73 $loader->load($file.'.yml');
76 public function provideInvalidFiles()
79 array('bad_parameters'),
82 array('bad_services'),
91 public function testLoadParameters()
93 $container = new ContainerBuilder();
94 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
95 $loader->load('services2.yml');
96 $this->assertEquals(array('foo' => 'bar', 'mixedcase' => array('MixedCaseKey' => 'value'), 'values' => array(true, false, 0, 1000.3, PHP_INT_MAX), 'bar' => 'foo', 'escape' => '@escapeme', 'foo_bar' => new Reference('foo_bar')), $container->getParameterBag()->all(), '->load() converts YAML keys to lowercase');
99 public function testLoadImports()
101 $container = new ContainerBuilder();
102 $resolver = new LoaderResolver(array(
103 new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/ini')),
104 new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')),
105 new PhpFileLoader($container, new FileLocator(self::$fixturesPath.'/php')),
106 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')),
108 $loader->setResolver($resolver);
109 $loader->load('services4.yml');
111 $actual = $container->getParameterBag()->all();
112 $expected = array('foo' => 'bar', 'values' => array(true, false, PHP_INT_MAX), 'bar' => '%foo%', 'escape' => '@escapeme', 'foo_bar' => new Reference('foo_bar'), 'mixedcase' => array('MixedCaseKey' => 'value'), 'imported_from_ini' => true, 'imported_from_xml' => true);
113 $this->assertEquals(array_keys($expected), array_keys($actual), '->load() imports and merges imported files');
114 $this->assertTrue($actual['imported_from_ini']);
116 // Bad import throws no exception due to ignore_errors value.
117 $loader->load('services4_bad_import.yml');
120 public function testLoadServices()
122 $container = new ContainerBuilder();
123 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
124 $loader->load('services6.yml');
125 $services = $container->getDefinitions();
126 $this->assertTrue(isset($services['foo']), '->load() parses service elements');
127 $this->assertFalse($services['not_shared']->isShared(), '->load() parses the shared flag');
128 $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Definition', $services['foo'], '->load() converts service element to Definition instances');
129 $this->assertEquals('FooClass', $services['foo']->getClass(), '->load() parses the class attribute');
130 $this->assertEquals('%path%/foo.php', $services['file']->getFile(), '->load() parses the file tag');
131 $this->assertEquals(array('foo', new Reference('foo'), array(true, false)), $services['arguments']->getArguments(), '->load() parses the argument tags');
132 $this->assertEquals('sc_configure', $services['configurator1']->getConfigurator(), '->load() parses the configurator tag');
133 $this->assertEquals(array(new Reference('baz'), 'configure'), $services['configurator2']->getConfigurator(), '->load() parses the configurator tag');
134 $this->assertEquals(array('BazClass', 'configureStatic'), $services['configurator3']->getConfigurator(), '->load() parses the configurator tag');
135 $this->assertEquals(array(array('setBar', array()), array('setBar', array()), array('setBar', array(new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")')))), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag');
136 $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag');
137 $this->assertEquals('factory', $services['new_factory1']->getFactory(), '->load() parses the factory tag');
138 $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['new_factory2']->getFactory(), '->load() parses the factory tag');
139 $this->assertEquals(array('BazClass', 'getInstance'), $services['new_factory3']->getFactory(), '->load() parses the factory tag');
141 $aliases = $container->getAliases();
142 $this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses aliases');
143 $this->assertEquals('foo', (string) $aliases['alias_for_foo'], '->load() parses aliases');
144 $this->assertTrue($aliases['alias_for_foo']->isPublic());
145 $this->assertTrue(isset($aliases['another_alias_for_foo']));
146 $this->assertEquals('foo', (string) $aliases['another_alias_for_foo']);
147 $this->assertFalse($aliases['another_alias_for_foo']->isPublic());
149 $this->assertEquals(array('decorated', null, 0), $services['decorator_service']->getDecoratedService());
150 $this->assertEquals(array('decorated', 'decorated.pif-pouf', 0), $services['decorator_service_with_name']->getDecoratedService());
151 $this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService());
154 public function testLoadFactoryShortSyntax()
156 $container = new ContainerBuilder();
157 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
158 $loader->load('services14.yml');
159 $services = $container->getDefinitions();
161 $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['factory']->getFactory(), '->load() parses the factory tag with service:method');
162 $this->assertEquals(array('FooBacFactory', 'createFooBar'), $services['factory_with_static_call']->getFactory(), '->load() parses the factory tag with Class::method');
165 public function testLoadConfiguratorShortSyntax()
167 $container = new ContainerBuilder();
168 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
169 $loader->load('services_configurator_short_syntax.yml');
170 $services = $container->getDefinitions();
172 $this->assertEquals(array(new Reference('foo_bar_configurator'), 'configure'), $services['foo_bar']->getConfigurator(), '->load() parses the configurator tag with service:method');
173 $this->assertEquals(array('FooBarConfigurator', 'configureFooBar'), $services['foo_bar_with_static_call']->getConfigurator(), '->load() parses the configurator tag with Class::method');
176 public function testExtensions()
178 $container = new ContainerBuilder();
179 $container->registerExtension(new \ProjectExtension());
180 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
181 $loader->load('services10.yml');
182 $container->compile();
183 $services = $container->getDefinitions();
184 $parameters = $container->getParameterBag()->all();
186 $this->assertTrue(isset($services['project.service.bar']), '->load() parses extension elements');
187 $this->assertTrue(isset($parameters['project.parameter.bar']), '->load() parses extension elements');
189 $this->assertEquals('BAR', $services['project.service.foo']->getClass(), '->load() parses extension elements');
190 $this->assertEquals('BAR', $parameters['project.parameter.foo'], '->load() parses extension elements');
193 $loader->load('services11.yml');
194 $this->fail('->load() throws an InvalidArgumentException if the tag is not valid');
195 } catch (\Exception $e) {
196 $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tag is not valid');
197 $this->assertStringStartsWith('There is no extension able to load the configuration for "foobarfoobar" (in', $e->getMessage(), '->load() throws an InvalidArgumentException if the tag is not valid');
201 public function testSupports()
203 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator());
205 $this->assertTrue($loader->supports('foo.yml'), '->supports() returns true if the resource is loadable');
206 $this->assertTrue($loader->supports('foo.yaml'), '->supports() returns true if the resource is loadable');
207 $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
210 public function testNonArrayTagsThrowsException()
212 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
214 $loader->load('badtag1.yml');
215 $this->fail('->load() should throw an exception when the tags key of a service is not an array');
216 } catch (\Exception $e) {
217 $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tags key is not an array');
218 $this->assertStringStartsWith('Parameter "tags" must be an array for service', $e->getMessage(), '->load() throws an InvalidArgumentException if the tags key is not an array');
223 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
224 * @expectedExceptionMessage A "tags" entry must be an array for service
226 public function testNonArrayTagThrowsException()
228 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
229 $loader->load('badtag4.yml');
232 public function testTagWithoutNameThrowsException()
234 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
236 $loader->load('badtag2.yml');
237 $this->fail('->load() should throw an exception when a tag is missing the name key');
238 } catch (\Exception $e) {
239 $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if a tag is missing the name key');
240 $this->assertStringStartsWith('A "tags" entry is missing a "name" key for service ', $e->getMessage(), '->load() throws an InvalidArgumentException if a tag is missing the name key');
244 public function testTagWithAttributeArrayThrowsException()
246 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
248 $loader->load('badtag3.yml');
249 $this->fail('->load() should throw an exception when a tag-attribute is not a scalar');
250 } catch (\Exception $e) {
251 $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if a tag-attribute is not a scalar');
252 $this->assertStringStartsWith('A "tags" attribute must be of a scalar-type for service "foo_service", tag "foo", attribute "bar"', $e->getMessage(), '->load() throws an InvalidArgumentException if a tag-attribute is not a scalar');
256 public function testLoadYamlOnlyWithKeys()
258 $container = new ContainerBuilder();
259 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
260 $loader->load('services21.yml');
262 $definition = $container->getDefinition('manager');
263 $this->assertEquals(array(array('setLogger', array(new Reference('logger'))), array('setClass', array('User'))), $definition->getMethodCalls());
264 $this->assertEquals(array(true), $definition->getArguments());
265 $this->assertEquals(array('manager' => array(array('alias' => 'user'))), $definition->getTags());
269 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
270 * @expectedExceptionMessageRegExp /The tag name for service ".+" in .+ must be a non-empty string/
272 public function testTagWithEmptyNameThrowsException()
274 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
275 $loader->load('tag_name_empty_string.yml');
279 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
280 * @expectedExceptionMessageREgExp /The tag name for service "\.+" must be a non-empty string/
282 public function testTagWithNonStringNameThrowsException()
284 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
285 $loader->load('tag_name_no_string.yml');
289 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
291 public function testTypesNotArray()
293 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
294 $loader->load('bad_types1.yml');
298 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
300 public function testTypeNotString()
302 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
303 $loader->load('bad_types2.yml');
306 public function testTypes()
308 $container = new ContainerBuilder();
309 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
310 $loader->load('services22.yml');
312 $this->assertEquals(array('Foo', 'Bar'), $container->getDefinition('foo_service')->getAutowiringTypes());
313 $this->assertEquals(array('Foo'), $container->getDefinition('baz_service')->getAutowiringTypes());
316 public function testAutowire()
318 $container = new ContainerBuilder();
319 $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
320 $loader->load('services23.yml');
322 $this->assertTrue($container->getDefinition('bar_service')->isAutowired());
326 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
327 * @expectedExceptionMessage The value of the "decorates" option for the "bar" service must be the id of the service without the "@" prefix (replace "@foo" with "foo").
329 public function testDecoratedServicesWithWrongSyntaxThrowsException()
331 $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
332 $loader->load('bad_decorates.yml');