4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\OptionsResolver
;
14 use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
;
15 use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
;
16 use Symfony\Component\OptionsResolver\Exception\MissingOptionsException
;
19 * Helper for merging default and concrete option values.
21 * @author Bernhard Schussek <bschussek@gmail.com>
22 * @author Tobias Schultze <http://tobion.de>
24 class OptionsResolver
implements OptionsResolverInterface
27 * The default option values.
30 private $defaultOptions;
33 * The options known by the resolver.
36 private $knownOptions = array();
39 * The options without defaults that are required to be passed to resolve().
42 private $requiredOptions = array();
45 * A list of accepted values for each option.
48 private $allowedValues = array();
51 * A list of accepted types for each option.
54 private $allowedTypes = array();
57 * Creates a new instance.
59 public function __construct()
61 $this->defaultOptions
= new Options();
65 * Clones the resolver.
67 public function __clone()
69 $this->defaultOptions
= clone $this->defaultOptions
;
75 public function setDefaults(array $defaultValues)
77 foreach ($defaultValues as $option => $value) {
78 $this->defaultOptions
->overload($option, $value);
79 $this->knownOptions
[$option] = true;
80 unset($this->requiredOptions
[$option]);
89 public function replaceDefaults(array $defaultValues)
91 foreach ($defaultValues as $option => $value) {
92 $this->defaultOptions
->set($option, $value);
93 $this->knownOptions
[$option] = true;
94 unset($this->requiredOptions
[$option]);
103 public function setOptional(array $optionNames)
105 foreach ($optionNames as $key => $option) {
107 throw new OptionDefinitionException('You should not pass default values to setOptional()');
110 $this->knownOptions
[$option] = true;
119 public function setRequired(array $optionNames)
121 foreach ($optionNames as $key => $option) {
123 throw new OptionDefinitionException('You should not pass default values to setRequired()');
126 $this->knownOptions
[$option] = true;
127 // set as required if no default has been set already
128 if (!isset($this->defaultOptions
[$option])) {
129 $this->requiredOptions
[$option] = true;
139 public function setAllowedValues(array $allowedValues)
141 $this->validateOptionsExistence($allowedValues);
143 $this->allowedValues
= array_replace($this->allowedValues
, $allowedValues);
151 public function addAllowedValues(array $allowedValues)
153 $this->validateOptionsExistence($allowedValues);
155 $this->allowedValues
= array_merge_recursive($this->allowedValues
, $allowedValues);
163 public function setAllowedTypes(array $allowedTypes)
165 $this->validateOptionsExistence($allowedTypes);
167 $this->allowedTypes
= array_replace($this->allowedTypes
, $allowedTypes);
175 public function addAllowedTypes(array $allowedTypes)
177 $this->validateOptionsExistence($allowedTypes);
179 $this->allowedTypes
= array_merge_recursive($this->allowedTypes
, $allowedTypes);
187 public function setNormalizers(array $normalizers)
189 $this->validateOptionsExistence($normalizers);
191 foreach ($normalizers as $option => $normalizer) {
192 $this->defaultOptions
->setNormalizer($option, $normalizer);
201 public function isKnown($option)
203 return isset($this->knownOptions
[$option]);
209 public function isRequired($option)
211 return isset($this->requiredOptions
[$option]);
217 public function resolve(array $options = array())
219 $this->validateOptionsExistence($options);
220 $this->validateOptionsCompleteness($options);
222 // Make sure this method can be called multiple times
223 $combinedOptions = clone $this->defaultOptions
;
225 // Override options set by the user
226 foreach ($options as $option => $value) {
227 $combinedOptions->set($option, $value);
231 $resolvedOptions = $combinedOptions->all();
233 $this->validateOptionValues($resolvedOptions);
234 $this->validateOptionTypes($resolvedOptions);
236 return $resolvedOptions;
240 * Validates that the given option names exist and throws an exception
243 * @param array $options An list of option names as keys.
245 * @throws InvalidOptionsException If any of the options has not been defined.
247 private function validateOptionsExistence(array $options)
249 $diff = array_diff_key($options, $this->knownOptions
);
251 if (count($diff) > 0) {
252 ksort($this->knownOptions
);
255 throw new InvalidOptionsException(sprintf(
256 (count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Known options are: "%s"',
257 implode('", "', array_keys($diff)),
258 implode('", "', array_keys($this->knownOptions
))
264 * Validates that all required options are given and throws an exception
267 * @param array $options An list of option names as keys.
269 * @throws MissingOptionsException If a required option is missing.
271 private function validateOptionsCompleteness(array $options)
273 $diff = array_diff_key($this->requiredOptions
, $options);
275 if (count($diff) > 0) {
278 throw new MissingOptionsException(sprintf(
279 count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.',
280 implode('", "', array_keys($diff))
286 * Validates that the given option values match the allowed values and
287 * throws an exception otherwise.
289 * @param array $options A list of option values.
291 * @throws InvalidOptionsException If any of the values does not match the
292 * allowed values of the option.
294 private function validateOptionValues(array $options)
296 foreach ($this->allowedValues
as $option => $allowedValues) {
297 if (isset($options[$option]) && !in_array($options[$option], $allowedValues, true)) {
298 throw new InvalidOptionsException(sprintf('The option "%s" has the value "%s", but is expected to be one of "%s"', $option, $options[$option], implode('", "', $allowedValues)));
304 * Validates that the given options match the allowed types and
305 * throws an exception otherwise.
307 * @param array $options A list of options.
309 * @throws InvalidOptionsException If any of the types does not match the
310 * allowed types of the option.
312 private function validateOptionTypes(array $options)
314 foreach ($this->allowedTypes
as $option => $allowedTypes) {
315 if (!array_key_exists($option, $options)) {
319 $value = $options[$option];
320 $allowedTypes = (array) $allowedTypes;
322 foreach ($allowedTypes as $type) {
323 $isFunction = 'is_'.$type;
325 if (function_exists($isFunction) && $isFunction($value)) {
327 } elseif ($value instanceof $type) {
332 $printableValue = is_object($value)
338 throw new InvalidOptionsException(sprintf(
339 'The option "%s" with value "%s" is expected to be of type "%s"',
342 implode('", "', $allowedTypes)