diff --git a/src/Symfony/Component/OptionsResolver/CHANGELOG.md b/src/Symfony/Component/OptionsResolver/CHANGELOG.md new file mode 100644 index 0000000000000..a3a44679289c9 --- /dev/null +++ b/src/Symfony/Component/OptionsResolver/CHANGELOG.md @@ -0,0 +1,8 @@ +CHANGELOG +========= + +2.5.0 +----- + + * added flags FORBID_UNKNOWN, REMOVE_UNKNOWN, IGNORE_UNKNOWN, FORBID_MISSING + and IGNORE_MISSING to OptionsResolverInterface diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 237ab8135f503..6e9f69cdecc4e 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -214,10 +214,25 @@ public function isRequired($option) /** * {@inheritdoc} */ - public function resolve(array $options = array()) + public function resolve(array $options = array(), $flags = 0) { - $this->validateOptionsExistence($options); - $this->validateOptionsCompleteness($options); + if (!($flags & (self::FORBID_UNKNOWN | self::REMOVE_UNKNOWN | self::IGNORE_UNKNOWN))) { + $flags |= self::FORBID_UNKNOWN; + } + + if (!($flags & (self::FORBID_MISSING | self::IGNORE_MISSING))) { + $flags |= self::FORBID_MISSING; + } + + if ($flags & self::FORBID_UNKNOWN) { + $this->validateOptionsExistence($options); + } elseif ($flags & self::REMOVE_UNKNOWN) { + $options = array_intersect_key($options, $this->knownOptions); + } + + if ($flags & self::FORBID_MISSING) { + $this->validateOptionsCompleteness($options); + } // Make sure this method can be called multiple times $combinedOptions = clone $this->defaultOptions; @@ -240,7 +255,7 @@ public function resolve(array $options = array()) * Validates that the given option names exist and throws an exception * otherwise. * - * @param array $options An list of option names as keys. + * @param array $options A list of option names as keys. * * @throws InvalidOptionsException If any of the options has not been defined. */ @@ -264,7 +279,7 @@ private function validateOptionsExistence(array $options) * Validates that all required options are given and throws an exception * otherwise. * - * @param array $options An list of option names as keys. + * @param array $options A list of option names as keys. * * @throws MissingOptionsException If a required option is missing. */ diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolverInterface.php b/src/Symfony/Component/OptionsResolver/OptionsResolverInterface.php index 8474c4bcd793d..29088a532eaec 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolverInterface.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolverInterface.php @@ -16,6 +16,31 @@ */ interface OptionsResolverInterface { + /** + * Throw an exception if unknown options are passed to {@link resolve()}. + */ + const FORBID_UNKNOWN = 1; + + /** + * Remove unknown options passed to {@link resolve()}. + */ + const REMOVE_UNKNOWN = 2; + + /** + * Ignore unknown options passed to {@link resolve()}. + */ + const IGNORE_UNKNOWN = 4; + + /** + * Throw an exception if a required option is missing in {@link resolve()}. + */ + const FORBID_MISSING = 32; + + /** + * Ignore missing required options in {@link resolve()}. + */ + const IGNORE_MISSING = 64; + /** * Sets default option values. * @@ -195,7 +220,9 @@ public function isRequired($option); /** * Returns the combination of the default and the passed options. * - * @param array $options The custom option values. + * @param array $options The custom option values. + * @param integer $flags A combination of the flag constants in this + * interface. * * @return array A list of options and their values. * @@ -206,5 +233,5 @@ public function isRequired($option); * @throws Exception\OptionDefinitionException If a cyclic dependency is detected * between two lazy options. */ - public function resolve(array $options = array()); + public function resolve(array $options = array(), $flags = 0); } diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index fc3b3fc5d38e3..3060ddc5b7f6b 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\OptionsResolver\Tests; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\Options; @@ -175,7 +176,7 @@ public function testResolveLazyReplaceDefaults() /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException */ - public function testResolveFailsIfNonExistingOption() + public function testResolveFailsIfUnknownOption() { $this->resolver->setDefaults(array( 'one' => '1', @@ -194,6 +195,57 @@ public function testResolveFailsIfNonExistingOption() )); } + public function testResolveRemovesUnknownOptionIfFlagIsSet() + { + $this->resolver->setDefaults(array( + 'one' => '1', + )); + + $this->resolver->setRequired(array( + 'two', + )); + + $this->resolver->setOptional(array( + 'three', + )); + + $options = $this->resolver->resolve(array( + 'foo' => 'bar', + 'two' => '20', + ), OptionsResolverInterface::REMOVE_UNKNOWN); + + $this->assertEquals(array( + 'one' => '1', + 'two' => '20' + ), $options); + } + + public function testResolveIgnoresUnknownOptionsIfFlagIsSet() + { + $this->resolver->setDefaults(array( + 'one' => '1', + )); + + $this->resolver->setRequired(array( + 'two', + )); + + $this->resolver->setOptional(array( + 'three', + )); + + $options = $this->resolver->resolve(array( + 'foo' => 'bar', + 'two' => '20', + ), OptionsResolverInterface::IGNORE_UNKNOWN); + + $this->assertEquals(array( + 'one' => '1', + 'foo' => 'bar', + 'two' => '20', + ), $options); + } + /** * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException */ @@ -212,6 +264,25 @@ public function testResolveFailsIfMissingRequiredOption() )); } + public function testResolveIgnoresMissingRequiredOptionIfFlagIsSet() + { + $this->resolver->setRequired(array( + 'one', + )); + + $this->resolver->setDefaults(array( + 'two' => '2', + )); + + $options = $this->resolver->resolve(array( + 'two' => '20', + ), OptionsResolverInterface::IGNORE_MISSING); + + $this->assertEquals(array( + 'two' => '20', + ), $options); + } + public function testResolveSucceedsIfOptionValueAllowed() { $this->resolver->setDefaults(array( @@ -717,4 +788,41 @@ public function testClone() 'three' => '3', ), $clone->resolve()); } + + public function testRemoveUnknownOption() + { + $this->resolver->setDefaults(array( + 'one' => 'default', + 'two' => 'default' + )); + + $options = $this->resolver->resolve(array( + 'one' => 'keep', + 'three' => 'remove' + ), OptionsResolverInterface::REMOVE_UNKNOWN); + + $this->assertEquals($options, array( + 'one' => 'keep', + 'two' => 'default' + )); + } + + public function testIgnoreUnknownOption() + { + $this->resolver->setDefaults(array( + 'one' => 'default', + 'two' => 'default' + )); + + $options = $this->resolver->resolve(array( + 'one' => 'keep', + 'three' => 'ignored' + ), OptionsResolverInterface::IGNORE_UNKNOWN); + + $this->assertEquals($options, array( + 'one' => 'keep', + 'two' => 'default', + 'three' => 'ignored' + )); + } }