2012-04-17 17:14:09 +01:00
< ? 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 .
*/
2012-05-10 15:37:03 +01:00
namespace Symfony\Component\OptionsResolver ;
2012-04-17 17:14:09 +01:00
2012-05-10 15:37:03 +01:00
use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException ;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException ;
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException ;
2012-04-17 17:14:09 +01:00
/**
* Helper for merging default and concrete option values .
*
* @ author Bernhard Schussek < bschussek @ gmail . com >
2012-05-24 04:31:42 +01:00
* @ author Tobias Schultze < http :// tobion . de >
2012-04-17 17:14:09 +01:00
*/
2012-05-23 18:40:50 +01:00
class OptionsResolver implements OptionsResolverInterface
2012-04-17 17:14:09 +01:00
{
/**
* The default option values .
* @ var Options
*/
private $defaultOptions ;
/**
2012-05-10 15:37:03 +01:00
* The options known by the resolver .
2012-04-17 17:14:09 +01:00
* @ var array
*/
private $knownOptions = array ();
/**
2012-05-24 04:31:42 +01:00
* The options without defaults that are required to be passed to resolve () .
2012-04-17 17:14:09 +01:00
* @ var array
*/
private $requiredOptions = array ();
/**
* A list of accepted values for each option .
* @ var array
*/
private $allowedValues = array ();
2012-05-23 15:46:54 +01:00
/**
* A list of accepted types for each option .
* @ var array
*/
private $allowedTypes = array ();
2012-05-23 14:59:31 +01:00
/**
* A list of filters transforming each resolved options .
* @ var array
*/
private $filters = array ();
2012-04-17 17:14:09 +01:00
/**
* Creates a new instance .
*/
public function __construct ()
{
$this -> defaultOptions = new Options ();
}
2012-07-12 17:02:35 +01:00
/**
* Clones the resolver .
*/
public function __clone ()
{
$this -> defaultOptions = clone $this -> defaultOptions ;
}
2012-04-17 17:14:09 +01:00
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-04-17 17:14:09 +01:00
*/
public function setDefaults ( array $defaultValues )
{
foreach ( $defaultValues as $option => $value ) {
2012-05-11 14:37:27 +01:00
$this -> defaultOptions -> overload ( $option , $value );
2012-04-17 17:14:09 +01:00
$this -> knownOptions [ $option ] = true ;
2012-05-24 04:31:42 +01:00
unset ( $this -> requiredOptions [ $option ]);
2012-04-17 17:14:09 +01:00
}
2012-05-11 08:41:52 +01:00
return $this ;
2012-04-17 17:14:09 +01:00
}
2012-04-23 11:10:43 +01:00
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-04-23 11:10:43 +01:00
*/
public function replaceDefaults ( array $defaultValues )
{
foreach ( $defaultValues as $option => $value ) {
2012-05-11 14:37:27 +01:00
$this -> defaultOptions -> set ( $option , $value );
2012-04-23 11:10:43 +01:00
$this -> knownOptions [ $option ] = true ;
2012-05-24 04:31:42 +01:00
unset ( $this -> requiredOptions [ $option ]);
2012-04-23 11:10:43 +01:00
}
2012-05-11 08:41:52 +01:00
return $this ;
2012-04-23 11:10:43 +01:00
}
2012-04-17 17:14:09 +01:00
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-04-17 17:14:09 +01:00
*/
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 ;
}
2012-05-11 08:41:52 +01:00
return $this ;
2012-04-17 17:14:09 +01:00
}
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-04-17 17:14:09 +01:00
*/
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 ;
2012-05-24 04:31:42 +01:00
// set as required if no default has been set already
if ( ! isset ( $this -> defaultOptions [ $option ])) {
$this -> requiredOptions [ $option ] = true ;
}
2012-04-17 17:14:09 +01:00
}
2012-05-11 08:41:52 +01:00
return $this ;
2012-04-17 17:14:09 +01:00
}
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-04-17 17:14:09 +01:00
*/
public function setAllowedValues ( array $allowedValues )
{
2012-05-24 04:31:42 +01:00
$this -> validateOptionsExistence ( $allowedValues );
2012-04-17 17:14:09 +01:00
$this -> allowedValues = array_replace ( $this -> allowedValues , $allowedValues );
2012-05-11 08:41:52 +01:00
return $this ;
2012-04-17 17:14:09 +01:00
}
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-04-17 17:14:09 +01:00
*/
public function addAllowedValues ( array $allowedValues )
{
2012-05-24 04:31:42 +01:00
$this -> validateOptionsExistence ( $allowedValues );
2012-04-17 17:14:09 +01:00
$this -> allowedValues = array_merge_recursive ( $this -> allowedValues , $allowedValues );
2012-05-11 08:41:52 +01:00
return $this ;
2012-04-17 17:14:09 +01:00
}
2012-05-23 15:46:54 +01:00
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-05-23 15:46:54 +01:00
*/
public function setAllowedTypes ( array $allowedTypes )
{
2012-05-23 18:40:50 +01:00
$this -> validateOptionsExistence ( $allowedTypes );
2012-05-23 15:46:54 +01:00
$this -> allowedTypes = array_replace ( $this -> allowedTypes , $allowedTypes );
return $this ;
}
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-05-23 15:46:54 +01:00
*/
public function addAllowedTypes ( array $allowedTypes )
{
2012-05-23 18:40:50 +01:00
$this -> validateOptionsExistence ( $allowedTypes );
2012-05-23 15:46:54 +01:00
$this -> allowedTypes = array_merge_recursive ( $this -> allowedTypes , $allowedTypes );
return $this ;
}
2012-05-23 14:59:31 +01:00
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-05-23 14:59:31 +01:00
*/
public function setFilters ( array $filters )
{
$this -> validateOptionsExistence ( $filters );
$this -> filters = array_replace ( $this -> filters , $filters );
return $this ;
}
2012-05-15 10:47:43 +01:00
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-05-15 10:47:43 +01:00
*/
public function isKnown ( $option )
{
return isset ( $this -> knownOptions [ $option ]);
}
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-05-15 10:47:43 +01:00
*/
public function isRequired ( $option )
{
2012-05-24 04:31:42 +01:00
return isset ( $this -> requiredOptions [ $option ]);
2012-05-15 10:47:43 +01:00
}
2012-04-17 17:14:09 +01:00
/**
2012-05-23 18:40:50 +01:00
* { @ inheritdoc }
2012-04-17 17:14:09 +01:00
*/
2012-07-12 17:02:35 +01:00
public function resolve ( array $options = array ())
2012-04-17 17:14:09 +01:00
{
2012-05-24 04:31:42 +01:00
$this -> validateOptionsExistence ( $options );
$this -> validateOptionsCompleteness ( $options );
2012-04-17 17:14:09 +01:00
// Make sure this method can be called multiple times
$combinedOptions = clone $this -> defaultOptions ;
// Override options set by the user
foreach ( $options as $option => $value ) {
2012-05-11 14:37:27 +01:00
$combinedOptions -> set ( $option , $value );
2012-04-17 17:14:09 +01:00
}
2012-05-23 14:59:31 +01:00
// Apply filters
foreach ( $this -> filters as $option => $filter ) {
$combinedOptions -> overload ( $option , $filter );
}
2012-04-17 17:14:09 +01:00
// Resolve options
2012-05-11 14:37:27 +01:00
$resolvedOptions = $combinedOptions -> all ();
2012-04-17 17:14:09 +01:00
2012-05-11 14:37:27 +01:00
$this -> validateOptionValues ( $resolvedOptions );
2012-05-23 15:46:54 +01:00
$this -> validateOptionTypes ( $resolvedOptions );
2012-04-17 17:14:09 +01:00
2012-05-11 14:37:27 +01:00
return $resolvedOptions ;
2012-04-17 17:14:09 +01:00
}
/**
* Validates that the given option names exist and throws an exception
* otherwise .
*
2012-05-24 04:31:42 +01:00
* @ param array $options An list of option names as keys .
2012-04-17 17:14:09 +01:00
*
2012-05-24 04:31:42 +01:00
* @ throws InvalidOptionsException If any of the options has not been defined .
2012-04-17 17:14:09 +01:00
*/
2012-05-24 04:31:42 +01:00
private function validateOptionsExistence ( array $options )
2012-04-17 17:14:09 +01:00
{
2012-05-24 04:31:42 +01:00
$diff = array_diff_key ( $options , $this -> knownOptions );
2012-04-17 17:14:09 +01:00
if ( count ( $diff ) > 0 ) {
2012-05-24 01:48:57 +01:00
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 ))
));
2012-04-17 17:14:09 +01:00
}
2012-05-24 04:31:42 +01:00
}
2012-04-17 17:14:09 +01:00
2012-05-24 04:31:42 +01:00
/**
* 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 );
2012-04-17 17:14:09 +01:00
if ( count ( $diff ) > 0 ) {
2012-05-24 01:48:57 +01:00
ksort ( $diff );
2012-05-11 09:31:19 +01:00
2012-05-24 01:48:57 +01:00
throw new MissingOptionsException ( sprintf (
count ( $diff ) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.' ,
implode ( '", "' , array_keys ( $diff ))
));
2012-04-17 17:14:09 +01:00
}
}
/**
* Validates that the given option values match the allowed values and
* throws an exception otherwise .
*
2012-05-15 21:19:31 +01:00
* @ param array $options A list of option values .
2012-04-17 17:14:09 +01:00
*
2012-05-24 04:31:42 +01:00
* @ throws InvalidOptionsException If any of the values does not match the
* allowed values of the option .
2012-04-17 17:14:09 +01:00
*/
private function validateOptionValues ( array $options )
{
foreach ( $this -> allowedValues as $option => $allowedValues ) {
if ( ! 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 )));
}
}
}
2012-05-23 15:46:54 +01:00
/**
* 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 )
));
}
}
2012-04-17 17:14:09 +01:00
}