10000 [OptionsResolver] Add a new method addNormalizer and normalization hierarchy by yceruto · Pull Request #30371 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[OptionsResolver] Add a new method addNormalizer and normalization hierarchy #30371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension
8000
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Symfony/Component/OptionsResolver/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

4.3.0
-----

* added `OptionsResolver::addNormalizer` method

4.2.0
-----

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
59 changes: 54 additions & 5 deletions src/Symfony/Component/OptionsResolver/OptionsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class OptionsResolver implements Options
/**
* A list of normalizer closures.
*
* @var \Closure[]
* @var \Closure[][]
*/
private $normalizers = [];

Expand Down Expand Up @@ -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]);
Expand Down Expand Up @@ -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]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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']));
Expand Down
0