diff --git a/src/Symfony/Component/OptionsResolver/CHANGELOG.md b/src/Symfony/Component/OptionsResolver/CHANGELOG.md index 791a402faaf3a..36a524df5ddc9 100644 --- a/src/Symfony/Component/OptionsResolver/CHANGELOG.md +++ b/src/Symfony/Component/OptionsResolver/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.3 +--- + + * Add `OptionsResolver::ignoreUndefined()` to ignore not defined options while resolving + 6.0 --- diff --git a/src/Symfony/Component/OptionsResolver/OptionConfigurator.php b/src/Symfony/Component/OptionsResolver/OptionConfigurator.php index 37d5138444da0..de98db012b2f5 100644 --- a/src/Symfony/Component/OptionsResolver/OptionConfigurator.php +++ b/src/Symfony/Component/OptionsResolver/OptionConfigurator.php @@ -134,4 +134,16 @@ public function info(string $info): static return $this; } + + /** + * Sets whether ignore undefined options. + * + * @return $this + */ + public function ignoreUndefined(bool $ignore = true): static + { + $this->resolver->setIgnoreUndefined($ignore); + + return $this; + } } diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index b4298121761e4..724b121dd4075 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -140,6 +140,11 @@ class OptionsResolver implements Options */ private $prototypeIndex; + /** + * Whether to ignore undefined options. + */ + private bool $ignoreUndefined = false; + /** * Sets the default value of a given option. * @@ -862,7 +867,7 @@ public function resolve(array $options = []): array $clone = clone $this; // Make sure that no unknown options are passed - $diff = array_diff_key($options, $clone->defined); + $diff = $this->ignoreUndefined ? [] : array_diff_key($options, $clone->defined); if (\count($diff) > 0) { ksort($clone->defined); @@ -873,6 +878,10 @@ public function resolve(array $options = []): array // Override options set by the user foreach ($options as $option => $value) { + if ($this->ignoreUndefined && !isset($clone->defined[$option])) { + continue; + } + $clone->given[$option] = true; $clone->defaults[$option] = $value; unset($clone->resolved[$option], $clone->lazy[$option]); @@ -1210,6 +1219,18 @@ public function count(): int return \count($this->defaults); } + /** + * Sets whether ignore undefined options. + * + * @return $this + */ + public function setIgnoreUndefined(bool $ignore = true): static + { + $this->ignoreUndefined = $ignore; + + return $this; + } + /** * Returns a string representation of the value. * diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 7d60358fa78ef..7e56d38c47969 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -36,6 +36,28 @@ protected function setUp(): void $this->resolver = new OptionsResolver(); } + /** + * @dataProvider provideResolveWithIgnoreUndefined + */ + public function testResolveWithIgnoreUndefined(array $defaults, array $options, array $expected) + { + $this->resolver + ->setDefaults($defaults) + ->setIgnoreUndefined(); + + $this->assertSame($expected, $this->resolver->resolve($options)); + } + + public static function provideResolveWithIgnoreUndefined(): array + { + return [ + 'no defaults options, undefined resolves empty' => [[], ['c' => 4, 'd' => 5], []], + 'empty options resolves defaults' => [['a' => '1', 'b' => '2'], [], ['a' => '1', 'b' => '2']], + 'undefined options resolves defaults' => [['a' => '1', 'b' => '2'], ['c' => 3, 'd' => 4], ['a' => '1', 'b' => '2']], + 'resolves defined' => [['a' => '1', 'b' => '2'], ['a' => '10', 'c' => '3'], ['b' => '2', 'a' => '10']], + ]; + } + public function testResolveFailsIfNonExistingOption() { $this->expectException(UndefinedOptionsException::class);