Skip to content

Commit

Permalink
feature #33848 [OptionsResolver] Add new OptionConfigurator class to …
Browse files Browse the repository at this point in the history
…define options with fluent interface (lmillucci, yceruto)

This PR was merged into the 5.1-dev branch.

Discussion
----------

[OptionsResolver] Add new OptionConfigurator class to define options with fluent interface

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | Fix #33735
| License       | MIT
| Doc PR        |  symfony/symfony-docs#12426

- [x] submit changes to the documentation

This PR adds OptionConfigurator to the OptionsResolver

Commits
-------

1ff5640 [OptionsResolver] Add new OptionConfigurator class to define options with fluent interface
  • Loading branch information
nicolas-grekas committed Feb 6, 2020
2 parents 1c28bf7 + 1ff5640 commit 4e659ca
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/Symfony/Component/OptionsResolver/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========

5.1.0
-----

* added fluent configuration of options using `OptionResolver::define()`

5.0.0
-----

Expand Down
127 changes: 127 additions & 0 deletions src/Symfony/Component/OptionsResolver/OptionConfigurator.php
@@ -0,0 +1,127 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\OptionsResolver;

use Symfony\Component\OptionsResolver\Exception\AccessException;

final class OptionConfigurator
{
private $name;
private $resolver;

public function __construct(string $name, OptionsResolver $resolver)
{
$this->name = $name;
$this->resolver = $resolver;
$this->resolver->setDefined($name);
}

/**
* Adds allowed types for this option.
*
* @param string ...$types One or more accepted types
*
* @return $this
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function allowedTypes(string ...$types): self
{
$this->resolver->setAllowedTypes($this->name, $types);

return $this;
}

/**
* Sets allowed values for this option.
*
* @param mixed ...$values One or more acceptable values/closures
*
* @return $this
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function allowedValues(...$values): self
{
$this->resolver->setAllowedValues($this->name, $values);

return $this;
}

/**
* Sets the default value for this option.
*
* @param mixed $value The default value of the option
*
* @return $this
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function default($value): self
{
$this->resolver->setDefault($this->name, $value);

return $this;
}

/**
* Defines an option configurator with the given name.
*/
public function define(string $option): self
{
return $this->resolver->define($option);
}

/**
* Marks this option as deprecated.
*
* @return $this
*
* @param string|\Closure $deprecationMessage
*/
public function deprecated($deprecationMessage = 'The option "%name%" is deprecated.'): self
{
$this->resolver->setDeprecated($this->name, $deprecationMessage);

return $this;
}

/**
* Sets the normalizer for this option.
*
* @param \Closure $normalizer The normalizer
*
* @return $this
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function normalize(\Closure $normalizer): self
{
$this->resolver->setNormalizer($this->name, $normalizer);

return $this;
}

/**
* Marks this option as required.
*
* @return $this
*
* @throws AccessException If called from a lazy option or normalizer
*/
public function required(): self
{
$this->resolver->setRequired($this->name);

return $this;
}
}
14 changes: 13 additions & 1 deletion src/Symfony/Component/OptionsResolver/OptionsResolver.php
Expand Up @@ -703,6 +703,18 @@ public function addAllowedTypes(string $option, $allowedTypes)
return $this;
}

/**
* Defines an option configurator with the given name.
*/
public function define(string $option): OptionConfigurator
{
if (isset($this->defined[$option])) {
throw new OptionDefinitionException(sprintf('The options "%s" is already defined.', $option));
}

return new OptionConfigurator($option, $this);
}

/**
* Removes the option with the given name.
*
Expand Down Expand Up @@ -830,7 +842,7 @@ public function resolve(array $options = [])
* Returns the resolved value of an option.
*
* @param string $option The option name
* @param bool $triggerDeprecation Whether to trigger the deprecation or not
* @param bool $triggerDeprecation Whether to trigger the deprecation or not (true by default)
*
* @return mixed The option value
*
Expand Down
Expand Up @@ -13,6 +13,7 @@

use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase;
use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
Expand Down Expand Up @@ -2378,4 +2379,35 @@ public function testAccessToParentOptionFromNestedNormalizerAndLazyOption()
];
$this->assertSame($expectedOptions, $actualOptions);
}

public function testFailsIfOptionIsAlreadyDefined()
{
$this->expectException('Symfony\Component\OptionsResolver\Exception\OptionDefinitionException');
$this->expectExceptionMessage('The options "foo" is already defined.');
$this->resolver->define('foo');
$this->resolver->define('foo');
}

public function testResolveOptionsDefinedByOptionConfigurator()
{
$this->resolver->define('foo')
->required()
->deprecated()
->default('bar')
->allowedTypes('string', 'bool')
->allowedValues('bar', 'zab')
->normalize(static function (Options $options, $value) {
return $value;
})
;
$introspector = new OptionsResolverIntrospector($this->resolver);

$this->assertTrue(true, $this->resolver->isDefined('foo'));
$this->assertTrue(true, $this->resolver->isDeprecated('foo'));
$this->assertTrue(true, $this->resolver->hasDefault('foo'));
$this->assertSame('bar', $introspector->getDefault('foo'));
$this->assertSame(['string', 'bool'], $introspector->getAllowedTypes('foo'));
$this->assertSame(['bar', 'zab'], $introspector->getAllowedValues('foo'));
$this->assertCount(1, $introspector->getNormalizers('foo'));
}
}

0 comments on commit 4e659ca

Please sign in to comment.