[DI] Added safeguards against invalid config in the YamlFileLoader

Exceptions explaining the mistake are better than fatal errors or weird
notices appearing when trying to deal with such invalid data.
Closes #11333
This commit is contained in:
Christophe Coevoet 2014-07-11 13:56:54 +02:00
parent b554961a5b
commit 5183501e0b
9 changed files with 90 additions and 9 deletions

View File

@ -55,6 +55,10 @@ class YamlFileLoader extends FileLoader
// parameters // parameters
if (isset($content['parameters'])) { if (isset($content['parameters'])) {
if (!is_array($content['parameters'])) {
throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $file));
}
foreach ($content['parameters'] as $key => $value) { foreach ($content['parameters'] as $key => $value) {
$this->container->setParameter($key, $this->resolveServices($value)); $this->container->setParameter($key, $this->resolveServices($value));
} }
@ -92,7 +96,15 @@ class YamlFileLoader extends FileLoader
return; return;
} }
if (!is_array($content['imports'])) {
throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in %s. Check your YAML syntax.', $file));
}
foreach ($content['imports'] as $import) { foreach ($content['imports'] as $import) {
if (!is_array($import)) {
throw new InvalidArgumentException(sprintf('The values in the "imports" key should be arrays in %s. Check your YAML syntax.', $file));
}
$this->setCurrentDir(dirname($file)); $this->setCurrentDir(dirname($file));
$this->import($import['resource'], null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file); $this->import($import['resource'], null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file);
} }
@ -110,6 +122,10 @@ class YamlFileLoader extends FileLoader
return; return;
} }
if (!is_array($content['services'])) {
throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file));
}
foreach ($content['services'] as $id => $service) { foreach ($content['services'] as $id => $service) {
$this->parseDefinition($id, $service, $file); $this->parseDefinition($id, $service, $file);
} }
@ -130,7 +146,13 @@ class YamlFileLoader extends FileLoader
$this->container->setAlias($id, substr($service, 1)); $this->container->setAlias($id, substr($service, 1));
return; return;
} elseif (isset($service['alias'])) { }
if (!is_array($service)) {
throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', gettype($service), $id, $file));
}
if (isset($service['alias'])) {
$public = !array_key_exists('public', $service) || (bool) $service['public']; $public = !array_key_exists('public', $service) || (bool) $service['public'];
$this->container->setAlias($id, new Alias($service['alias'], $public)); $this->container->setAlias($id, new Alias($service['alias'], $public));
@ -204,6 +226,10 @@ class YamlFileLoader extends FileLoader
} }
if (isset($service['calls'])) { if (isset($service['calls'])) {
if (!is_array($service['calls'])) {
throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
foreach ($service['calls'] as $call) { foreach ($service['calls'] as $call) {
$args = isset($call[1]) ? $this->resolveServices($call[1]) : array(); $args = isset($call[1]) ? $this->resolveServices($call[1]) : array();
$definition->addMethodCall($call[0], $args); $definition->addMethodCall($call[0], $args);
@ -212,10 +238,14 @@ class YamlFileLoader extends FileLoader
if (isset($service['tags'])) { if (isset($service['tags'])) {
if (!is_array($service['tags'])) { if (!is_array($service['tags'])) {
throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s.', $id, $file)); throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
} }
foreach ($service['tags'] as $tag) { foreach ($service['tags'] as $tag) {
if (!is_array($tag)) {
throw new InvalidArgumentException(sprintf('A "tags" entry must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
if (!isset($tag['name'])) { if (!isset($tag['name'])) {
throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file)); throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file));
} }
@ -223,9 +253,9 @@ class YamlFileLoader extends FileLoader
$name = $tag['name']; $name = $tag['name'];
unset($tag['name']); unset($tag['name']);
foreach ($tag as $attribute => $value) { foreach ($tag as $value) {
if (!is_scalar($value)) { if (!is_scalar($value)) {
throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s" in %s.', $id, $name, $file)); throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s" in %s. Check your YAML syntax.', $id, $name, $file));
} }
} }
@ -279,7 +309,7 @@ class YamlFileLoader extends FileLoader
} }
if (!is_array($content)) { if (!is_array($content)) {
throw new InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file)); throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file));
} }
foreach (array_keys($content) as $namespace) { foreach (array_keys($content) as $namespace) {
@ -305,9 +335,9 @@ class YamlFileLoader extends FileLoader
/** /**
* Resolves services. * Resolves services.
* *
* @param string $value * @param string|array $value
* *
* @return Reference * @return array|string|Reference
*/ */
private function resolveServices($value) private function resolveServices($value)
{ {

View File

@ -0,0 +1,4 @@
services:
method_call1:
class: FooClass
calls: foo

View File

@ -0,0 +1,2 @@
imports:
- foo.yml

View File

@ -0,0 +1,2 @@
imports:
foo:bar

View File

@ -0,0 +1,2 @@
parameters:
foo:bar

View File

@ -0,0 +1,2 @@
services:
foo: bar

View File

@ -0,0 +1 @@
services: foo

View File

@ -0,0 +1,6 @@
services:
foo_service:
class: FooClass
tags:
# tag is not an array
- foo

View File

@ -13,7 +13,6 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\Config\Loader\Loader; use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
@ -80,6 +79,29 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
} }
} }
/**
* @dataProvider provideInvalidFiles
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
*/
public function testLoadInvalidFile($file)
{
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load($file.'.yml');
}
public function provideInvalidFiles()
{
return array(
array('bad_parameters'),
array('bad_imports'),
array('bad_import'),
array('bad_services'),
array('bad_service'),
array('bad_calls'),
);
}
public function testLoadParameters() public function testLoadParameters()
{ {
$container = new ContainerBuilder(); $container = new ContainerBuilder();
@ -179,7 +201,7 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
} }
public function testNonArrayTagThrowsException() public function testNonArrayTagsThrowsException()
{ {
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
try { try {
@ -191,6 +213,16 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
} }
} }
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
* @expectedExceptionMessage A "tags" entry must be an array for service
*/
public function testNonArrayTagThrowsException()
{
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('badtag4.yml');
}
public function testTagWithoutNameThrowsException() public function testTagWithoutNameThrowsException()
{ {
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));