Browse Source

[Config] Adding an ignoreExtraKeys options, which allows you to let options simply be ignore without throwing a validation exception.

I had thought that this was unnecessary - when would you ever want to just let "extra" options fail silently?

But, the SecurityExtension takes advantage of this by creating two separate config trees. The first tree looks for just one particular value on the configuration array and ignores the rest. So, there *is* a use-case for allowing all extra fields to simply be ignored, though this should not be the norm.
Ryan Weaver 14 years ago
parent
commit
026ab6c6ce

+ 13 - 2
src/Symfony/Component/Config/Definition/ArrayNode.php

@@ -35,6 +35,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
     protected $minNumberOfElements;
     protected $performDeepMerging;
     protected $defaultValue;
+    protected $ignoreExtraKeys;
 
     /**
      * Constructor.
@@ -144,13 +145,23 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
     /**
      * Sets if deep merging should occur.
      *
-     * @param boolean $boolean
+     * @param Boolean $boolean
      */
     public function setPerformDeepMerging($boolean)
     {
         $this->performDeepMerging = (Boolean) $boolean;
     }
 
+    /**
+     * Whether extra keys should just be ignore without an exception.
+     *
+     * @param Boolean $boolean To allow extra keys
+     */
+    public function setIgnoreExtraKeys($boolean)
+    {
+        $this->ignoreExtraKeys = (Boolean) $boolean;
+    }
+
     /**
      * Sets the node Name.
      *
@@ -416,7 +427,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
         }
 
         // if extra fields are present, throw exception
-        if (count($value)) {
+        if (count($value) && !$this->ignoreExtraKeys) {
             $msg = sprintf('Unrecognized options "%s" under "%s"', implode(', ', array_keys($value)), $this->getPath());
 
             throw new InvalidConfigurationException($msg);

+ 18 - 0
src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php

@@ -43,6 +43,7 @@ class NodeBuilder
     public $trueEquivalent;
     public $falseEquivalent;
     public $performDeepMerging;
+    public $ignoreExtraKeys;
 
     /**
      * Constructor
@@ -484,4 +485,21 @@ class NodeBuilder
     {
         return $this->parent;
     }
+
+    /**
+     * Allows extra config keys to be specified under an array without
+     * throwing an exception.
+     *
+     * Those config values are simply ignored. This should be used only
+     * in special cases where you want to send an entire configuration
+     * array through a special tree that processes only part of the array.
+     *
+     * @return Symfony\Component\Config\Definition\Builder\NodeBuilder
+     */
+    public function ignoreExtraKeys()
+    {
+        $this->ignoreExtraKeys = true;
+
+        return $this;
+    }
 }

+ 1 - 0
src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php

@@ -163,6 +163,7 @@ class TreeBuilder
         $configNode->addEquivalentValue(false, $node->falseEquivalent);
         $configNode->setPerformDeepMerging($node->performDeepMerging);
         $configNode->setRequired($node->required);
+        $configNode->setIgnoreExtraKeys($node->ignoreExtraKeys);
 
         if (null !== $node->key) {
             $configNode->setKeyAttribute($node->key, $node->removeKeyItem);

+ 15 - 0
tests/Symfony/Tests/Component/Config/Definition/ArrayNodeTest.php

@@ -68,6 +68,21 @@ class ArrayNodeTest extends \PHPUnit_Framework_TestCase
         }
     }
 
+    /**
+     * Tests that no exception is thrown for an unrecognized child if the
+     * ignoreExtraKeys option is set to true.
+     *
+     * Related to testExceptionThrownOnUnrecognizedChild
+     */
+    public function testIgnoreExtraKeysNoException()
+    {
+        $node = new ArrayNode('roo');
+        $node->setIgnoreExtraKeys(true);
+
+        $node->normalize(array('foo' => 'bar'));
+        $this->assertTrue(true, 'No exception was thrown when setIgnoreExtraKeys is true');
+    }
+
     // a remapped key (e.g. "mapping" -> "mappings") should be unset after being used
     public function testRemappedKeysAreUnset()
     {