[OptionsResolver] Added a light-weight, low-level API for basic option resolving
This commit is contained in:
parent
e86fe91d70
commit
90660255a2
@ -0,0 +1,21 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when an argument is invalid.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
@ -11,6 +11,9 @@
|
||||
|
||||
namespace Symfony\Component\OptionsResolver;
|
||||
|
||||
use Symfony\Component\OptionsResolver\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
|
||||
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
|
||||
use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
|
||||
|
||||
/**
|
||||
@ -56,6 +59,244 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
*/
|
||||
private $reading = false;
|
||||
|
||||
/**
|
||||
* Merges options with an array of default values and throws an exception if
|
||||
* any of the options does not exist.
|
||||
*
|
||||
* @param array $options A list of option names and
|
||||
* values
|
||||
* @param array|Options|OptionsConfig $defaults The accepted options and
|
||||
* their default values
|
||||
*
|
||||
* @return array The merged and validated options
|
||||
*
|
||||
* @throws InvalidOptionsException If any of the options is not present in
|
||||
* the defaults array
|
||||
* @throws InvalidArgumentException If the defaults are invalid
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static function resolve(array $options, $defaults)
|
||||
{
|
||||
if (is_array($defaults)) {
|
||||
static::validateNames($options, $defaults, true);
|
||||
|
||||
return array_replace($defaults, $options);
|
||||
}
|
||||
|
||||
if ($defaults instanceof self) {
|
||||
static::validateNames($options, $defaults->options, true);
|
||||
|
||||
// Make sure this method can be called multiple times
|
||||
$combinedOptions = clone $defaults;
|
||||
|
||||
// Override options set by the user
|
||||
foreach ($options as $option => $value) {
|
||||
$combinedOptions->set($option, $value);
|
||||
}
|
||||
|
||||
// Resolve options
|
||||
return $combinedOptions->all();
|
||||
}
|
||||
|
||||
if ($defaults instanceof OptionsConfig) {
|
||||
static::validateNames($options, $defaults->knownOptions, true);
|
||||
static::validateRequired($options, $defaults->requiredOptions, true);
|
||||
|
||||
// Make sure this method can be called multiple times
|
||||
$combinedOptions = clone $defaults->defaultOptions;
|
||||
|
||||
// Override options set by the user
|
||||
foreach ($options as $option => $value) {
|
||||
$combinedOptions->set($option, $value);
|
||||
}
|
||||
|
||||
// Resolve options
|
||||
$resolvedOptions = $combinedOptions->all();
|
||||
|
||||
static::validateTypes($resolvedOptions, $defaults->allowedTypes);
|
||||
static::validateValues($resolvedOptions, $defaults->allowedValues);
|
||||
|
||||
return $resolvedOptions;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('The second argument is expected to be given as array, Options instance or OptionsConfig instance.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given option names exist and throws an exception
|
||||
* otherwise.
|
||||
*
|
||||
* @param array $options A list of option names and values
|
||||
* @param string|array $acceptedOptions The accepted option(s), either passed
|
||||
* as single string or in the values of
|
||||
* the given array
|
||||
* @param bool $namesAsKeys If set to true, the option names
|
||||
* should be passed in the keys of the
|
||||
* accepted options array
|
||||
*
|
||||
* @throws InvalidOptionsException If any of the options is not present in
|
||||
* the accepted options
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static function validateNames(array $options, $acceptedOptions, $namesAsKeys = false)
|
||||
{
|
||||
$acceptedOptions = (array) $acceptedOptions;
|
||||
|
||||
if (!$namesAsKeys) {
|
||||
$acceptedOptions = array_flip($acceptedOptions);
|
||||
}
|
||||
|
||||
$diff = array_diff_key($options, $acceptedOptions);
|
||||
|
||||
if (count($diff) > 0) {
|
||||
ksort($acceptedOptions);
|
||||
ksort($diff);
|
||||
|
||||
throw new InvalidOptionsException(sprintf(
|
||||
(count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Known options are: "%s"',
|
||||
implode('", "', array_keys($diff)),
|
||||
implode('", "', array_keys($acceptedOptions))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the required options are given and throws an exception
|
||||
* otherwise.
|
||||
*
|
||||
* The option names may be any strings that don't consist exclusively of
|
||||
* digits. For example, "case1" is a valid option name, "1" is not.
|
||||
*
|
||||
* @param array $options A list of option names and values
|
||||
* @param string|array $requiredOptions The required option(s), either
|
||||
* passed as single string or in the
|
||||
* values of the given array
|
||||
* @param bool $namesAsKeys If set to true, the option names
|
||||
* should be passed in the keys of the
|
||||
* required options array
|
||||
*
|
||||
* @throws MissingOptionsException If a required option is missing
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static function validateRequired(array $options, $requiredOptions, $namesAsKeys = false)
|
||||
{
|
||||
$requiredOptions = (array) $requiredOptions;
|
||||
|
||||
if (!$namesAsKeys) {
|
||||
$requiredOptions = array_flip($requiredOptions);
|
||||
}
|
||||
|
||||
$diff = array_diff_key($requiredOptions, $options);
|
||||
|
||||
if (count($diff) > 0) {
|
||||
ksort($diff);
|
||||
|
||||
throw new MissingOptionsException(sprintf(
|
||||
count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.',
|
||||
implode('", "', array_keys($diff))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given options match the accepted types and
|
||||
* throws an exception otherwise.
|
||||
*
|
||||
* Accepted type names are any types for which a native "is_*()" function
|
||||
* exists. For example, "int" is an acceptable type name and will be checked
|
||||
* with the "is_int()" function.
|
||||
*
|
||||
* Types may also be passed as closures which return true or false.
|
||||
*
|
||||
* @param array $options A list of option names and values
|
||||
* @param array $acceptedTypes A mapping of option names to accepted option
|
||||
* types. The types may be given as
|
||||
* string/closure or as array of strings/closures
|
||||
*
|
||||
* @throws InvalidOptionsException If any of the types does not match the
|
||||
* accepted types of the option
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static function validateTypes(array $options, array $acceptedTypes)
|
||||
{
|
||||
foreach ($acceptedTypes as $option => $optionTypes) {
|
||||
if (!array_key_exists($option, $options)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $options[$option];
|
||||
$optionTypes = (array) $optionTypes;
|
||||
|
||||
foreach ($optionTypes as $type) {
|
||||
$isFunction = 'is_'.$type;
|
||||
|
||||
if (function_exists($isFunction) && $isFunction($value)) {
|
||||
continue 2;
|
||||
} elseif ($value instanceof $type) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$printableValue = is_object($value)
|
||||
? get_class($value)
|
||||
: (is_array($value)
|
||||
? 'Array'
|
||||
: (string) $value);
|
||||
|
||||
throw new InvalidOptionsException(sprintf(
|
||||
'The option "%s" with value "%s" is expected to be of type "%s"',
|
||||
$option,
|
||||
$printableValue,
|
||||
implode('", "', $optionTypes)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given option values match the accepted values and
|
||||
* throws an exception otherwise.
|
||||
*
|
||||
* @param array $options A list of option names and values
|
||||
* @param array $acceptedValues A mapping of option names to accepted option
|
||||
* values. The option values must be given as
|
||||
* arrays
|
||||
*
|
||||
* @throws InvalidOptionsException If any of the values does not match the
|
||||
* accepted values of the option
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static function validateValues(array $options, array $acceptedValues)
|
||||
{
|
||||
foreach ($acceptedValues as $option => $optionValues) {
|
||||
if (array_key_exists($option, $options)) {
|
||||
if (is_array($optionValues) && !in_array($options[$option], $optionValues, true)) {
|
||||
throw new InvalidOptionsException(sprintf('The option "%s" has the value "%s", but is expected to be one of "%s"', $option, $options[$option], implode('", "', $optionValues)));
|
||||
}
|
||||
|
||||
if (is_callable($optionValues) && !call_user_func($optionValues, $options[$option])) {
|
||||
throw new InvalidOptionsException(sprintf('The option "%s" has the value "%s", which it is not valid', $option, $options[$option]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new object with a set of default options.
|
||||
*
|
||||
* @param array $options A list of option names and values
|
||||
*/
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
foreach ($options as $option => $value) {
|
||||
$this->set($option, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a given option.
|
||||
*
|
||||
@ -179,8 +420,10 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
|
||||
// If an option is a closure that should be evaluated lazily, store it
|
||||
// in the "lazy" property.
|
||||
if ($value instanceof \Closure) {
|
||||
$reflClosure = new \ReflectionFunction($value);
|
||||
if (is_callable($value)) {
|
||||
$reflClosure = is_array($value)
|
||||
? new \ReflectionMethod($value[0], $value[1])
|
||||
: new \ReflectionFunction($value);
|
||||
$params = $reflClosure->getParameters();
|
||||
|
||||
if (isset($params[0]) && null !== ($class = $params[0]->getClass()) && __CLASS__ === $class->name) {
|
||||
@ -229,11 +472,11 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
}
|
||||
|
||||
if (isset($this->lazy[$option])) {
|
||||
$this->resolve($option);
|
||||
$this->resolveOption($option);
|
||||
}
|
||||
|
||||
if (isset($this->normalizers[$option])) {
|
||||
$this->normalize($option);
|
||||
$this->normalizeOption($option);
|
||||
}
|
||||
|
||||
return $this->options[$option];
|
||||
@ -306,13 +549,13 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
// Double check, in case the option has already been resolved
|
||||
// by cascade in the previous cycles
|
||||
if (isset($this->lazy[$option])) {
|
||||
$this->resolve($option);
|
||||
$this->resolveOption($option);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->normalizers as $option => $normalizer) {
|
||||
if (isset($this->normalizers[$option])) {
|
||||
$this->normalize($option);
|
||||
$this->normalizeOption($option);
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,7 +687,7 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
* @throws OptionDefinitionException If the option has a cyclic dependency
|
||||
* on another option.
|
||||
*/
|
||||
private function resolve($option)
|
||||
private function resolveOption($option)
|
||||
{
|
||||
// The code duplication with normalize() exists for performance
|
||||
// reasons, in order to save a method call.
|
||||
@ -464,7 +707,7 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
|
||||
$this->lock[$option] = true;
|
||||
foreach ($this->lazy[$option] as $closure) {
|
||||
$this->options[$option] = $closure($this, $this->options[$option]);
|
||||
$this->options[$option] = call_user_func($closure, $this, $this->options[$option]);
|
||||
}
|
||||
unset($this->lock[$option]);
|
||||
|
||||
@ -482,7 +725,7 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
* @throws OptionDefinitionException If the option has a cyclic dependency
|
||||
* on another option.
|
||||
*/
|
||||
private function normalize($option)
|
||||
private function normalizeOption($option)
|
||||
{
|
||||
// The code duplication with resolve() exists for performance
|
||||
// reasons, in order to save a method call.
|
||||
|
354
src/Symfony/Component/OptionsResolver/OptionsConfig.php
Normal file
354
src/Symfony/Component/OptionsResolver/OptionsConfig.php
Normal file
@ -0,0 +1,354 @@
|
||||
<?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\OptionDefinitionException;
|
||||
|
||||
/**
|
||||
* Stores option configuration.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
class OptionsConfig
|
||||
{
|
||||
/**
|
||||
* The default option values.
|
||||
*
|
||||
* @var Options
|
||||
*
|
||||
* @internal Public for performance reasons. Should not be accessed by user
|
||||
* code.
|
||||
*/
|
||||
public $defaultOptions;
|
||||
|
||||
/**
|
||||
* The options known by the resolver.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @internal Public for performance reasons. Should not be accessed by user
|
||||
* code.
|
||||
*/
|
||||
public $knownOptions = array();
|
||||
|
||||
/**
|
||||
* The options without defaults that are required to be passed to resolve().
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @internal Public for performance reasons. Should not be accessed by user
|
||||
* code.
|
||||
*/
|
||||
public $requiredOptions = array();
|
||||
|
||||
/**
|
||||
* A list of accepted values for each option.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @internal Public for performance reasons. Should not be accessed by user
|
||||
* code.
|
||||
*/
|
||||
public $allowedValues = array();
|
||||
|
||||
/**
|
||||
* A list of accepted types for each option.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @internal Public for performance reasons. Should not be accessed by user
|
||||
* code.
|
||||
*/
|
||||
public $allowedTypes = array();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->defaultOptions = new Options();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the resolver.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->defaultOptions = clone $this->defaultOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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:
|
||||
*
|
||||
* <code>
|
||||
* function (Options $options)
|
||||
* function (Options $options, $value)
|
||||
* </code>
|
||||
*
|
||||
* 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 OptionsConfig This configuration instance
|
||||
*/
|
||||
public function setDefaults(array $defaultValues)
|
||||
{
|
||||
foreach ($defaultValues as $option => $value) {
|
||||
$this->defaultOptions->overload($option, $value);
|
||||
$this->knownOptions[$option] = true;
|
||||
unset($this->requiredOptions[$option]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 OptionsConfig This configuration instance
|
||||
*/
|
||||
public function replaceDefaults(array $defaultValues)
|
||||
{
|
||||
foreach ($defaultValues as $option => $value) {
|
||||
$this->defaultOptions->set($option, $value);
|
||||
$this->knownOptions[$option] = true;
|
||||
unset($this->requiredOptions[$option]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 OptionsConfig This configuration instance
|
||||
*
|
||||
* @throws Exception\OptionDefinitionException When trying to pass default values
|
||||
*/
|
||||
public function setOptional(array $optionNames)
|
||||
{
|
||||
foreach ($optionNames as $key => $option) {
|
||||
if (!is_int($key)) {
|
||||
throw new OptionDefinitionException('You should not pass default values to setOptional()');
|
||||
}
|
||||
|
||||
$this->knownOptions[$option] = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 OptionsConfig This configuration instance
|
||||
*
|
||||
* @throws Exception\OptionDefinitionException When trying to pass default values
|
||||
*/
|
||||
public function setRequired(array $optionNames)
|
||||
{
|
||||
foreach ($optionNames as $key => $option) {
|
||||
if (!is_int($key)) {
|
||||
throw new OptionDefinitionException('You should not pass default values to setRequired()');
|
||||
}
|
||||
|
||||
$this->knownOptions[$option] = true;
|
||||
// set as required if no default has been set already
|
||||
if (!isset($this->defaultOptions[$option])) {
|
||||
$this->requiredOptions[$option] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 OptionsConfig This configuration instance
|
||||
*
|
||||
* @throws Exception\InvalidOptionsException If an option has not been defined
|
||||
* (see {@link isKnown()}) for which
|
||||
* an allowed value is set
|
||||
*/
|
||||
public function setAllowedValues(array $allowedValues)
|
||||
{
|
||||
Options::validateNames($allowedValues, $this->knownOptions, true);
|
||||
|
||||
$this->allowedValues = array_replace($this->allowedValues, $allowedValues);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 OptionsConfig This configuration instance
|
||||
*
|
||||
* @throws Exception\InvalidOptionsException If an option has not been defined
|
||||
* (see {@link isKnown()}) for which
|
||||
* an allowed value is set
|
||||
*/
|
||||
public function addAllowedValues(array $allowedValues)
|
||||
{
|
||||
Options::validateNames($allowedValues, $this->knownOptions, true);
|
||||
|
||||
$this->allowedValues = array_merge_recursive($this->allowedValues, $allowedValues);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 OptionsConfig This configuration instance
|
||||
*
|
||||
* @throws Exception\InvalidOptionsException If an option has not been defined for
|
||||
* which an allowed type is set
|
||||
*/
|
||||
public function setAllowedTypes(array $allowedTypes)
|
||||
{
|
||||
Options::validateNames($allowedTypes, $this->knownOptions, true);
|
||||
|
||||
$this->allowedTypes = array_replace($this->allowedTypes, $allowedTypes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 OptionsConfig This configuration instance
|
||||
*
|
||||
* @throws Exception\InvalidOptionsException If an option has not been defined for
|
||||
* which an allowed type is set
|
||||
*/
|
||||
public function addAllowedTypes(array $allowedTypes)
|
||||
{
|
||||
Options::validateNames($allowedTypes, $this->knownOptions, true);
|
||||
|
||||
$this->allowedTypes = array_merge_recursive($this->allowedTypes, $allowedTypes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets normalizers that are applied on resolved options.
|
||||
*
|
||||
* The normalizers should be closures with the following signature:
|
||||
*
|
||||
* <code>
|
||||
* function (Options $options, $value)
|
||||
* </code>
|
||||
*
|
||||
* 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 OptionsConfig This configuration instance
|
||||
*/
|
||||
public function setNormalizers(array $normalizers)
|
||||
{
|
||||
Options::validateNames($normalizers, $this->knownOptions, true);
|
||||
|
||||
foreach ($normalizers as $option => $normalizer) {
|
||||
$this->defaultOptions->setNormalizer($option, $normalizer);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return isset($this->knownOptions[$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)
|
||||
{
|
||||
return isset($this->requiredOptions[$option]);
|
||||
}
|
||||
}
|
@ -11,342 +11,19 @@
|
||||
|
||||
namespace Symfony\Component\OptionsResolver;
|
||||
|
||||
use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
|
||||
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
|
||||
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
|
||||
|
||||
/**
|
||||
* Helper for merging default and concrete option values.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
class OptionsResolver implements OptionsResolverInterface
|
||||
class OptionsResolver extends OptionsConfig implements OptionsResolverInterface
|
||||
{
|
||||
/**
|
||||
* The default option values.
|
||||
* @var Options
|
||||
*/
|
||||
private $defaultOptions;
|
||||
|
||||
/**
|
||||
* The options known by the resolver.
|
||||
* @var array
|
||||
*/
|
||||
private $knownOptions = array();
|
||||
|
||||
/**
|
||||
* The options without defaults that are required to be passed to resolve().
|
||||
* @var array
|
||||
*/
|
||||
private $requiredOptions = array();
|
||||
|
||||
/**
|
||||
* A list of accepted values for each option.
|
||||
* @var array
|
||||
*/
|
||||
private $allowedValues = array();
|
||||
|
||||
/**
|
||||
* A list of accepted types for each option.
|
||||
* @var array
|
||||
*/
|
||||
private $allowedTypes = array();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->defaultOptions = new Options();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the resolver.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->defaultOptions = clone $this->defaultOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDefaults(array $defaultValues)
|
||||
{
|
||||
foreach ($defaultValues as $option => $value) {
|
||||
$this->defaultOptions->overload($option, $value);
|
||||
$this->knownOptions[$option] = true;
|
||||
unset($this->requiredOptions[$option]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function replaceDefaults(array $defaultValues)
|
||||
{
|
||||
foreach ($defaultValues as $option => $value) {
|
||||
$this->defaultOptions->set($option, $value);
|
||||
$this->knownOptions[$option] = true;
|
||||
unset($this->requiredOptions[$option]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setOptional(array $optionNames)
|
||||
{
|
||||
foreach ($optionNames as $key => $option) {
|
||||
if (!is_int($key)) {
|
||||
throw new OptionDefinitionException('You should not pass default values to setOptional()');
|
||||
}
|
||||
|
||||
$this->knownOptions[$option] = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRequired(array $optionNames)
|
||||
{
|
||||
foreach ($optionNames as $key => $option) {
|
||||
if (!is_int($key)) {
|
||||
throw new OptionDefinitionException('You should not pass default values to setRequired()');
|
||||
}
|
||||
|
||||
$this->knownOptions[$option] = true;
|
||||
// set as required if no default has been set already
|
||||
if (!isset($this->defaultOptions[$option])) {
|
||||
$this->requiredOptions[$option] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setAllowedValues(array $allowedValues)
|
||||
{
|
||||
$this->validateOptionsExistence($allowedValues);
|
||||
|
||||
$this->allowedValues = array_replace($this->allowedValues, $allowedValues);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addAllowedValues(array $allowedValues)
|
||||
{
|
||||
$this->validateOptionsExistence($allowedValues);
|
||||
|
||||
$this->allowedValues = array_merge_recursive($this->allowedValues, $allowedValues);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setAllowedTypes(array $allowedTypes)
|
||||
{
|
||||
$this->validateOptionsExistence($allowedTypes);
|
||||
|
||||
$this->allowedTypes = array_replace($this->allowedTypes, $allowedTypes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addAllowedTypes(array $allowedTypes)
|
||||
{
|
||||
$this->validateOptionsExistence($allowedTypes);
|
||||
|
||||
$this->allowedTypes = array_merge_recursive($this->allowedTypes, $allowedTypes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setNormalizers(array $normalizers)
|
||||
{
|
||||
$this->validateOptionsExistence($normalizers);
|
||||
|
||||
foreach ($normalizers as $option => $normalizer) {
|
||||
$this->defaultOptions->setNormalizer($option, $normalizer);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isKnown($option)
|
||||
{
|
||||
return isset($this->knownOptions[$option]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRequired($option)
|
||||
{
|
||||
return isset($this->requiredOptions[$option]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resolve(array $options = array())
|
||||
{
|
||||
$this->validateOptionsExistence($options);
|
||||
$this->validateOptionsCompleteness($options);
|
||||
|
||||
// Make sure this method can be called multiple times
|
||||
$combinedOptions = clone $this->defaultOptions;
|
||||
|
||||
// Override options set by the user
|
||||
foreach ($options as $option => $value) {
|
||||
$combinedOptions->set($option, $value);
|
||||
}
|
||||
|
||||
// Resolve options
|
||||
$resolvedOptions = $combinedOptions->all();
|
||||
|
||||
$this->validateOptionTypes($resolvedOptions);
|
||||
$this->validateOptionValues($resolvedOptions);
|
||||
|
||||
return $resolvedOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given option names exist and throws an exception
|
||||
* otherwise.
|
||||
*
|
||||
* @param array $options An list of option names as keys.
|
||||
*
|
||||
* @throws InvalidOptionsException If any of the options has not been defined.
|
||||
*/
|
||||
private function validateOptionsExistence(array $options)
|
||||
{
|
||||
$diff = array_diff_key($options, $this->knownOptions);
|
||||
|
||||
if (count($diff) > 0) {
|
||||
ksort($this->knownOptions);
|
||||
ksort($diff);
|
||||
|
||||
throw new InvalidOptionsException(sprintf(
|
||||
(count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Known options are: "%s"',
|
||||
implode('", "', array_keys($diff)),
|
||||
implode('", "', array_keys($this->knownOptions))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that all required options are given and throws an exception
|
||||
* otherwise.
|
||||
*
|
||||
* @param array $options An list of option names as keys.
|
||||
*
|
||||
* @throws MissingOptionsException If a required option is missing.
|
||||
*/
|
||||
private function validateOptionsCompleteness(array $options)
|
||||
{
|
||||
$diff = array_diff_key($this->requiredOptions, $options);
|
||||
|
||||
if (count($diff) > 0) {
|
||||
ksort($diff);
|
||||
|
||||
throw new MissingOptionsException(sprintf(
|
||||
count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.',
|
||||
implode('", "', array_keys($diff))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given option values match the allowed values and
|
||||
* throws an exception otherwise.
|
||||
*
|
||||
* @param array $options A list of option values.
|
||||
*
|
||||
* @throws InvalidOptionsException If any of the values does not match the
|
||||
* allowed values of the option.
|
||||
*/
|
||||
private function validateOptionValues(array $options)
|
||||
{
|
||||
foreach ($this->allowedValues as $option => $allowedValues) {
|
||||
if (isset($options[$option])) {
|
||||
if (is_array($allowedValues) && !in_array($options[$option], $allowedValues, true)) {
|
||||
throw new InvalidOptionsException(sprintf('The option "%s" has the value "%s", but is expected to be one of "%s"', $option, $options[$option], implode('", "', $allowedValues)));
|
||||
}
|
||||
|
||||
if (is_callable($allowedValues) && !call_user_func($allowedValues, $options[$option])) {
|
||||
throw new InvalidOptionsException(sprintf('The option "%s" has the value "%s", which it is not valid', $option, $options[$option]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given options match the allowed types and
|
||||
* throws an exception otherwise.
|
||||
*
|
||||
* @param array $options A list of options.
|
||||
*
|
||||
* @throws InvalidOptionsException If any of the types does not match the
|
||||
* allowed types of the option.
|
||||
*/
|
||||
private function validateOptionTypes(array $options)
|
||||
{
|
||||
foreach ($this->allowedTypes as $option => $allowedTypes) {
|
||||
if (!array_key_exists($option, $options)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $options[$option];
|
||||
$allowedTypes = (array) $allowedTypes;
|
||||
|
||||
foreach ($allowedTypes as $type) {
|
||||
$isFunction = 'is_'.$type;
|
||||
|
||||
if (function_exists($isFunction) && $isFunction($value)) {
|
||||
continue 2;
|
||||
} elseif ($value instanceof $type) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$printableValue = is_object($value)
|
||||
? get_class($value)
|
||||
: (is_array($value)
|
||||
? 'Array'
|
||||
: (string) $value);
|
||||
|
||||
throw new InvalidOptionsException(sprintf(
|
||||
'The option "%s" with value "%s" is expected to be of type "%s"',
|
||||
$option,
|
||||
$printableValue,
|
||||
implode('", "', $allowedTypes)
|
||||
));
|
||||
}
|
||||
return Options::resolve($options, $this);
|
||||
}
|
||||
}
|
||||
|
@ -1,101 +1,12 @@
|
||||
OptionsResolver Component
|
||||
=========================
|
||||
|
||||
OptionsResolver helps at configuring objects with option arrays.
|
||||
|
||||
It supports default values on different levels of your class hierarchy,
|
||||
option constraints (required vs. optional, allowed values) and lazy options
|
||||
whose default value depends on the value of another option.
|
||||
|
||||
The following example demonstrates a Person class with two required options
|
||||
"firstName" and "lastName" and two optional options "age" and "gender", where
|
||||
the default value of "gender" is derived from the passed first name, if
|
||||
possible, and may only be one of "male" and "female".
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
|
||||
class Person
|
||||
{
|
||||
protected $options;
|
||||
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$resolver = new OptionsResolver();
|
||||
$this->setDefaultOptions($resolver);
|
||||
|
||||
$this->options = $resolver->resolve($options);
|
||||
}
|
||||
|
||||
protected function setDefaultOptions(OptionsResolverInterface $resolver)
|
||||
{
|
||||
$resolver->setRequired(array(
|
||||
'firstName',
|
||||
'lastName',
|
||||
));
|
||||
|
||||
$resolver->setDefaults(array(
|
||||
'age' => null,
|
||||
'gender' => function (Options $options) {
|
||||
if (self::isKnownMaleName($options['firstName'])) {
|
||||
return 'male';
|
||||
}
|
||||
|
||||
return 'female';
|
||||
},
|
||||
));
|
||||
|
||||
$resolver->setAllowedValues(array(
|
||||
'gender' => array('male', 'female'),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
We can now easily instantiate a Person object:
|
||||
|
||||
// 'gender' is implicitly set to 'female'
|
||||
$person = new Person(array(
|
||||
'firstName' => 'Jane',
|
||||
'lastName' => 'Doe',
|
||||
));
|
||||
|
||||
We can also override the default values of the optional options:
|
||||
|
||||
$person = new Person(array(
|
||||
'firstName' => 'Abdullah',
|
||||
'lastName' => 'Mogashi',
|
||||
'gender' => 'male',
|
||||
'age' => 30,
|
||||
));
|
||||
|
||||
Options can be added or changed in subclasses by overriding the `setDefaultOptions`
|
||||
method:
|
||||
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
|
||||
class Employee extends Person
|
||||
{
|
||||
protected function setDefaultOptions(OptionsResolverInterface $resolver)
|
||||
{
|
||||
parent::setDefaultOptions($resolver);
|
||||
|
||||
$resolver->setRequired(array(
|
||||
'birthDate',
|
||||
));
|
||||
|
||||
$resolver->setDefaults(array(
|
||||
// $previousValue contains the default value configured in the
|
||||
// parent class
|
||||
'age' => function (Options $options, $previousValue) {
|
||||
return self::calculateAge($options['birthDate']);
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
This component processes and validates option arrays.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
The documentation for the component can be found [online] [1].
|
||||
|
||||
Resources
|
||||
---------
|
||||
@ -105,3 +16,5 @@ You can run the unit tests with the following command:
|
||||
$ cd path/to/Symfony/Component/OptionsResolver/
|
||||
$ composer.phar install
|
||||
$ phpunit
|
||||
|
||||
[1]: http://symfony.com/doc/current/components/options_resolver.html
|
||||
|
@ -43,6 +43,23 @@ class OptionsResolverTest extends \PHPUnit_Framework_TestCase
|
||||
), $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(
|
||||
@ -94,7 +111,7 @@ class OptionsResolverTest extends \PHPUnit_Framework_TestCase
|
||||
},
|
||||
));
|
||||
|
||||
$options = array(
|
||||
$options = array(,
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
@ -623,7 +640,7 @@ class OptionsResolverTest extends \PHPUnit_Framework_TestCase
|
||||
));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'foo' => 'bar'
|
||||
'foo' => 'bar',
|
||||
), $this->resolver->resolve(array()));
|
||||
}
|
||||
|
||||
@ -637,7 +654,7 @@ class OptionsResolverTest extends \PHPUnit_Framework_TestCase
|
||||
));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'foo' => 'bar'
|
||||
'foo' => 'bar',
|
||||
), $this->resolver->resolve(array()));
|
||||
}
|
||||
|
||||
@ -652,7 +669,7 @@ class OptionsResolverTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$options = array(
|
||||
'one' => '1',
|
||||
'two' => '2'
|
||||
'two' => '2',
|
||||
);
|
||||
|
||||
$this->assertEquals($options, $this->resolver->resolve($options));
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\OptionsResolver\Tests;
|
||||
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsConfig;
|
||||
|
||||
class OptionsTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
@ -25,6 +26,389 @@ class OptionsTest extends \PHPUnit_Framework_TestCase
|
||||
$this->options = new Options();
|
||||
}
|
||||
|
||||
public function testResolve()
|
||||
{
|
||||
$defaults = array(
|
||||
'one' => '1',
|
||||
'two' => '2',
|
||||
);
|
||||
|
||||
$options = array(
|
||||
'two' => '20',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
'two' => '20',
|
||||
), Options::resolve($options, $defaults));
|
||||
}
|
||||
|
||||
public function testResolveNumericOptions()
|
||||
{
|
||||
$defaults = array(
|
||||
'1' => '1',
|
||||
'2' => '2',
|
||||
);
|
||||
|
||||
$options = array(
|
||||
'2' => '20',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'1' => '1',
|
||||
'2' => '20',
|
||||
), Options::resolve($options, $defaults));
|
||||
}
|
||||
|
||||
public function testResolveLazy()
|
||||
{
|
||||
$defaults = new Options(array(
|
||||
'one' => '1',
|
||||
'two' => function (Options $options) {
|
||||
return '20';
|
||||
},
|
||||
));
|
||||
|
||||
$options = array();
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
'two' => '20',
|
||||
), Options::resolve($options, $defaults));
|
||||
}
|
||||
|
||||
public function testResolveConfig()
|
||||
{
|
||||
$config = new OptionsConfig();
|
||||
|
||||
$config->setDefaults(array(
|
||||
'one' => '1',
|
||||
'two' => '2',
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'two' => '20',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
'two' => '20',
|
||||
), Options::resolve($options, $config));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testResolveFailsIfNonExistingOption()
|
||||
{
|
||||
$defaults = array(
|
||||
'one' => '1',
|
||||
);
|
||||
|
||||
$options = array(
|
||||
'foo' => 'bar',
|
||||
);
|
||||
|
||||
Options::resolve($options, $defaults);
|
||||
}
|
||||
|
||||
public function testValidateNamesSucceedsIfValidOption()
|
||||
{
|
||||
$options = array(
|
||||
'one' => '1',
|
||||
);
|
||||
|
||||
Options::validateNames($options, 'one');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testValidateNamesFailsIfNonExistingOption()
|
||||
{
|
||||
$options = array(
|
||||
'foo' => 'bar',
|
||||
);
|
||||
|
||||
Options::validateNames($options, 'one');
|
||||
}
|
||||
|
||||
public function testValidateNamesSucceedsIfValidOptions()
|
||||
{
|
||||
$options = array(
|
||||
'one' => '1',
|
||||
);
|
||||
|
||||
Options::validateNames($options, array(
|
||||
'one',
|
||||
'two',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testValidateNamesFailsIfNonExistingOptions()
|
||||
{
|
||||
$options = array(
|
||||
'one' => '1',
|
||||
'foo' => 'bar',
|
||||
);
|
||||
|
||||
Options::validateNames($options, array(
|
||||
'one',
|
||||
'two',
|
||||
));
|
||||
}
|
||||
|
||||
public function testValidateNamesSucceedsIfValidOptionsNamesAsKeys()
|
||||
{
|
||||
$options = array(
|
||||
'one' => '1',
|
||||
);
|
||||
|
||||
Options::validateNames($options, array(
|
||||
'one' => null,
|
||||
'two' => null,
|
||||
), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testValidateNamesFailsIfNonExistingOptionsNamesAsKeys()
|
||||
{
|
||||
$options = array(
|
||||
'one' => '1',
|
||||
'foo' => 'bar',
|
||||
);
|
||||
|
||||
Options::validateNames($options, array(
|
||||
'one' => null,
|
||||
'two' => null,
|
||||
), true);
|
||||
}
|
||||
|
||||
public function testValidateRequiredSucceedsIfRequiredOptionPresent()
|
||||
{
|
||||
$options = array(
|
||||
'one' => '10',
|
||||
);
|
||||
|
||||
Options::validateRequired($options, 'one');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
|
||||
*/
|
||||
public function testValidateRequiredFailsIfMissingRequiredOption()
|
||||
{
|
||||
$options = array(
|
||||
'two' => '20',
|
||||
);
|
||||
|
||||
Options::validateRequired($options, 'one');
|
||||
}
|
||||
|
||||
public function testValidateRequiredSucceedsIfRequiredOptionsPresent()
|
||||
{
|
||||
$options = array(
|
||||
'one' => '10',
|
||||
'two' => '20',
|
||||
);
|
||||
|
||||
Options::validateRequired($options, array(
|
||||
'one',
|
||||
'two',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
|
||||
*/
|
||||
public function testValidateRequiredFailsIfMissingRequiredOptions()
|
||||
{
|
||||
$options = array(
|
||||
'two' => '20',
|
||||
);
|
||||
|
||||
Options::validateRequired($options, array(
|
||||
'one',
|
||||
'two',
|
||||
));
|
||||
}
|
||||
|
||||
public function testValidateRequiredSucceedsIfRequiredOptionsPresentNamesAsKeys()
|
||||
{
|
||||
$options = array(
|
||||
'one' => '10',
|
||||
'two' => '20',
|
||||
);
|
||||
|
||||
Options::validateRequired($options, array(
|
||||
'one' => null,
|
||||
'two' => null,
|
||||
), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
|
||||
*/
|
||||
public function testValidateRequiredFailsIfMissingRequiredOptionsNamesAsKeys()
|
||||
{
|
||||
$options = array(
|
||||
'two' => '20',
|
||||
);
|
||||
|
||||
Options::validateRequired($options, array(
|
||||
'one' => null,
|
||||
'two' => null,
|
||||
), true);
|
||||
}
|
||||
|
||||
public function testValidateTypesSucceedsIfValidType()
|
||||
{
|
||||
$options = array(
|
||||
'one' => 'one',
|
||||
);
|
||||
|
||||
Options::validateTypes($options, array(
|
||||
'one' => 'string',
|
||||
));
|
||||
}
|
||||
|
||||
public function testValidateTypesSucceedsIfValidTypePassArray()
|
||||
{
|
||||
$options = array(
|
||||
'one' => 'one',
|
||||
);
|
||||
|
||||
Options::validateTypes($options, array(
|
||||
'one' => array('string', 'bool'),
|
||||
));
|
||||
}
|
||||
|
||||
public function testValidateTypesSucceedsIfValidTypePassObject()
|
||||
{
|
||||
$object = new \stdClass();
|
||||
$options = array(
|
||||
'one' => $object,
|
||||
);
|
||||
|
||||
Options::validateTypes($options, array(
|
||||
'one' => 'object',
|
||||
));
|
||||
}
|
||||
|
||||
public function testValidateTypesSucceedsIfValidTypePassClass()
|
||||
{
|
||||
$object = new \stdClass();
|
||||
$options = array(
|
||||
'one' => $object,
|
||||
);
|
||||
|
||||
Options::validateTypes($options, array(
|
||||
'one' => '\stdClass',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testValidateTypesFailsIfInvalidType()
|
||||
{
|
||||
$options = array(
|
||||
'one' => 1.23,
|
||||
);
|
||||
|
||||
Options::validateTypes($options, array(
|
||||
'one' => array('string', 'bool'),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testValidateTypesFailsIfInvalidTypeMultipleOptions()
|
||||
{
|
||||
$options = array(
|
||||
'one' => 'foo',
|
||||
'two' => 1.23,
|
||||
);
|
||||
|
||||
Options::validateTypes($options, array(
|
||||
'one' => 'string',
|
||||
'two' => 'bool',
|
||||
));
|
||||
}
|
||||
|
||||
public function testValidateValuesSucceedsIfValidValue()
|
||||
{
|
||||
$options = array(
|
||||
'one' => 'one',
|
||||
);
|
||||
|
||||
Options::validateValues($options, array(
|
||||
'one' => array('1', 'one'),
|
||||
));
|
||||
}
|
||||
|
||||
public function testValidateValuesSucceedsIfValidValueMultipleOptions()
|
||||
{
|
||||
$options = array(
|
||||
'one' => '1',
|
||||
'two' => 'two',
|
||||
);
|
||||
|
||||
Options::validateValues($options, array(
|
||||
'one' => array('1', 'one'),
|
||||
'two' => array('2', 'two'),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testValidateValuesFailsIfInvalidValue()
|
||||
{
|
||||
$options = array(
|
||||
'one' => '2',
|
||||
);
|
||||
|
||||
Options::validateValues($options, array(
|
||||
'one' => array('1', 'one'),
|
||||
));
|
||||
}
|
||||
|
||||
public function testValidateValuesSucceedsIfValidValueCallback()
|
||||
{
|
||||
$options = array(
|
||||
'test' => true,
|
||||
);
|
||||
|
||||
Options::validateValues($options, array(
|
||||
'test' => function ($value) {
|
||||
return true;
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testValidateValuesFailsIfInvalidValueCallback()
|
||||
{
|
||||
$options = array(
|
||||
'test' => true,
|
||||
);
|
||||
|
||||
Options::validateValues($options, array(
|
||||
'test' => function ($value) {
|
||||
return false;
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
public function testArrayAccess()
|
||||
{
|
||||
$this->assertFalse(isset($this->options['foo']));
|
||||
@ -91,15 +475,37 @@ class OptionsTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testSetLazyOption()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
$this->options->set('foo', function (Options $options) use ($test) {
|
||||
$this->options->set('foo', function (Options $options) {
|
||||
return 'dynamic';
|
||||
});
|
||||
|
||||
$this->assertEquals('dynamic', $this->options->get('foo'));
|
||||
}
|
||||
|
||||
public static function getLazyOptionStatic(Options $options)
|
||||
{
|
||||
return 'dynamic';
|
||||
}
|
||||
|
||||
public function testSetLazyOptionToClassMethod()
|
||||
{
|
||||
$this->options->set('foo', array(__CLASS__, 'getLazyOptionStatic'));
|
||||
|
||||
$this->assertEquals('dynamic', $this->options->get('foo'));
|
||||
}
|
||||
|
||||
public static function getLazyOption(Options $options)
|
||||
{
|
||||
return 'dynamic';
|
||||
}
|
||||
|
||||
public function testSetLazyOptionToInstanceMethod()
|
||||
{
|
||||
$this->options->set('foo', array($this, 'getLazyOption'));
|
||||
|
||||
$this->assertEquals('dynamic', $this->options->get('foo'));
|
||||
}
|
||||
|
||||
public function testSetDiscardsPreviousValue()
|
||||
{
|
||||
$test = $this;
|
||||
@ -372,7 +778,7 @@ class OptionsTest extends \PHPUnit_Framework_TestCase
|
||||
'two' => '2',
|
||||
'three' => function (Options $options) {
|
||||
return '2' === $options['two'] ? '3' : 'foo';
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
$this->assertEquals(array(
|
||||
|
Reference in New Issue
Block a user