[DependencyInjection] Add a new pass to check arguments validity

This commit is contained in:
Kévin Dunglas 2017-01-31 10:49:36 +01:00
parent 6e501296f9
commit 2ce36a6074
No known key found for this signature in database
GPG Key ID: 4D04EBEF06AAF3A6
7 changed files with 134 additions and 7 deletions

View File

@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
/**
* Checks if arguments of methods are properly configured.
*
* @author Kévin Dunglas <dunglas@gmail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/
class CheckArgumentsValidityPass extends AbstractRecursivePass
{
/**
* {@inheritdoc}
*/
protected function processValue($value, $isRoot = false)
{
if (!$value instanceof Definition) {
return parent::processValue($value, $isRoot);
}
$i = 0;
foreach ($value->getArguments() as $k => $v) {
if ($k !== $i++) {
if (!is_int($k)) {
throw new RuntimeException(sprintf('Invalid constructor argument for service "%s": integer expected but found string "%s". Check your service definition.', $this->currentId, $k));
}
throw new RuntimeException(sprintf('Invalid constructor argument %d for service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $this->currentId, $i));
}
}
foreach ($value->getMethodCalls() as $methodCall) {
$i = 0;
foreach ($methodCall[1] as $k => $v) {
if ($k !== $i++) {
if (!is_int($k)) {
throw new RuntimeException(sprintf('Invalid argument for method call "%s" of service "%s": integer expected but found string "%s". Check your service definition.', $methodCall[0], $this->currentId, $k));
}
throw new RuntimeException(sprintf('Invalid argument %d for method call "%s" of service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $methodCall[0], $this->currentId, $i));
}
}
}
}
}

View File

@ -60,6 +60,7 @@ class PassConfig
new AnalyzeServiceReferencesPass(true),
new CheckCircularReferencesPass(),
new CheckReferenceValidityPass(),
new CheckArgumentsValidityPass(),
));
$this->removingPasses = array(array(

View File

@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CheckArgumentsValidityPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class CheckArgumentsValidityPassTest extends \PHPUnit_Framework_TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$definition = $container->register('foo');
$definition->setArguments(array(null, 1, 'a'));
$definition->setMethodCalls(array(
array('bar', array('a', 'b')),
array('baz', array('c', 'd')),
));
$pass = new CheckArgumentsValidityPass();
$pass->process($container);
$this->assertEquals(array(null, 1, 'a'), $container->getDefinition('foo')->getArguments());
$this->assertEquals(array(
array('bar', array('a', 'b')),
array('baz', array('c', 'd')),
), $container->getDefinition('foo')->getMethodCalls());
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @dataProvider definitionProvider
*/
public function testException(array $arguments, array $methodCalls)
{
$container = new ContainerBuilder();
$definition = $container->register('foo');
$definition->setArguments($arguments);
$definition->setMethodCalls($methodCalls);
$pass = new CheckArgumentsValidityPass();
$pass->process($container);
}
public function definitionProvider()
{
return array(
array(array(null, 'a' => 'a'), array()),
array(array(1 => 1), array()),
array(array(), array(array('baz', array(null, 'a' => 'a')))),
array(array(), array(array('baz', array(1 => 1)))),
);
}
}

View File

@ -2,6 +2,7 @@
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy">
<argument />
<argument key="$apiKey">ABCD</argument>
<call method="setApiKey">
<argument key="$apiKey">123</argument>

View File

@ -1,9 +1,10 @@
services:
Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy: { $apiKey: ABCD }
Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy: { 0: ~, $apiKey: ABCD }
another_one:
class: Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy
arguments:
0: ~
$apiKey: ABCD
calls:
- ['setApiKey', { $apiKey: '123' }]

View File

@ -701,11 +701,11 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
$loader->load('services_named_args.xml');
$this->assertEquals(array('$apiKey' => 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments());
$this->assertEquals(array(null, '$apiKey' => 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments());
$container->compile();
$this->assertEquals(array(1 => 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments());
$this->assertEquals(array(null, 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments());
$this->assertEquals(array(array('setApiKey', array('123'))), $container->getDefinition(NamedArgumentsDummy::class)->getMethodCalls());
}
}

View File

@ -447,13 +447,13 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('services_named_args.yml');
$this->assertEquals(array('$apiKey' => 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments());
$this->assertEquals(array('$apiKey' => 'ABCD'), $container->getDefinition('another_one')->getArguments());
$this->assertEquals(array(null, '$apiKey' => 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments());
$this->assertEquals(array(null, '$apiKey' => 'ABCD'), $container->getDefinition('another_one')->getArguments());
$container->compile();
$this->assertEquals(array(1 => 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments());
$this->assertEquals(array(1 => 'ABCD'), $container->getDefinition('another_one')->getArguments());
$this->assertEquals(array(null, 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments());
$this->assertEquals(array(null, 'ABCD'), $container->getDefinition('another_one')->getArguments());
$this->assertEquals(array(array('setApiKey', array('123'))), $container->getDefinition('another_one')->getMethodCalls());
}