diff --git a/src/Symfony/Component/OptionsResolver/CHANGELOG.md b/src/Symfony/Component/OptionsResolver/CHANGELOG.md index a6f8f0ff47c03..60ea43f7ec20c 100644 --- a/src/Symfony/Component/OptionsResolver/CHANGELOG.md +++ b/src/Symfony/Component/OptionsResolver/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.3.0 +----- + + * added `OptionsResolver::addNormalizer` method + 4.2.0 ----- diff --git a/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php b/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php index ccce082fed29d..9ce5263334e15 100644 --- a/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php +++ b/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php @@ -84,6 +84,14 @@ public function getAllowedValues(string $option): array * @throws NoConfigurationException on no configured normalizer */ public function getNormalizer(string $option): \Closure + { + return current($this->getNormalizers($option)); + } + + /** + * @throws NoConfigurationException when no normalizer is configured + */ + public function getNormalizers(string $option): array { return ($this->get)('normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option)); } diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index eb0a6c2480601..0e68e75ff83ce 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -57,7 +57,7 @@ class OptionsResolver implements Options /** * A list of normalizer closures. * - * @var \Closure[] + * @var \Closure[][] */ private $normalizers = []; @@ -484,7 +484,56 @@ public function setNormalizer($option, \Closure $normalizer) throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined)))); } - $this->normalizers[$option] = $normalizer; + $this->normalizers[$option] = [$normalizer]; + + // Make sure the option is processed + unset($this->resolved[$option]); + + return $this; + } + + /** + * Adds a normalizer for an option. + * + * The normalizer should be a closure with the following signature: + * + * function (Options $options, $value): mixed { + * // ... + * } + * + * The closure is invoked when {@link resolve()} is called. The closure + * has access to the resolved values of other options through the passed + * {@link Options} instance. + * + * The second parameter passed to the closure is the value of + * the option. + * + * The resolved option value is set to the return value of the closure. + * + * @param string $option The option name + * @param \Closure $normalizer The normalizer + * @param bool $forcePrepend If set to true, prepend instead of appending + * + * @return $this + * + * @throws UndefinedOptionsException If the option is undefined + * @throws AccessException If called from a lazy option or normalizer + */ + public function addNormalizer(string $option, \Closure $normalizer, bool $forcePrepend = false): self + { + if ($this->locked) { + throw new AccessException('Normalizers cannot be set from a lazy option or normalizer.'); + } + + if (!isset($this->defined[$option])) { + throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined)))); + } + + if ($forcePrepend) { + array_unshift($this->normalizers[$option], $normalizer); + } else { + $this->normalizers[$option][] = $normalizer; + } // Make sure the option is processed unset($this->resolved[$option]); @@ -966,15 +1015,15 @@ public function offsetGet($option/*, bool $triggerDeprecation = true*/) throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling)))); } - $normalizer = $this->normalizers[$option]; - // The following section must be protected from cyclic // calls. Set $calling for the current $option to detect a cyclic // dependency // BEGIN $this->calling[$option] = true; try { - $value = $normalizer($this, $value); + foreach ($this->normalizers[$option] as $normalizer) { + $value = $normalizer($this, $value); + } } finally { unset($this->calling[$option]); } diff --git a/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php b/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php index c6615573cd881..64a1ead1fe014 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php @@ -201,6 +201,42 @@ public function testGetNormalizerThrowsOnNotDefinedOption() $this->assertSame('bar', $debug->getNormalizer('foo')); } + public function testGetNormalizers() + { + $resolver = new OptionsResolver(); + $resolver->setDefined('foo'); + $resolver->addNormalizer('foo', $normalizer1 = function () {}); + $resolver->addNormalizer('foo', $normalizer2 = function () {}); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame([$normalizer1, $normalizer2], $debug->getNormalizers('foo')); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException + * @expectedExceptionMessage No normalizer was set for the "foo" option. + */ + public function testGetNormalizersThrowsOnNoConfiguredValue() + { + $resolver = new OptionsResolver(); + $resolver->setDefined('foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $debug->getNormalizers('foo'); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + * @expectedExceptionMessage The option "foo" does not exist. + */ + public function testGetNormalizersThrowsOnNotDefinedOption() + { + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $debug->getNormalizers('foo'); + } + public function testGetDeprecationMessage() { $resolver = new OptionsResolver(); diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 886a88ebfa66c..edf53cd2e2565 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -1554,6 +1554,63 @@ public function testNormalizerNotCalledForUnsetOptions() $this->assertEmpty($this->resolver->resolve()); } + public function testAddNormalizerReturnsThis() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame($this->resolver, $this->resolver->addNormalizer('foo', function () {})); + } + + public function testAddNormalizerClosure() + { + // defined by superclass + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return '1st-normalized-'.$value; + }); + // defined by subclass + $this->resolver->addNormalizer('foo', function (Options $options, $value) { + return '2nd-normalized-'.$value; + }); + + $this->assertEquals(['foo' => '2nd-normalized-1st-normalized-bar'], $this->resolver->resolve()); + } + + public function testForcePrependNormalizerClosure() + { + // defined by superclass + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return '2nd-normalized-'.$value; + }); + // defined by subclass + $this->resolver->addNormalizer('foo', function (Options $options, $value) { + return '1st-normalized-'.$value; + }, true); + + $this->assertEquals(['foo' => '2nd-normalized-1st-normalized-bar'], $this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + */ + public function testAddNormalizerFailsIfUnknownOption() + { + $this->resolver->addNormalizer('foo', function () {}); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfAddNormalizerFromLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->addNormalizer('foo', function () {}); + }); + + $this->resolver->resolve(); + } + public function testSetDefaultsReturnsThis() { $this->assertSame($this->resolver, $this->resolver->setDefaults(['foo', 'bar'])); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy