Upgrade 1-11.38

This commit is contained in:
xesmyd
2026-03-30 14:10:30 +02:00
parent f2a7e6d1fc
commit ac648ef29d
24665 changed files with 69682 additions and 2205004 deletions
+6
View File
@@ -1,6 +1,12 @@
CHANGELOG
=========
3.4.0
-----
* added `OptionsResolverIntrospector` to inspect options definitions inside an `OptionsResolver` instance
* added array of types support in allowed types (e.g int[])
2.6.0
-----
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2004-2018 Fabien Potencier
Copyright (c) 2004-2020 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+168 -240
View File
@@ -24,63 +24,56 @@ use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Tobias Schultze <http://tobion.de>
*/
class OptionsResolver implements Options, OptionsResolverInterface
class OptionsResolver implements Options
{
/**
* The fully qualified name of the {@link Options} interface.
*
* @internal
*/
const OPTIONS_INTERFACE = 'Symfony\\Component\\OptionsResolver\\Options';
/**
* The names of all defined options.
*/
private $defined = array();
private $defined = [];
/**
* The default option values.
*/
private $defaults = array();
private $defaults = [];
/**
* The names of required options.
*/
private $required = array();
private $required = [];
/**
* The resolved option values.
*/
private $resolved = array();
private $resolved = [];
/**
* A list of normalizer closures.
*
* @var \Closure[]
*/
private $normalizers = array();
private $normalizers = [];
/**
* A list of accepted values for each option.
*/
private $allowedValues = array();
private $allowedValues = [];
/**
* A list of accepted types for each option.
*/
private $allowedTypes = array();
private $allowedTypes = [];
/**
* A list of closures for evaluating lazy options.
*/
private $lazy = array();
private $lazy = [];
/**
* A list of lazy options whose closure is currently being called.
*
* This list helps detecting circular dependencies between lazy options.
*/
private $calling = array();
private $calling = [];
/**
* Whether the instance is locked for reading.
@@ -92,11 +85,11 @@ class OptionsResolver implements Options, OptionsResolverInterface
*/
private $locked = false;
private static $typeAliases = array(
private static $typeAliases = [
'boolean' => 'bool',
'integer' => 'int',
'double' => 'float',
);
];
/**
* Sets the default value of a given option.
@@ -153,7 +146,7 @@ class OptionsResolver implements Options, OptionsResolverInterface
$reflClosure = new \ReflectionFunction($value);
$params = $reflClosure->getParameters();
if (isset($params[0]) && null !== ($class = $params[0]->getClass()) && self::OPTIONS_INTERFACE === $class->name) {
if (isset($params[0]) && Options::class === $this->getParameterClassName($params[0])) {
// Initialize the option if no previous value exists
if (!isset($this->defaults[$option])) {
$this->defaults[$option] = null;
@@ -161,7 +154,7 @@ class OptionsResolver implements Options, OptionsResolverInterface
// Ignore previous lazy options if the closure has no second parameter
if (!isset($this->lazy[$option]) || !isset($params[1])) {
$this->lazy[$option] = array();
$this->lazy[$option] = [];
}
// Store closure for later evaluation
@@ -182,7 +175,7 @@ class OptionsResolver implements Options, OptionsResolverInterface
// to resolve options with lazy closures, normalizers or validation
// rules, none of which can exist for undefined options
// If the option was resolved before, update the resolved value
if (!isset($this->defined[$option]) || array_key_exists($option, $this->resolved)) {
if (!isset($this->defined[$option]) || \array_key_exists($option, $this->resolved)) {
$this->resolved[$option] = $value;
}
@@ -222,7 +215,7 @@ class OptionsResolver implements Options, OptionsResolverInterface
*/
public function hasDefault($option)
{
return array_key_exists($option, $this->defaults);
return \array_key_exists($option, $this->defaults);
}
/**
@@ -287,7 +280,7 @@ class OptionsResolver implements Options, OptionsResolverInterface
*/
public function isMissing($option)
{
return isset($this->required[$option]) && !array_key_exists($option, $this->defaults);
return isset($this->required[$option]) && !\array_key_exists($option, $this->defaults);
}
/**
@@ -399,30 +392,6 @@ class OptionsResolver implements Options, OptionsResolverInterface
return $this;
}
/**
* Sets the normalizers for an array of options.
*
* @param array $normalizers An array of closures
*
* @return $this
*
* @throws UndefinedOptionsException If the option is undefined
* @throws AccessException If called from a lazy option or normalizer
*
* @see setNormalizer()
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function setNormalizers(array $normalizers)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use setNormalizer() instead.', E_USER_DEPRECATED);
foreach ($normalizers as $option => $normalizer) {
$this->setNormalizer($option, $normalizer);
}
return $this;
}
/**
* Sets allowed values for an option.
*
@@ -444,28 +413,17 @@ class OptionsResolver implements Options, OptionsResolverInterface
* @throws UndefinedOptionsException If the option is undefined
* @throws AccessException If called from a lazy option or normalizer
*/
public function setAllowedValues($option, $allowedValues = null)
public function setAllowedValues($option, $allowedValues)
{
if ($this->locked) {
throw new AccessException('Allowed values cannot be set from a lazy option or normalizer.');
}
// BC
if (\is_array($option) && null === $allowedValues) {
@trigger_error('Calling the '.__METHOD__.' method with an array of options is deprecated since Symfony 2.6 and will be removed in 3.0. Use the new signature with a single option instead.', E_USER_DEPRECATED);
foreach ($option as $optionName => $optionValues) {
$this->setAllowedValues($optionName, $optionValues);
}
return $this;
}
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))));
}
$this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : array($allowedValues);
$this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : [$allowedValues];
// Make sure the option is processed
unset($this->resolved[$option]);
@@ -496,29 +454,18 @@ class OptionsResolver implements Options, OptionsResolverInterface
* @throws UndefinedOptionsException If the option is undefined
* @throws AccessException If called from a lazy option or normalizer
*/
public function addAllowedValues($option, $allowedValues = null)
public function addAllowedValues($option, $allowedValues)
{
if ($this->locked) {
throw new AccessException('Allowed values cannot be added from a lazy option or normalizer.');
}
// BC
if (\is_array($option) && null === $allowedValues) {
@trigger_error('Calling the '.__METHOD__.' method with an array of options is deprecated since Symfony 2.6 and will be removed in 3.0. Use the new signature with a single option instead.', E_USER_DEPRECATED);
foreach ($option as $optionName => $optionValues) {
$this->addAllowedValues($optionName, $optionValues);
}
return $this;
}
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 (!\is_array($allowedValues)) {
$allowedValues = array($allowedValues);
$allowedValues = [$allowedValues];
}
if (!isset($this->allowedValues[$option])) {
@@ -548,23 +495,12 @@ class OptionsResolver implements Options, OptionsResolverInterface
* @throws UndefinedOptionsException If the option is undefined
* @throws AccessException If called from a lazy option or normalizer
*/
public function setAllowedTypes($option, $allowedTypes = null)
public function setAllowedTypes($option, $allowedTypes)
{
if ($this->locked) {
throw new AccessException('Allowed types cannot be set from a lazy option or normalizer.');
}
// BC
if (\is_array($option) && null === $allowedTypes) {
@trigger_error('Calling the '.__METHOD__.' method with an array of options is deprecated since Symfony 2.6 and will be removed in 3.0. Use the new signature with a single option instead.', E_USER_DEPRECATED);
foreach ($option as $optionName => $optionTypes) {
$this->setAllowedTypes($optionName, $optionTypes);
}
return $this;
}
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))));
}
@@ -594,23 +530,12 @@ class OptionsResolver implements Options, OptionsResolverInterface
* @throws UndefinedOptionsException If the option is undefined
* @throws AccessException If called from a lazy option or normalizer
*/
public function addAllowedTypes($option, $allowedTypes = null)
public function addAllowedTypes($option, $allowedTypes)
{
if ($this->locked) {
throw new AccessException('Allowed types cannot be added from a lazy option or normalizer.');
}
// BC
if (\is_array($option) && null === $allowedTypes) {
@trigger_error('Calling the '.__METHOD__.' method with an array of options is deprecated since Symfony 2.6 and will be removed in 3.0. Use the new signature with a single option instead.', E_USER_DEPRECATED);
foreach ($option as $optionName => $optionTypes) {
$this->addAllowedTypes($optionName, $optionTypes);
}
return $this;
}
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))));
}
@@ -665,14 +590,14 @@ class OptionsResolver implements Options, OptionsResolverInterface
throw new AccessException('Options cannot be cleared from a lazy option or normalizer.');
}
$this->defined = array();
$this->defaults = array();
$this->required = array();
$this->resolved = array();
$this->lazy = array();
$this->normalizers = array();
$this->allowedTypes = array();
$this->allowedValues = array();
$this->defined = [];
$this->defaults = [];
$this->required = [];
$this->resolved = [];
$this->lazy = [];
$this->normalizers = [];
$this->allowedTypes = [];
$this->allowedValues = [];
return $this;
}
@@ -701,7 +626,7 @@ class OptionsResolver implements Options, OptionsResolverInterface
* @throws NoSuchOptionException If a lazy option reads an unavailable option
* @throws AccessException If called from a lazy option or normalizer
*/
public function resolve(array $options = array())
public function resolve(array $options = [])
{
if ($this->locked) {
throw new AccessException('Options cannot be resolved from a lazy option or normalizer.');
@@ -769,12 +694,12 @@ class OptionsResolver implements Options, OptionsResolverInterface
}
// Shortcut for resolved options
if (array_key_exists($option, $this->resolved)) {
if (\array_key_exists($option, $this->resolved)) {
return $this->resolved[$option];
}
// Check whether the option is set at all
if (!array_key_exists($option, $this->defaults)) {
if (!\array_key_exists($option, $this->defaults)) {
if (!isset($this->defined[$option])) {
throw new NoSuchOptionException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
}
@@ -801,48 +726,49 @@ class OptionsResolver implements Options, OptionsResolverInterface
foreach ($this->lazy[$option] as $closure) {
$value = $closure($this, $value);
}
} catch (\Exception $e) {
} finally {
unset($this->calling[$option]);
throw $e;
} catch (\Throwable $e) {
unset($this->calling[$option]);
throw $e;
}
unset($this->calling[$option]);
// END
}
// Validate the type of the resolved option
if (isset($this->allowedTypes[$option])) {
$valid = false;
$valid = true;
$invalidTypes = [];
foreach ($this->allowedTypes[$option] as $type) {
$type = isset(self::$typeAliases[$type]) ? self::$typeAliases[$type] : $type;
if (\function_exists($isFunction = 'is_'.$type)) {
if ($isFunction($value)) {
$valid = true;
break;
}
continue;
}
if ($value instanceof $type) {
$valid = true;
if ($valid = $this->verifyTypes($type, $value, $invalidTypes)) {
break;
}
}
if (!$valid) {
throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), $this->formatTypeOf($value)));
$fmtActualValue = $this->formatValue($value);
$fmtAllowedTypes = implode('" or "', $this->allowedTypes[$option]);
$fmtProvidedTypes = implode('|', array_keys($invalidTypes));
$allowedContainsArrayType = \count(array_filter(
$this->allowedTypes[$option],
function ($item) {
return '[]' === substr(isset(self::$typeAliases[$item]) ? self::$typeAliases[$item] : $item, -2);
}
)) > 0;
if (\is_array($value) && $allowedContainsArrayType) {
throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $option, $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes));
}
throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $option, $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes));
}
}
// Validate the value of the resolved option
if (isset($this->allowedValues[$option])) {
$success = false;
$printableAllowedValues = array();
$printableAllowedValues = [];
foreach ($this->allowedValues[$option] as $allowedValue) {
if ($allowedValue instanceof \Closure) {
@@ -853,7 +779,9 @@ class OptionsResolver implements Options, OptionsResolverInterface
// Don't include closures in the exception message
continue;
} elseif ($value === $allowedValue) {
}
if ($value === $allowedValue) {
$success = true;
break;
}
@@ -896,14 +824,9 @@ class OptionsResolver implements Options, OptionsResolverInterface
$this->calling[$option] = true;
try {
$value = $normalizer($this, $value);
} catch (\Exception $e) {
} finally {
unset($this->calling[$option]);
throw $e;
} catch (\Throwable $e) {
unset($this->calling[$option]);
throw $e;
}
unset($this->calling[$option]);
// END
}
@@ -913,6 +836,65 @@ class OptionsResolver implements Options, OptionsResolverInterface
return $value;
}
/**
* @param string $type
* @param mixed $value
* @param array &$invalidTypes
*
* @return bool
*/
private function verifyTypes($type, $value, array &$invalidTypes)
{
if (\is_array($value) && '[]' === substr($type, -2)) {
return $this->verifyArrayType($type, $value, $invalidTypes);
}
if (self::isValueValidType($type, $value)) {
return true;
}
if (!$invalidTypes) {
$invalidTypes[$this->formatTypeOf($value, null)] = true;
}
return false;
}
/**
* @return bool
*/
private function verifyArrayType($type, array $value, array &$invalidTypes, $level = 0)
{
$type = substr($type, 0, -2);
if ('[]' === substr($type, -2)) {
$success = true;
foreach ($value as $item) {
if (!\is_array($item)) {
$invalidTypes[$this->formatTypeOf($item, null)] = true;
$success = false;
} elseif (!$this->verifyArrayType($type, $item, $invalidTypes, $level + 1)) {
$success = false;
}
}
return $success;
}
$valid = true;
foreach ($value as $item) {
if (!self::isValueValidType($type, $item)) {
$invalidTypes[$this->formatTypeOf($item, $type)] = $value;
$valid = false;
}
}
return $valid;
}
/**
* Returns whether a resolved option with the given name exists.
*
@@ -930,7 +912,7 @@ class OptionsResolver implements Options, OptionsResolverInterface
throw new AccessException('Array access is only supported within closures of lazy options and normalizers.');
}
return array_key_exists($option, $this->defaults);
return \array_key_exists($option, $this->defaults);
}
/**
@@ -973,106 +955,6 @@ class OptionsResolver implements Options, OptionsResolverInterface
return \count($this->defaults);
}
/**
* Alias of {@link setDefault()}.
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function set($option, $value)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the setDefaults() method instead.', E_USER_DEPRECATED);
return $this->setDefault($option, $value);
}
/**
* Shortcut for {@link clear()} and {@link setDefaults()}.
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function replace(array $defaults)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the clear() and setDefaults() methods instead.', E_USER_DEPRECATED);
$this->clear();
return $this->setDefaults($defaults);
}
/**
* Alias of {@link setDefault()}.
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function overload($option, $value)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the setDefault() method instead.', E_USER_DEPRECATED);
return $this->setDefault($option, $value);
}
/**
* Alias of {@link offsetGet()}.
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function get($option)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the ArrayAccess syntax instead to get an option value.', E_USER_DEPRECATED);
return $this->offsetGet($option);
}
/**
* Alias of {@link offsetExists()}.
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function has($option)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the ArrayAccess syntax instead to get an option value.', E_USER_DEPRECATED);
return $this->offsetExists($option);
}
/**
* Shortcut for {@link clear()} and {@link setDefaults()}.
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function replaceDefaults(array $defaultValues)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the clear() and setDefaults() methods instead.', E_USER_DEPRECATED);
$this->clear();
return $this->setDefaults($defaultValues);
}
/**
* Alias of {@link setDefined()}.
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function setOptional(array $optionNames)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the setDefined() method instead.', E_USER_DEPRECATED);
return $this->setDefined($optionNames);
}
/**
* Alias of {@link isDefined()}.
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
public function isKnown($option)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the isDefined() method instead.', E_USER_DEPRECATED);
return $this->isDefined($option);
}
/**
* Returns a string representation of the type of the value.
*
@@ -1081,13 +963,38 @@ class OptionsResolver implements Options, OptionsResolverInterface
* parameters should usually not be included in messages aimed at
* non-technical people.
*
* @param mixed $value The value to return the type of
* @param mixed $value The value to return the type of
* @param string $type
*
* @return string The type of the value
*/
private function formatTypeOf($value)
private function formatTypeOf($value, $type)
{
return \is_object($value) ? \get_class($value) : \gettype($value);
$suffix = '';
if ('[]' === substr($type, -2)) {
$suffix = '[]';
$type = substr($type, 0, -2);
while ('[]' === substr($type, -2)) {
$type = substr($type, 0, -2);
$value = array_shift($value);
if (!\is_array($value)) {
break;
}
$suffix .= '[]';
}
if (\is_array($value)) {
$subTypes = [];
foreach ($value as $val) {
$subTypes[$this->formatTypeOf($val, null)] = true;
}
return implode('|', array_keys($subTypes)).$suffix;
}
}
return (\is_object($value) ? \get_class($value) : \gettype($value)).$suffix;
}
/**
@@ -1154,4 +1061,25 @@ class OptionsResolver implements Options, OptionsResolverInterface
return implode(', ', $values);
}
private static function isValueValidType($type, $value)
{
return (\function_exists($isFunction = 'is_'.$type) && $isFunction($value)) || $value instanceof $type;
}
/**
* @return string|null
*/
private function getParameterClassName(\ReflectionParameter $parameter)
{
if (!method_exists($parameter, 'getType')) {
return ($class = $parameter->getClass()) ? $class->name : null;
}
if (!($type = $parameter->getType()) || $type->isBuiltin()) {
return null;
}
return $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type;
}
}
@@ -1,208 +0,0 @@
<?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\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @deprecated since version 2.6, to be removed in 3.0. Use {@link OptionsResolver} instead.
*/
interface OptionsResolverInterface
{
/**
* Sets default option values.
*
* The options can either be values of any types or closures that
* evaluate the option value lazily. These closures must have one
* of the following signatures:
*
* function (Options $options)
* function (Options $options, $value)
*
* The second parameter passed to the closure is the previously
* set default value, in case you are overwriting an existing
* default value.
*
* The closures should return the lazily created option value.
*
* @param array $defaultValues A list of option names as keys and default
* values or closures as values
*
* @return $this
*/
public function setDefaults(array $defaultValues);
/**
* Replaces default option values.
*
* Old defaults are erased, which means that closures passed here cannot
* access the previous default value. This may be useful to improve
* performance if the previous default value is calculated by an expensive
* closure.
*
* @param array $defaultValues A list of option names as keys and default
* values or closures as values
*
* @return $this
*/
public function replaceDefaults(array $defaultValues);
/**
* Sets optional options.
*
* This method declares valid option names without setting default values for them.
* If these options are not passed to {@link resolve()} and no default has been set
* for them, they will be missing in the final options array. This can be helpful
* if you want to determine whether an option has been set or not because otherwise
* {@link resolve()} would trigger an exception for unknown options.
*
* @param array $optionNames A list of option names
*
* @return $this
*/
public function setOptional(array $optionNames);
/**
* Sets required options.
*
* If these options are not passed to {@link resolve()} and no default has been set for
* them, an exception will be thrown.
*
* @param array $optionNames A list of option names
*
* @return $this
*/
public function setRequired($optionNames);
/**
* Sets allowed values for a list of options.
*
* @param array $allowedValues A list of option names as keys and arrays
* with values acceptable for that option as
* values
*
* @return $this
*
* @throws InvalidOptionsException if an option has not been defined
* (see {@link isKnown()}) for which
* an allowed value is set
*/
public function setAllowedValues($allowedValues);
/**
* Adds allowed values for a list of options.
*
* The values are merged with the allowed values defined previously.
*
* @param array $allowedValues A list of option names as keys and arrays
* with values acceptable for that option as
* values
*
* @return $this
*
* @throws InvalidOptionsException if an option has not been defined
* (see {@link isKnown()}) for which
* an allowed value is set
*/
public function addAllowedValues($allowedValues);
/**
* Sets allowed types for a list of options.
*
* @param array $allowedTypes A list of option names as keys and type
* names passed as string or array as values
*
* @return $this
*
* @throws InvalidOptionsException if an option has not been defined for
* which an allowed type is set
*/
public function setAllowedTypes($allowedTypes);
/**
* Adds allowed types for a list of options.
*
* The types are merged with the allowed types defined previously.
*
* @param array $allowedTypes A list of option names as keys and type
* names passed as string or array as values
*
* @return $this
*
* @throws InvalidOptionsException if an option has not been defined for
* which an allowed type is set
*/
public function addAllowedTypes($allowedTypes);
/**
* Sets normalizers that are applied on resolved options.
*
* The normalizers should be closures with the following signature:
*
* function (Options $options, $value)
*
* The second parameter passed to the closure is the value of
* the option.
*
* The closure should return the normalized value.
*
* @param array $normalizers An array of closures
*
* @return $this
*/
public function setNormalizers(array $normalizers);
/**
* Returns whether an option is known.
*
* An option is known if it has been passed to either {@link setDefaults()},
* {@link setRequired()} or {@link setOptional()} before.
*
* @param string $option The name of the option
*
* @return bool Whether the option is known
*/
public function isKnown($option);
/**
* Returns whether an option is required.
*
* An option is required if it has been passed to {@link setRequired()},
* but not to {@link setDefaults()}. That is, the option has been declared
* as required and no default value has been set.
*
* @param string $option The name of the option
*
* @return bool Whether the option is required
*/
public function isRequired($option);
/**
* Returns the combination of the default and the passed options.
*
* @param array $options The custom option values
*
* @return array A list of options and their values
*
* @throws InvalidOptionsException if any of the passed options has not
* been defined or does not contain an
* allowed value
* @throws MissingOptionsException if a required option is missing
* @throws OptionDefinitionException if a cyclic dependency is detected
* between two lazy options
*/
public function resolve(array $options = array());
}
@@ -1,734 +0,0 @@
<?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\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* @group legacy
*/
class LegacyOptionsResolverTest extends TestCase
{
/**
* @var OptionsResolver
*/
private $resolver;
protected function setUp()
{
$this->resolver = new OptionsResolver();
}
public function testResolve()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$options = array(
'two' => '20',
);
$this->assertEquals(array(
'one' => '1',
'two' => '20',
), $this->resolver->resolve($options));
}
public function testResolveNumericOptions()
{
$this->resolver->setDefaults(array(
'1' => '1',
'2' => '2',
));
$options = array(
'2' => '20',
);
$this->assertEquals(array(
'1' => '1',
'2' => '20',
), $this->resolver->resolve($options));
}
public function testResolveLazy()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => function (Options $options) {
return '20';
},
));
$this->assertEquals(array(
'one' => '1',
'two' => '20',
), $this->resolver->resolve(array()));
}
public function testTypeAliasesForAllowedTypes()
{
$this->resolver->setDefaults(array(
'force' => false,
));
$this->resolver->setAllowedTypes(array(
'force' => 'boolean',
));
$this->assertSame(array('force' => true), $this->resolver->resolve(array(
'force' => true,
)));
}
public function testResolveLazyDependencyOnOptional()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => function (Options $options) {
return $options['one'].'2';
},
));
$options = array(
'one' => '10',
);
$this->assertEquals(array(
'one' => '10',
'two' => '102',
), $this->resolver->resolve($options));
}
public function testResolveLazyDependencyOnMissingOptionalWithoutDefault()
{
$test = $this;
$this->resolver->setOptional(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => function (Options $options) use ($test) {
/* @var TestCase $test */
$test->assertArrayNotHasKey('one', $options);
return '2';
},
));
$options = array();
$this->assertEquals(array(
'two' => '2',
), $this->resolver->resolve($options));
}
public function testResolveLazyDependencyOnOptionalWithoutDefault()
{
$test = $this;
$this->resolver->setOptional(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => function (Options $options) use ($test) {
/* @var TestCase $test */
$test->assertArrayHasKey('one', $options);
return $options['one'].'2';
},
));
$options = array(
'one' => '10',
);
$this->assertEquals(array(
'one' => '10',
'two' => '102',
), $this->resolver->resolve($options));
}
public function testResolveLazyDependencyOnRequired()
{
$this->resolver->setRequired(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => function (Options $options) {
return $options['one'].'2';
},
));
$options = array(
'one' => '10',
);
$this->assertEquals(array(
'one' => '10',
'two' => '102',
), $this->resolver->resolve($options));
}
public function testResolveLazyReplaceDefaults()
{
$test = $this;
$this->resolver->setDefaults(array(
'one' => function (Options $options) use ($test) {
/* @var TestCase $test */
$test->fail('Previous closure should not be executed');
},
));
$this->resolver->replaceDefaults(array(
'one' => function (Options $options, $previousValue) {
return '1';
},
));
$this->assertEquals(array(
'one' => '1',
), $this->resolver->resolve(array()));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
* @expectedExceptionMessage The option "foo" does not exist. Defined options are: "one", "three", "two".
*/
public function testResolveFailsIfNonExistingOption()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setRequired(array(
'two',
));
$this->resolver->setOptional(array(
'three',
));
$this->resolver->resolve(array(
'foo' => 'bar',
));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
*/
public function testResolveFailsIfMissingRequiredOption()
{
$this->resolver->setRequired(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => '2',
));
$this->resolver->resolve(array(
'two' => '20',
));
}
public function testResolveSucceedsIfOptionValueAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedValues(array(
'one' => array('1', 'one'),
));
$options = array(
'one' => 'one',
);
$this->assertEquals(array(
'one' => 'one',
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionValueAllowed2()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$this->resolver->setAllowedValues(array(
'one' => '1',
'two' => '2',
));
$this->resolver->addAllowedValues(array(
'one' => 'one',
'two' => 'two',
));
$options = array(
'one' => '1',
'two' => 'two',
);
$this->assertEquals(array(
'one' => '1',
'two' => 'two',
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionalWithAllowedValuesNotSet()
{
$this->resolver->setRequired(array(
'one',
));
$this->resolver->setOptional(array(
'two',
));
$this->resolver->setAllowedValues(array(
'one' => array('1', 'one'),
'two' => array('2', 'two'),
));
$options = array(
'one' => '1',
);
$this->assertEquals(array(
'one' => '1',
), $this->resolver->resolve($options));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionValueNotAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedValues(array(
'one' => array('1', 'one'),
));
$this->resolver->resolve(array(
'one' => '2',
));
}
public function testResolveSucceedsIfOptionTypeAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
));
$options = array(
'one' => 'one',
);
$this->assertEquals(array(
'one' => 'one',
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedPassArray()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => array('string', 'bool'),
));
$options = array(
'one' => true,
);
$this->assertEquals(array(
'one' => true,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedPassObject()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => 'object',
));
$object = new \stdClass();
$options = array(
'one' => $object,
);
$this->assertEquals(array(
'one' => $object,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedPassClass()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => '\stdClass',
));
$object = new \stdClass();
$options = array(
'one' => $object,
);
$this->assertEquals(array(
'one' => $object,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedAddTypes()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
'two' => 'bool',
));
$this->resolver->addAllowedTypes(array(
'one' => 'float',
'two' => 'integer',
));
$options = array(
'one' => 1.23,
'two' => false,
);
$this->assertEquals(array(
'one' => 1.23,
'two' => false,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionalWithTypeAndWithoutValue()
{
$this->resolver->setOptional(array(
'one',
'two',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
'two' => 'int',
));
$options = array(
'two' => 1,
);
$this->assertEquals(array(
'two' => 1,
), $this->resolver->resolve($options));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionTypeNotAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => array('string', 'bool'),
));
$this->resolver->resolve(array(
'one' => 1.23,
));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionTypeNotAllowedMultipleOptions()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
'two' => 'bool',
));
$this->resolver->resolve(array(
'one' => 'foo',
'two' => 1.23,
));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionTypeNotAllowedAddTypes()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
));
$this->resolver->addAllowedTypes(array(
'one' => 'bool',
));
$this->resolver->resolve(array(
'one' => 1.23,
));
}
public function testFluidInterface()
{
$this->resolver->setDefaults(array('one' => '1'))
->replaceDefaults(array('one' => '2'))
->setAllowedValues(array('one' => array('1', '2')))
->addAllowedValues(array('one' => array('3')))
->setRequired(array('two'))
->setOptional(array('three'));
$options = array(
'two' => '2',
);
$this->assertEquals(array(
'one' => '2',
'two' => '2',
), $this->resolver->resolve($options));
}
public function testKnownIfDefaultWasSet()
{
$this->assertFalse($this->resolver->isKnown('foo'));
$this->resolver->setDefaults(array(
'foo' => 'bar',
));
$this->assertTrue($this->resolver->isKnown('foo'));
}
public function testKnownIfRequired()
{
$this->assertFalse($this->resolver->isKnown('foo'));
$this->resolver->setRequired(array(
'foo',
));
$this->assertTrue($this->resolver->isKnown('foo'));
}
public function testKnownIfOptional()
{
$this->assertFalse($this->resolver->isKnown('foo'));
$this->resolver->setOptional(array(
'foo',
));
$this->assertTrue($this->resolver->isKnown('foo'));
}
public function testRequiredIfRequired()
{
$this->assertFalse($this->resolver->isRequired('foo'));
$this->resolver->setRequired(array(
'foo',
));
$this->assertTrue($this->resolver->isRequired('foo'));
}
public function testNormalizersTransformFinalOptions()
{
$this->resolver->setDefaults(array(
'foo' => 'bar',
'bam' => 'baz',
));
$this->resolver->setNormalizers(array(
'foo' => function (Options $options, $value) {
return $options['bam'].'['.$value.']';
},
));
$expected = array(
'foo' => 'baz[bar]',
'bam' => 'baz',
);
$this->assertEquals($expected, $this->resolver->resolve(array()));
$expected = array(
'foo' => 'boo[custom]',
'bam' => 'boo',
);
$this->assertEquals($expected, $this->resolver->resolve(array(
'foo' => 'custom',
'bam' => 'boo',
)));
}
public function testResolveWithoutOptionSucceedsIfRequiredAndDefaultValue()
{
$this->resolver->setRequired(array(
'foo',
));
$this->resolver->setDefaults(array(
'foo' => 'bar',
));
$this->assertEquals(array(
'foo' => 'bar',
), $this->resolver->resolve(array()));
}
public function testResolveWithoutOptionSucceedsIfDefaultValueAndRequired()
{
$this->resolver->setDefaults(array(
'foo' => 'bar',
));
$this->resolver->setRequired(array(
'foo',
));
$this->assertEquals(array(
'foo' => 'bar',
), $this->resolver->resolve(array()));
}
public function testResolveSucceedsIfOptionRequiredAndValueAllowed()
{
$this->resolver->setRequired(array(
'one', 'two',
));
$this->resolver->setAllowedValues(array(
'two' => array('2'),
));
$options = array(
'one' => '1',
'two' => '2',
);
$this->assertEquals($options, $this->resolver->resolve($options));
}
public function testResolveSucceedsIfValueAllowedCallbackReturnsTrue()
{
$this->resolver->setRequired(array(
'test',
));
$this->resolver->setAllowedValues(array(
'test' => function ($value) {
return true;
},
));
$options = array(
'test' => true,
);
$this->assertEquals($options, $this->resolver->resolve($options));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfValueAllowedCallbackReturnsFalse()
{
$this->resolver->setRequired(array(
'test',
));
$this->resolver->setAllowedValues(array(
'test' => function ($value) {
return false;
},
));
$options = array(
'test' => true,
);
$this->assertEquals($options, $this->resolver->resolve($options));
}
public function testClone()
{
$this->resolver->setDefaults(array('one' => '1'));
$clone = clone $this->resolver;
// Changes after cloning don't affect each other
$this->resolver->setDefaults(array('two' => '2'));
$clone->setDefaults(array('three' => '3'));
$this->assertEquals(array(
'one' => '1',
'two' => '2',
), $this->resolver->resolve());
$this->assertEquals(array(
'one' => '1',
'three' => '3',
), $clone->resolve());
}
public function testOverloadReturnsThis()
{
$this->assertSame($this->resolver, $this->resolver->overload('foo', 'bar'));
}
public function testOverloadCallsSet()
{
$this->resolver->overload('foo', 'bar');
$this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
}
}
@@ -1,336 +0,0 @@
<?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\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* @group legacy
*/
class LegacyOptionsTest extends TestCase
{
/**
* @var OptionsResolver
*/
private $options;
protected function setUp()
{
$this->options = new OptionsResolver();
}
public function testSetLazyOption()
{
$this->options->set('foo', function (Options $options) {
return 'dynamic';
});
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
}
public function testOverloadKeepsPreviousValue()
{
$test = $this;
// defined by superclass
$this->options->set('foo', 'bar');
// defined by subclass
$this->options->overload('foo', function (Options $options, $previousValue) use ($test) {
/* @var TestCase $test */
$test->assertEquals('bar', $previousValue);
return 'dynamic';
});
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
}
public function testPreviousValueIsEvaluatedIfLazy()
{
$test = $this;
// defined by superclass
$this->options->set('foo', function (Options $options) {
return 'bar';
});
// defined by subclass
$this->options->overload('foo', function (Options $options, $previousValue) use ($test) {
/* @var TestCase $test */
$test->assertEquals('bar', $previousValue);
return 'dynamic';
});
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
}
public function testPreviousValueIsNotEvaluatedIfNoSecondArgument()
{
$test = $this;
// defined by superclass
$this->options->set('foo', function (Options $options) use ($test) {
$test->fail('Should not be called');
});
// defined by subclass, no $previousValue argument defined!
$this->options->overload('foo', function (Options $options) {
return 'dynamic';
});
$this->assertEquals(array('foo' => 'dynamic'), $this->options->resolve());
}
public function testLazyOptionCanAccessOtherOptions()
{
$test = $this;
$this->options->set('foo', 'bar');
$this->options->set('bam', function (Options $options) use ($test) {
/* @var TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'dynamic';
});
$this->assertEquals(array('foo' => 'bar', 'bam' => 'dynamic'), $this->options->resolve());
}
public function testLazyOptionCanAccessOtherLazyOptions()
{
$test = $this;
$this->options->set('foo', function (Options $options) {
return 'bar';
});
$this->options->set('bam', function (Options $options) use ($test) {
/* @var TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'dynamic';
});
$this->assertEquals(array('foo' => 'bar', 'bam' => 'dynamic'), $this->options->resolve());
}
public function testNormalizer()
{
$this->options->set('foo', 'bar');
$this->options->setNormalizer('foo', function () {
return 'normalized';
});
$this->assertEquals(array('foo' => 'normalized'), $this->options->resolve());
}
public function testNormalizerReceivesUnnormalizedValue()
{
$this->options->set('foo', 'bar');
$this->options->setNormalizer('foo', function (Options $options, $value) {
return 'normalized['.$value.']';
});
$this->assertEquals(array('foo' => 'normalized[bar]'), $this->options->resolve());
}
public function testNormalizerCanAccessOtherOptions()
{
$test = $this;
$this->options->set('foo', 'bar');
$this->options->set('bam', 'baz');
$this->options->setNormalizer('bam', function (Options $options) use ($test) {
/* @var TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'normalized';
});
$this->assertEquals(array('foo' => 'bar', 'bam' => 'normalized'), $this->options->resolve());
}
public function testNormalizerCanAccessOtherLazyOptions()
{
$test = $this;
$this->options->set('foo', function (Options $options) {
return 'bar';
});
$this->options->set('bam', 'baz');
$this->options->setNormalizer('bam', function (Options $options) use ($test) {
/* @var TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'normalized';
});
$this->assertEquals(array('foo' => 'bar', 'bam' => 'normalized'), $this->options->resolve());
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testFailForCyclicDependencies()
{
$this->options->set('foo', function (Options $options) {
$options->get('bam');
});
$this->options->set('bam', function (Options $options) {
$options->get('foo');
});
$this->options->resolve();
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testFailForCyclicDependenciesBetweenNormalizers()
{
$this->options->set('foo', 'bar');
$this->options->set('bam', 'baz');
$this->options->setNormalizer('foo', function (Options $options) {
$options->get('bam');
});
$this->options->setNormalizer('bam', function (Options $options) {
$options->get('foo');
});
$this->options->resolve();
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testFailForCyclicDependenciesBetweenNormalizerAndLazyOption()
{
$this->options->set('foo', function (Options $options) {
$options->get('bam');
});
$this->options->set('bam', 'baz');
$this->options->setNormalizer('bam', function (Options $options) {
$options->get('foo');
});
$this->options->resolve();
}
public function testReplaceClearsAndSets()
{
$this->options->set('one', '1');
$this->options->replace(array(
'two' => '2',
'three' => function (Options $options) {
return '2' === $options['two'] ? '3' : 'foo';
},
));
$this->assertEquals(array(
'two' => '2',
'three' => '3',
), $this->options->resolve());
}
public function testClearRemovesAllOptions()
{
$this->options->set('one', 1);
$this->options->set('two', 2);
$this->options->clear();
$this->assertEmpty($this->options->resolve());
}
public function testOverloadCannotBeEvaluatedLazilyWithoutExpectedClosureParams()
{
$this->options->set('foo', 'bar');
$this->options->overload('foo', function () {
return 'test';
});
$resolved = $this->options->resolve();
$this->assertInternalType('callable', $resolved['foo']);
}
public function testOverloadCannotBeEvaluatedLazilyWithoutFirstParamTypeHint()
{
$this->options->set('foo', 'bar');
$this->options->overload('foo', function ($object) {
return 'test';
});
$resolved = $this->options->resolve();
$this->assertInternalType('callable', $resolved['foo']);
}
public function testRemoveOptionAndNormalizer()
{
$this->options->set('foo1', 'bar');
$this->options->setNormalizer('foo1', function (Options $options) {
return '';
});
$this->options->set('foo2', 'bar');
$this->options->setNormalizer('foo2', function (Options $options) {
return '';
});
$this->options->remove('foo2');
$this->assertEquals(array('foo1' => ''), $this->options->resolve());
}
public function testReplaceOptionAndNormalizer()
{
$this->options->set('foo1', 'bar');
$this->options->setNormalizer('foo1', function (Options $options) {
return '';
});
$this->options->set('foo2', 'bar');
$this->options->setNormalizer('foo2', function (Options $options) {
return '';
});
$this->options->replace(array('foo1' => 'new'));
$this->assertEquals(array('foo1' => 'new'), $this->options->resolve());
}
public function testClearOptionAndNormalizer()
{
$this->options->set('foo1', 'bar');
$this->options->setNormalizer('foo1', function (Options $options) {
return '';
});
$this->options->set('foo2', 'bar');
$this->options->setNormalizer('foo2', function (Options $options) {
return '';
});
$this->options->clear();
$this->assertEmpty($this->options->resolve());
}
}
File diff suppressed because it is too large Load Diff
+2 -7
View File
@@ -16,7 +16,7 @@
}
],
"require": {
"php": ">=5.3.9"
"php": "^5.5.9|>=7.0.8"
},
"autoload": {
"psr-4": { "Symfony\\Component\\OptionsResolver\\": "" },
@@ -24,10 +24,5 @@
"/Tests/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
}
"minimum-stability": "dev"
}