diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php index 87007b2b36..2053d3a53d 100644 --- a/src/Symfony/Component/Config/Definition/ArrayNode.php +++ b/src/Symfony/Component/Config/Definition/ArrayNode.php @@ -307,7 +307,30 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface // if extra fields are present, throw exception if (\count($value) && !$this->ignoreExtraKeys) { + $proposals = array_keys($this->children); + sort($proposals); + $guesses = array(); + + foreach (array_keys($value) as $subject) { + $minScore = INF; + foreach ($proposals as $proposal) { + $distance = levenshtein($subject, $proposal); + if ($distance <= $minScore && $distance < 3) { + $guesses[$proposal] = $distance; + $minScore = $distance; + } + } + } + $msg = sprintf('Unrecognized option%s "%s" under "%s"', 1 === \count($value) ? '' : 's', implode(', ', array_keys($value)), $this->getPath()); + + if (\count($guesses)) { + asort($guesses); + $msg .= sprintf('. Did you mean "%s"?', implode('", "', array_keys($guesses))); + } else { + $msg .= sprintf('. Available option%s %s "%s".', 1 === \count($proposals) ? '' : 's', 1 === \count($proposals) ? 'is' : 'are', implode('", "', $proposals)); + } + $ex = new InvalidConfigurationException($msg); $ex->setPath($this->getPath()); diff --git a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php index 089cb56638..a95439009d 100644 --- a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php @@ -37,6 +37,31 @@ class ArrayNodeTest extends TestCase $node->normalize(array('foo' => 'bar')); } + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage Did you mean "alpha1", "alpha2"? + */ + public function testNormalizeWithProposals() + { + $node = new ArrayNode('root'); + $node->addChild(new ArrayNode('alpha1')); + $node->addChild(new ArrayNode('alpha2')); + $node->addChild(new ArrayNode('beta')); + $node->normalize(array('alpha3' => 'foo')); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage Available options are "alpha1", "alpha2". + */ + public function testNormalizeWithoutProposals() + { + $node = new ArrayNode('root'); + $node->addChild(new ArrayNode('alpha1')); + $node->addChild(new ArrayNode('alpha2')); + $node->normalize(array('beta' => 'foo')); + } + public function ignoreAndRemoveMatrixProvider() { $unrecognizedOptionException = new InvalidConfigurationException('Unrecognized option "foo" under "root"');