[DependencyInjection] added a check for circular references in parameter definitions
This commit is contained in:
parent
456eb53eb8
commit
2438a73c7b
@ -0,0 +1,34 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* This exception is thrown when a circular reference in a parameter is detected.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ParameterCircularReferenceException extends RuntimeException
|
||||
{
|
||||
private $parameters;
|
||||
|
||||
public function __construct($parameters)
|
||||
{
|
||||
parent::__construct(sprintf('Circular reference detected for parameter "%s" ("%s" > "%s").', $parameters[0], implode('" > "', $parameters), $parameters[0]));
|
||||
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\DependencyInjection\ParameterBag;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
|
||||
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -125,18 +126,20 @@ class ParameterBag implements ParameterBagInterface
|
||||
* Replaces parameter placeholders (%name%) by their values.
|
||||
*
|
||||
* @param mixed $value A value
|
||||
* @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
|
||||
*
|
||||
* @return mixed The resolved value
|
||||
*
|
||||
* @throws ParameterNotFoundException if a placeholder references a parameter that does not exist
|
||||
* @throws ParameterCircularReferenceException if a circular reference if detected
|
||||
* @throws \LogicException when a given parameter has a type problem.
|
||||
*/
|
||||
public function resolveValue($value)
|
||||
public function resolveValue($value, array $resolving = array())
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$args = array();
|
||||
foreach ($value as $k => $v) {
|
||||
$args[$this->resolveValue($k)] = $this->resolveValue($v);
|
||||
$args[$this->resolveValue($k, $resolving)] = $this->resolveValue($v, $resolving);
|
||||
}
|
||||
|
||||
return $args;
|
||||
@ -146,36 +149,55 @@ class ParameterBag implements ParameterBagInterface
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $this->resolveString($value);
|
||||
return $this->resolveString($value, $resolving);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves parameters inside a string
|
||||
*
|
||||
* @param string $value The string to resolve
|
||||
* @param string $value The string to resolve
|
||||
* @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
|
||||
*
|
||||
* @return string The resolved string
|
||||
*
|
||||
* @throws ParameterNotFoundException if a placeholder references a parameter that does not exist
|
||||
* @throws ParameterCircularReferenceException if a circular reference if detected
|
||||
* @throws \LogicException when a given parameter has a type problem.
|
||||
*/
|
||||
public function resolveString($value)
|
||||
public function resolveString($value, array $resolving = array())
|
||||
{
|
||||
// we do this to deal with non string values (Boolean, integer, ...)
|
||||
// as the preg_replace_callback throw an exception when trying
|
||||
// a non-string in a parameter value
|
||||
if (preg_match('/^%([^%]+)%$/', $value, $match)) {
|
||||
// we do this to deal with non string values (Boolean, integer, ...)
|
||||
// as the preg_replace_callback throw an exception when trying
|
||||
// a non-string in a parameter value
|
||||
return $this->resolveValue($this->get(strtolower($match[1])));
|
||||
$key = strtolower($match[1]);
|
||||
|
||||
if (isset($resolving[$key])) {
|
||||
throw new ParameterCircularReferenceException(array_keys($resolving));
|
||||
}
|
||||
|
||||
$resolving[$key] = true;
|
||||
|
||||
return $this->resolveValue($this->get($key), $resolving);
|
||||
}
|
||||
|
||||
$self = $this;
|
||||
return str_replace('%%', '%', preg_replace_callback('/(?<!%)%([^%]+)%/', function ($match) use ($self) {
|
||||
$resolved = $self->get(strtolower($match[1]));
|
||||
return str_replace('%%', '%', preg_replace_callback('/(?<!%)%([^%]+)%/', function ($match) use ($self, $resolving) {
|
||||
$key = strtolower($match[1]);
|
||||
|
||||
if (isset($resolving[$key])) {
|
||||
throw new ParameterCircularReferenceException(array_keys($resolving));
|
||||
}
|
||||
|
||||
$resolved = $self->get($key);
|
||||
|
||||
if (!is_string($resolved)) {
|
||||
throw new \LogicException('A parameter cannot contain a non-string parameter.');
|
||||
}
|
||||
|
||||
return $self->resolveString($resolved);
|
||||
$resolving[$key] = true;
|
||||
|
||||
return $self->resolveString($resolved, $resolving);
|
||||
}, $value));
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Tests\Component\DependencyInjection\ParameterBag;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
|
||||
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
|
||||
|
||||
class ParameterBagTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
@ -120,6 +121,22 @@ class ParameterBagTest extends \PHPUnit_Framework_TestCase
|
||||
} catch (\LogicException $e) {
|
||||
$this->assertEquals('A parameter cannot contain a non-string parameter.', $e->getMessage(), '->resolveValue() throws a LogicException when a parameter embeds another non-string parameter');
|
||||
}
|
||||
|
||||
$bag = new ParameterBag(array('foo' => '%bar%', 'bar' => '%foobar%', 'foobar' => '%foo%'));
|
||||
try {
|
||||
$bag->resolveValue('%foo%');
|
||||
$this->fail('->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference');
|
||||
} catch (ParameterCircularReferenceException $e) {
|
||||
$this->assertEquals('Circular reference detected for parameter "foo" ("foo" > "bar" > "foobar" > "foo").', $e->getMessage(), '->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference');
|
||||
}
|
||||
|
||||
$bag = new ParameterBag(array('foo' => 'a %bar%', 'bar' => 'a %foobar%', 'foobar' => 'a %foo%'));
|
||||
try {
|
||||
$bag->resolveValue('%foo%');
|
||||
$this->fail('->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference');
|
||||
} catch (ParameterCircularReferenceException $e) {
|
||||
$this->assertEquals('Circular reference detected for parameter "foo" ("foo" > "bar" > "foobar" > "foo").', $e->getMessage(), '->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user