diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php index e741b8a02de2..a063978ef14f 100644 --- a/src/Symfony/Component/Config/Definition/ArrayNode.php +++ b/src/Symfony/Component/Config/Definition/ArrayNode.php @@ -396,7 +396,12 @@ protected function mergeValues($leftSide, $rightSide) } if (!isset($this->children[$k])) { - throw new \RuntimeException('merge() expects a normalized config array.'); + if (!$this->ignoreExtraKeys || $this->removeExtraKeys) { + throw new \RuntimeException('merge() expects a normalized config array.'); + } + + $leftSide[$k] = $v; + continue; } $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v); diff --git a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php index 33bede7427c9..fa91b47b5188 100644 --- a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php @@ -255,4 +255,66 @@ public function testSetDeprecated() restore_error_handler(); $this->assertTrue($deprecationTriggered, '->finalize() should trigger if the deprecated node is set'); } + + /** + * @dataProvider getDataWithIncludedExtraKeys + */ + public function testMergeWithoutIgnoringExtraKeys($prenormalizeds, $merged) + { + $this->expectException('RuntimeException'); + $this->expectExceptionMessage('merge() expects a normalized config array.'); + $node = new ArrayNode('root'); + $node->addChild(new ScalarNode('foo')); + $node->addChild(new ScalarNode('bar')); + $node->setIgnoreExtraKeys(false); + + $r = new \ReflectionMethod($node, 'mergeValues'); + $r->setAccessible(true); + + $r->invoke($node, ...$prenormalizeds); + } + + /** + * @dataProvider getDataWithIncludedExtraKeys + */ + public function testMergeWithIgnoringAndRemovingExtraKeys($prenormalizeds, $merged) + { + $this->expectException('RuntimeException'); + $this->expectExceptionMessage('merge() expects a normalized config array.'); + $node = new ArrayNode('root'); + $node->addChild(new ScalarNode('foo')); + $node->addChild(new ScalarNode('bar')); + $node->setIgnoreExtraKeys(true); + + $r = new \ReflectionMethod($node, 'mergeValues'); + $r->setAccessible(true); + + $r->invoke($node, ...$prenormalizeds); + } + + /** + * @dataProvider getDataWithIncludedExtraKeys + */ + public function testMergeWithIgnoringExtraKeys($prenormalizeds, $merged) + { + $node = new ArrayNode('root'); + $node->addChild(new ScalarNode('foo')); + $node->addChild(new ScalarNode('bar')); + $node->setIgnoreExtraKeys(true, false); + + $r = new \ReflectionMethod($node, 'mergeValues'); + $r->setAccessible(true); + + $this->assertEquals($merged, $r->invoke($node, ...$prenormalizeds)); + } + + public function getDataWithIncludedExtraKeys() + { + return [ + [ + [['foo' => 'bar', 'baz' => 'not foo'], ['bar' => 'baz', 'baz' => 'foo']], + ['foo' => 'bar', 'bar' => 'baz', 'baz' => 'foo'], + ], + ]; + } }