Skip to content

Commit

Permalink
feature #19584 [DependencyInjection] Improve ParameterNotFoundExcepti…
Browse files Browse the repository at this point in the history
…on when accessing a nested parameter (wouterj)

This PR was squashed before being merged into the 3.2-dev branch (closes #19584).

Discussion
----------

[DependencyInjection] Improve ParameterNotFoundException when accessing a nested parameter

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | n/a

A common problem under beginners is to think that the dot notation is used to access nested arrays saved in parameters (*common here means someone asks help for this problem at least once a week on IRC*). Adding a little extra detail to the exception message and a working alternative should help pointing these people in the right direction before spending time debugging this.

It's quite late in the night over here, so the wording of the exception message probably isn't great. I'm happy to accept better suggestions 😃

Commits
-------

df70f06 [DependencyInjection] Improve ParameterNotFoundException when accessing a nested parameter
  • Loading branch information
fabpot committed Sep 14, 2016
2 parents 68737eb + df70f06 commit 0e63f47
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 29 deletions.
Expand Up @@ -22,20 +22,23 @@ class ParameterNotFoundException extends InvalidArgumentException
private $sourceId;
private $sourceKey;
private $alternatives;
private $nonNestedAlternative;

/**
* @param string $key The requested parameter key
* @param string $sourceId The service id that references the non-existent parameter
* @param string $sourceKey The parameter key that references the non-existent parameter
* @param \Exception $previous The previous exception
* @param string[] $alternatives Some parameter name alternatives
* @param string $key The requested parameter key
* @param string $sourceId The service id that references the non-existent parameter
* @param string $sourceKey The parameter key that references the non-existent parameter
* @param \Exception $previous The previous exception
* @param string[] $alternatives Some parameter name alternatives
* @param string|null $nonNestedAlternative The alternative parameter name when the user expected dot notation for nested parameters
*/
public function __construct($key, $sourceId = null, $sourceKey = null, \Exception $previous = null, array $alternatives = array())
public function __construct($key, $sourceId = null, $sourceKey = null, \Exception $previous = null, array $alternatives = array(), $nonNestedAlternative = null)
{
$this->key = $key;
$this->sourceId = $sourceId;
$this->sourceKey = $sourceKey;
$this->alternatives = $alternatives;
$this->nonNestedAlternative = $nonNestedAlternative;

parent::__construct('', 0, $previous);

Expand All @@ -59,6 +62,8 @@ public function updateRepr()
$this->message .= ' Did you mean one of these: "';
}
$this->message .= implode('", "', $this->alternatives).'"?';
} elseif (null !== $this->nonNestedAlternative) {
$this->message .= ' You cannot access nested array items, do you want to inject "'.$this->nonNestedAlternative.'" instead?';
}
}

Expand Down
Expand Up @@ -81,7 +81,23 @@ public function get($name)
}
}

throw new ParameterNotFoundException($name, null, null, null, $alternatives);
$nonNestedAlternative = null;
if (!count($alternatives) && false !== strpos($name, '.')) {
$namePartsLength = array_map('strlen', explode('.', $name));
$key = substr($name, 0, -1 * (1 + array_pop($namePartsLength)));
while (count($namePartsLength)) {
if ($this->has($key)) {
if (is_array($this->get($key))) {
$nonNestedAlternative = $key;
}
break;
}

$key = substr($key, 0, -1 * (1 + array_pop($namePartsLength)));
}
}

throw new ParameterNotFoundException($name, null, null, null, $alternatives, $nonNestedAlternative);
}

return $this->parameters[$name];
Expand Down
Expand Up @@ -71,37 +71,32 @@ public function testGetSet()
}
}

public function testGetThrowParameterNotFoundException()
/**
* @dataProvider provideGetThrowParameterNotFoundExceptionData
*/
public function testGetThrowParameterNotFoundException($parameterKey, $exceptionMessage)
{
$bag = new ParameterBag(array(
'foo' => 'foo',
'bar' => 'bar',
'baz' => 'baz',
'fiz' => array('bar' => array('boo' => 12)),
));

try {
$bag->get('foo1');
$this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist');
} catch (\Exception $e) {
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist');
$this->assertEquals('You have requested a non-existent parameter "foo1". Did you mean this: "foo"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException with some advices');
}
$this->setExpectedException('Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException', $exceptionMessage);

try {
$bag->get('bag');
$this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist');
} catch (\Exception $e) {
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist');
$this->assertEquals('You have requested a non-existent parameter "bag". Did you mean one of these: "bar", "baz"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException with some advices');
}
$bag->get($parameterKey);
}

try {
$bag->get('');
$this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist');
} catch (\Exception $e) {
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist');
$this->assertEquals('You have requested a non-existent parameter "".', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException with some advices');
}
public function provideGetThrowParameterNotFoundExceptionData()
{
return array(
array('foo1', 'You have requested a non-existent parameter "foo1". Did you mean this: "foo"?'),
array('bag', 'You have requested a non-existent parameter "bag". Did you mean one of these: "bar", "baz"?'),
array('', 'You have requested a non-existent parameter "".'),

array('fiz.bar.boo', 'You have requested a non-existent parameter "fiz.bar.boo". You cannot access nested array items, do you want to inject "fiz" instead?'),
);
}

public function testHas()
Expand Down

0 comments on commit 0e63f47

Please sign in to comment.