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-10 15:37:03 +01:00
class OptionsResolver
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 ();
/**
* Creates a new instance .
*/
public function __construct ()
{
$this -> defaultOptions = new Options ();
}
/**
* Sets default option values .
*
2012-05-10 18:26:05 +01:00
* @ param array $defaultValues A list of option names as keys and default values
* as values . The option values may be closures
* of the following signatures :
2012-04-17 17:14:09 +01:00
*
2012-05-10 18:26:05 +01:00
* - function ( Options $options )
* - function ( Options $options , $previousValue )
2012-05-11 08:41:52 +01:00
*
* @ return OptionsResolver The resolver instance .
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
/**
* Replaces default option values .
*
* Old defaults are erased , which means that closures passed here can ' t
* access the previous default value . This may be useful to improve
* performance if the previous default value is calculated by an expensive
* closure .
*
2012-05-10 18:26:05 +01:00
* @ param array $defaultValues A list of option names as keys and default values
* as values . The option values may be closures
* of the following signature :
2012-04-23 11:10:43 +01:00
*
2012-05-10 18:26:05 +01:00
* - function ( Options $options )
2012-05-11 08:41:52 +01:00
*
* @ return OptionsResolver The resolver instance .
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
/**
* Sets optional options .
*
2012-05-24 04:31:42 +01:00
* This method declares a 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 .
2012-04-17 17:14:09 +01:00
*
2012-05-15 21:19:31 +01:00
* @ param array $optionNames A list of option names .
2012-04-17 17:14:09 +01:00
*
2012-05-11 08:41:52 +01:00
* @ return OptionsResolver The resolver instance .
*
2012-04-17 17:14:09 +01:00
* @ throws 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 ;
}
2012-05-11 08:41:52 +01:00
return $this ;
2012-04-17 17:14:09 +01:00
}
/**
* Sets required options .
*
2012-05-24 04:31:42 +01:00
* If these options are not passed to resolve () and no default has been set for
* them , an exception will be thrown .
2012-04-17 17:14:09 +01:00
*
2012-05-15 21:19:31 +01:00
* @ param array $optionNames A list of option names .
2012-04-17 17:14:09 +01:00
*
2012-05-11 08:41:52 +01:00
* @ return OptionsResolver The resolver instance .
*
2012-04-17 17:14:09 +01:00
* @ throws 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 ;
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
}
/**
* 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 .
*
2012-05-11 08:41:52 +01:00
* @ return OptionsResolver The resolver instance .
*
2012-05-24 04:31:42 +01:00
* @ throws InvalidOptionsException If an option has not been defined
* ( see { @ link isKnown ()}) for which
* an allowed value is set .
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
}
/**
* 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 .
*
2012-05-11 08:41:52 +01:00
* @ return OptionsResolver The resolver instance .
*
2012-04-17 17:14:09 +01:00
* @ throws InvalidOptionsException If an option has not been defined for
* which an allowed value is set .
*/
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-15 10:47:43 +01:00
/**
* 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 .
*
2012-05-15 21:19:31 +01:00
* @ param string $option The name of the option .
2012-05-15 10:47:43 +01:00
* @ return Boolean 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 .
*
2012-05-15 21:19:31 +01:00
* @ param string $option The name of the option .
2012-05-15 10:47:43 +01:00
* @ return Boolean Whether the option is required .
*/
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
/**
* Returns the combination of the default and the passed options .
*
2012-05-15 21:19:31 +01:00
* @ param array $options The custom option values .
2012-04-17 17:14:09 +01:00
*
2012-05-11 14:37:27 +01:00
* @ return array A list of options and their values .
2012-04-17 17:14:09 +01:00
*
* @ 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 .
*/
2012-05-10 15:37:03 +01:00
public function resolve ( array $options )
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
}
// Resolve options
2012-05-11 14:37:27 +01:00
$resolvedOptions = $combinedOptions -> all ();
2012-04-17 17:14:09 +01:00
// Validate against allowed values
2012-05-11 14:37:27 +01:00
$this -> validateOptionValues ( $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 )));
}
}
}
}