Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[WIP] [OptionsResolver] handle nested options #18134

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

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[OptionsResolver] resolve extra options
closes #15524.

Set allowed types for many options or for a set of nested
or extra options.

  * add `Options::NONE`
  * add `Options::ALL`
  * add `Options::DEFINED`
  * add `Options::NESTED`
  * add `Options::EXTRA`
  * add `OptionsResolver::allowedValuesForAll`
  * add `OptionsResolver::allowedTypesForAll`
  * add `OptionsResolver::resolveUndefined`
  * add `OptionsResolver::addAllowedValuesForAll()`
  * add `OptionsResolver::addAllowedTypesForAll()`
  * add `OptionsResolver::setPrototype()`
  • Loading branch information
HeahDude committed Mar 14, 2016
commit 9e4e02a298c2b3acf9ea7ccefbaf2ab3d0f62902
5 changes: 5 additions & 0 deletions 5 src/Symfony/Component/OptionsResolver/Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@
*/
interface Options extends \ArrayAccess, \Countable
{
const NONE = 0;
const ALL = 1;
const DEFINED = 2;
const NESTED = 3;
const EXTRA = 4;
}
294 changes: 285 additions & 9 deletions 294 src/Symfony/Component/OptionsResolver/OptionsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,34 @@ class OptionsResolver implements Options
*/
private $allowedValues = array();

/**
* A list of accepted values for all options.
*
* @var array
*/
private $allowedValuesForAll = array();

/**
* A list of accepted types for each option.
*
* @var array
*/
private $allowedTypes = array();

/**
* A list of accepted types for all options.
*
* @var array
*/
private $allowedTypesForAll = array();

/**
* Whether to resolve undefined options.
*
* @var bool
*/
private $resolveUndefined = false;

/**
* A list of closures for evaluating lazy options.
*
Expand Down Expand Up @@ -558,7 +579,7 @@ public function setAllowedValues($option, $allowedValues)
return $this;
}

if (!isset($this->defined[$option])) {
if (!isset($this->defined[$option]) && !$this->resolveUndefined && Options::EXTRA !== $option) {
throw new UndefinedOptionsException(sprintf(
'The option "%s" does not exist. Defined options are: "%s".',
$option,
Expand Down Expand Up @@ -609,7 +630,7 @@ public function addAllowedValues($option, $allowedValues)
return $this;
}

if (!isset($this->defined[$option])) {
if (!isset($this->defined[$option]) && !$this->resolveUndefined && Options::EXTRA !== $option) {
throw new UndefinedOptionsException(sprintf(
'The option "%s" does not exist. Defined options are: "%s".',
$option,
Expand All @@ -633,6 +654,98 @@ public function addAllowedValues($option, $allowedValues)
return $this;
}

/**
* Adds allowed values for one or more options.
*
* First argument may be a name or an array of option names.
*
* You can pass a constant of Options interface as first argument to target
* a group of options. Supported values are:
*
* - Options::ALL Defined and extra options
* - Options::DEFINED
* - Options::NESTED
* - Options::EXTRA
*
* If a nested option name is passed it will apply to all nested options.
* You can prevent it by passing Options::NONE as fourth argument.
*
* Instead of passing values, you may also pass a closures with the
* following signature:
*
* function ($value) {
* // return true or false
* }
*
* The closure receives the value as argument and should return true to
* accept the value and false to reject the value.
*
* @param string|string[]|int $optionNames One or more option names
* or an Options constant
* @param mixed $allowedValues One or more accepted values
* or closures
* @param bool $replace Whether to replace previous
* values
* @param int $nested This method is recursive for
* nested options which is the
* default. Pass Options::NONE
* to change it.
*
* @return OptionsResolver This instance
*
* @throws UndefinedOptionsException If an option is undefined
* @throws AccessException If called from a lazy option or normalizer
*/
public function addAllowedValuesForAll($optionNames, $allowedValues, $replace = false, $nested = Options::ALL)
{
if ($this->locked) {
throw new AccessException('Allowed types cannot be added from a lazy option or normalizer.');
}

switch ($optionNames) {
case Options::ALL:
// Use this values for all defined and extra options
// except nested which are always arrays.
$this->allowedValuesForAll = $replace
? $allowedValues : array_merge($this->allowedValuesForAll, $allowedValues);

// If not recursive return
if (Options::NONE === $nested) {
return $this;
}

$optionNames = $this->getNestedOptions();
break;
case Options::NESTED:
$optionNames = $this->getNestedOptions();
break;
case Options::DEFINED:
$optionNames = $this->getDefinedOptions();
break;
case Options::NONE:
// Not supported
return $this;
default:
// A custom array of option names or
// one option name or Options::EXTRA
$optionNames = (array) $optionNames;
}

foreach ($optionNames as $option) {
if ($this->isNested($option) && Options::NONE !== $nested) {
$this->nested[$option]->addAllowedValuesForAll($nested, $allowedValues, $replace);
} else {
if ($replace) {
$this->setAllowedValues($option, $allowedValues);
} else {
$this->addAllowedValues($option, $allowedValues);
}
}
}

return $this;
}

/**
* Sets allowed types for an option.
*
Expand Down Expand Up @@ -660,7 +773,7 @@ public function setAllowedTypes($option, $allowedTypes)
return $this;
}

if (!isset($this->defined[$option])) {
if (!isset($this->defined[$option]) && !$this->resolveUndefined && Options::EXTRA !== $option) {
throw new UndefinedOptionsException(sprintf(
'The option "%s" does not exist. Defined options are: "%s".',
$option,
Expand Down Expand Up @@ -705,7 +818,7 @@ public function addAllowedTypes($option, $allowedTypes)
return $this;
}

if (!isset($this->defined[$option])) {
if (!isset($this->defined[$option]) && !$this->resolveUndefined && Options::EXTRA !== $option) {
throw new UndefinedOptionsException(sprintf(
'The option "%s" does not exist. Defined options are: "%s".',
$option,
Expand All @@ -725,6 +838,147 @@ public function addAllowedTypes($option, $allowedTypes)
return $this;
}

/**
* Adds allowed types for one or more options.
*
* First argument may be a name or an array of option names.
*
* You can pass a constant of Options interface as first argument to target
* a group of options. Supported values are:
*
* - Options::ALL Defined and extra options
* - Options::DEFINED
* - Options::NESTED
*
* If a nested option name is passed it will apply to all nested options.
* You can prevent it by passing Options::NONE as fourth argument.
*
* Any type for which a corresponding is_<type>() function exists is
* acceptable. Additionally, fully-qualified class or interface names may
* be passed.
*
* @param string|string[]|int $optionNames One or more option names
* or Options::ALL
* @param string|string[] $allowedTypes One or more accepted types
* @param bool $replace Whether to replace previous
* value
* @param int $nested This method is recursive for
* nested options which is the
* default. Pass Options::NONE
* to change it.
*
* @return OptionsResolver This instance
*
* @throws UndefinedOptionsException If an option is undefined
* @throws AccessException If called from a lazy option or normalizer
*/
public function addAllowedTypesForAll($optionNames, $allowedTypes, $replace = false, $nested = Options::ALL)
{
if ($this->locked) {
throw new AccessException('Allowed types cannot be added from a lazy option or normalizer.');
}

switch ($optionNames) {
case Options::ALL:
// Use this values for all defined and extra options
// except nested which are always arrays.
$this->allowedTypesForAll = $replace
? array_merge($this->allowedTypesForAll, $allowedTypes) : $allowedTypes;

// If not recursive return
if (Options::NONE === $nested) {
return $this;
}

$optionNames = $this->getNestedOptions();
break;
case Options::NESTED:
$optionNames = $this->getNestedOptions();
break;
case Options::DEFINED:
$optionNames = $this->getDefinedOptions();
break;
case Options::NONE:
// Not supported
return $this;
default:
// A custom array of option names or
// one option name or Options::EXTRA
$optionNames = (array) $optionNames;
}

foreach ($optionNames as $option) {
if ($this->isNested($option) && Options::NONE !== $nested) {
$this->nested[$option]->addAllowedTypesForAll($nested, $allowedTypes, $replace);
} else {
if ($replace) {
$this->setAllowedTypes($option, $allowedTypes);
} else {
$this->addAllowedTypes($option, $allowedTypes);
}
}
}

return $this;
}

/**
* Add a prototype for extra or all options.
*
* If this instance was not accepting extra options before, this method
* allows it by default.
*
* First argument is an allowed type and second is one more values/closures.
* By default it only applies on undefined options, to validate previous defaults
* as well, pass Options::ALL as third argument.
*
* This method overrides previous allowed type and values for concerned options.
*
* $resolver->setNested('emails')
* ->setPrototype(array(
* 'string' => function ($email) use ($regex) {
* return preg_match($regex, $email);
* },
* );
*
* @param string $type A string defining an allowed type
* @param mixed $values One or more allowed values/closures
* @param int $options Options::EXTRA by default, but you can pass
* Options::ALL to apply the rule on previous defaults
*
* @return OptionsResolver This instance
*
* @throws UndefinedOptionsException If an option is undefined
* @throws AccessException If called from a lazy option or normalizer
*/
public function setPrototype($type, $values, $options = Options::EXTRA)
{
if ($this->locked) {
throw new AccessException('Allowed types cannot be added from a lazy option or normalizer.');
}

$this->resolveUndefined = true;

$this->addAllowedTypesForAll($options, $type, true, Options::NONE);
$this->addAllowedValuesForAll($options, $values, true, Options::NONE);

return $this;
}

/**
* Defines whether undefined options should be resolved.
*
* @param bool $allow Whether to resolve undefined options
*
* @return OptionsResolver This instance
*/
public function allowExtraOptions($allow = true)
{
$this->resolveUndefined = $allow;

return $this;
}

/**
* Removes the option with the given name.
*
Expand Down Expand Up @@ -813,7 +1067,7 @@ public function resolve(array $options = array())
// Make sure that no unknown options are passed
$diff = array_diff_key($options, $clone->defined);

if (count($diff) > 0) {
if (count($diff) > 0 && false === $this->resolveUndefined) {
ksort($clone->defined);
ksort($diff);

Expand Down Expand Up @@ -939,10 +1193,21 @@ public function offsetGet($option)
}

// Validate the type of the resolved option
if (isset($this->allowedTypes[$option])) {
if (isset($this->allowedTypes[$option])
|| ($this->allowedTypesForAll && false === $this->isNested($option))
|| $extra = (isset($this->allowedTypes[Options::EXTRA]) && false === $this->isDefined($option))
) {
$valid = false;

foreach ($this->allowedTypes[$option] as $type) {
if (isset($extra) && $extra && $this->resolveUndefined) {
$allowedTypes = $this->allowedTypes[Options::EXTRA];
} else {
$allowedTypes = isset($this->allowedTypes[$option]) ? $this->allowedTypes[$option] : array();
}

$allowedTypes = array_unique(array_merge($allowedTypes, $this->allowedTypesForAll));

foreach ($allowedTypes as $type) {
$type = isset(self::$typeAliases[$type]) ? self::$typeAliases[$type] : $type;

if (function_exists($isFunction = 'is_'.$type)) {
Expand Down Expand Up @@ -973,11 +1238,22 @@ public function offsetGet($option)
}

// Validate the value of the resolved option
if (isset($this->allowedValues[$option])) {
if (isset($this->allowedValues[$option])
|| ($this->allowedValuesForAll && false === $this->isNested($option))
|| $extra = (isset($this->allowedValues[Options::EXTRA]) && false === $this->isDefined($option))
) {
$success = false;
$printableAllowedValues = array();

foreach ($this->allowedValues[$option] as $allowedValue) {
if (isset($extra) && $extra && $this->resolveUndefined) {
$allowedValues = $this->allowedTypes[Options::EXTRA];
} else {
$allowedValues = isset($this->allowedValues[$option]) ? $this->allowedValues[$option] : array();
}

$allowedValues = array_merge($allowedValues, $this->allowedValuesForAll);

foreach ($allowedValues as $allowedValue) {
if ($allowedValue instanceof \Closure) {
if ($allowedValue($value)) {
$success = true;
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.