[OptionsResolver] Added option type validation capabilities
This commit is contained in:
parent
0af5f06703
commit
97de0041a1
@ -120,10 +120,10 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
* Passed closures should have the following signature:
|
||||
*
|
||||
* <code>
|
||||
* function (Options $options, $previousValue)
|
||||
* function (Options $options, $value)
|
||||
* </code>
|
||||
*
|
||||
* The second parameter passed to the closure is the previous default
|
||||
* The second parameter passed to the closure is the current default
|
||||
* value of the option.
|
||||
*
|
||||
* @param string $option The option name.
|
||||
|
@ -47,6 +47,12 @@ class OptionsResolver
|
||||
*/
|
||||
private $allowedValues = array();
|
||||
|
||||
/**
|
||||
* A list of accepted types for each option.
|
||||
* @var array
|
||||
*/
|
||||
private $allowedTypes = array();
|
||||
|
||||
/**
|
||||
* A list of filters transforming each resolved options.
|
||||
* @var array
|
||||
@ -222,6 +228,48 @@ class OptionsResolver
|
||||
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 OptionsResolver The resolver instance.
|
||||
*
|
||||
* @throws InvalidOptionsException If an option has not been defined for
|
||||
* which an allowed type is set.
|
||||
*/
|
||||
public function setAllowedTypes(array $allowedTypes)
|
||||
{
|
||||
$this->validateOptionNames(array_keys($allowedTypes));
|
||||
|
||||
$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 OptionsResolver The resolver instance.
|
||||
*
|
||||
* @throws InvalidOptionsException If an option has not been defined for
|
||||
* which an allowed type is set.
|
||||
*/
|
||||
public function addAllowedTypes(array $allowedTypes)
|
||||
{
|
||||
$this->validateOptionNames(array_keys($allowedTypes));
|
||||
|
||||
$this->allowedTypes = array_merge_recursive($this->allowedTypes, $allowedTypes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets filters that are applied on resolved options.
|
||||
*
|
||||
@ -312,8 +360,8 @@ class OptionsResolver
|
||||
// Resolve options
|
||||
$resolvedOptions = $combinedOptions->all();
|
||||
|
||||
// Validate against allowed values
|
||||
$this->validateOptionValues($resolvedOptions);
|
||||
$this->validateOptionTypes($resolvedOptions);
|
||||
|
||||
return $resolvedOptions;
|
||||
}
|
||||
@ -381,4 +429,44 @@ class OptionsResolver
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$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)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -276,6 +276,171 @@ class OptionsResolverTest extends \PHPUnit_Framework_TestCase
|
||||
));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
|
||||
*/
|
||||
|
Reference in New Issue
Block a user