]> git.immae.eu Git - github/wallabag/wallabag.git/blame - vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolver.php
gitignore vendor
[github/wallabag/wallabag.git] / vendor / symfony / options-resolver / Symfony / Component / OptionsResolver / OptionsResolver.php
CommitLineData
4f5b44bd
NL
1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\OptionsResolver;
13
14use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
15use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
16use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
17
18/**
19 * Helper for merging default and concrete option values.
20 *
21 * @author Bernhard Schussek <bschussek@gmail.com>
22 * @author Tobias Schultze <http://tobion.de>
23 */
24class OptionsResolver implements OptionsResolverInterface
25{
26 /**
27 * The default option values.
28 * @var Options
29 */
30 private $defaultOptions;
31
32 /**
33 * The options known by the resolver.
34 * @var array
35 */
36 private $knownOptions = array();
37
38 /**
39 * The options without defaults that are required to be passed to resolve().
40 * @var array
41 */
42 private $requiredOptions = array();
43
44 /**
45 * A list of accepted values for each option.
46 * @var array
47 */
48 private $allowedValues = array();
49
50 /**
51 * A list of accepted types for each option.
52 * @var array
53 */
54 private $allowedTypes = array();
55
56 /**
57 * Creates a new instance.
58 */
59 public function __construct()
60 {
61 $this->defaultOptions = new Options();
62 }
63
64 /**
65 * Clones the resolver.
66 */
67 public function __clone()
68 {
69 $this->defaultOptions = clone $this->defaultOptions;
70 }
71
72 /**
73 * {@inheritdoc}
74 */
75 public function setDefaults(array $defaultValues)
76 {
77 foreach ($defaultValues as $option => $value) {
78 $this->defaultOptions->overload($option, $value);
79 $this->knownOptions[$option] = true;
80 unset($this->requiredOptions[$option]);
81 }
82
83 return $this;
84 }
85
86 /**
87 * {@inheritdoc}
88 */
89 public function replaceDefaults(array $defaultValues)
90 {
91 foreach ($defaultValues as $option => $value) {
92 $this->defaultOptions->set($option, $value);
93 $this->knownOptions[$option] = true;
94 unset($this->requiredOptions[$option]);
95 }
96
97 return $this;
98 }
99
100 /**
101 * {@inheritdoc}
102 */
103 public function setOptional(array $optionNames)
104 {
105 foreach ($optionNames as $key => $option) {
106 if (!is_int($key)) {
107 throw new OptionDefinitionException('You should not pass default values to setOptional()');
108 }
109
110 $this->knownOptions[$option] = true;
111 }
112
113 return $this;
114 }
115
116 /**
117 * {@inheritdoc}
118 */
119 public function setRequired(array $optionNames)
120 {
121 foreach ($optionNames as $key => $option) {
122 if (!is_int($key)) {
123 throw new OptionDefinitionException('You should not pass default values to setRequired()');
124 }
125
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;
130 }
131 }
132
133 return $this;
134 }
135
136 /**
137 * {@inheritdoc}
138 */
139 public function setAllowedValues(array $allowedValues)
140 {
141 $this->validateOptionsExistence($allowedValues);
142
143 $this->allowedValues = array_replace($this->allowedValues, $allowedValues);
144
145 return $this;
146 }
147
148 /**
149 * {@inheritdoc}
150 */
151 public function addAllowedValues(array $allowedValues)
152 {
153 $this->validateOptionsExistence($allowedValues);
154
155 $this->allowedValues = array_merge_recursive($this->allowedValues, $allowedValues);
156
157 return $this;
158 }
159
160 /**
161 * {@inheritdoc}
162 */
163 public function setAllowedTypes(array $allowedTypes)
164 {
165 $this->validateOptionsExistence($allowedTypes);
166
167 $this->allowedTypes = array_replace($this->allowedTypes, $allowedTypes);
168
169 return $this;
170 }
171
172 /**
173 * {@inheritdoc}
174 */
175 public function addAllowedTypes(array $allowedTypes)
176 {
177 $this->validateOptionsExistence($allowedTypes);
178
179 $this->allowedTypes = array_merge_recursive($this->allowedTypes, $allowedTypes);
180
181 return $this;
182 }
183
184 /**
185 * {@inheritdoc}
186 */
187 public function setNormalizers(array $normalizers)
188 {
189 $this->validateOptionsExistence($normalizers);
190
191 foreach ($normalizers as $option => $normalizer) {
192 $this->defaultOptions->setNormalizer($option, $normalizer);
193 }
194
195 return $this;
196 }
197
198 /**
199 * {@inheritdoc}
200 */
201 public function isKnown($option)
202 {
203 return isset($this->knownOptions[$option]);
204 }
205
206 /**
207 * {@inheritdoc}
208 */
209 public function isRequired($option)
210 {
211 return isset($this->requiredOptions[$option]);
212 }
213
214 /**
215 * {@inheritdoc}
216 */
217 public function resolve(array $options = array())
218 {
219 $this->validateOptionsExistence($options);
220 $this->validateOptionsCompleteness($options);
221
222 // Make sure this method can be called multiple times
223 $combinedOptions = clone $this->defaultOptions;
224
225 // Override options set by the user
226 foreach ($options as $option => $value) {
227 $combinedOptions->set($option, $value);
228 }
229
230 // Resolve options
231 $resolvedOptions = $combinedOptions->all();
232
233 $this->validateOptionValues($resolvedOptions);
234 $this->validateOptionTypes($resolvedOptions);
235
236 return $resolvedOptions;
237 }
238
239 /**
240 * Validates that the given option names exist and throws an exception
241 * otherwise.
242 *
243 * @param array $options An list of option names as keys.
244 *
245 * @throws InvalidOptionsException If any of the options has not been defined.
246 */
247 private function validateOptionsExistence(array $options)
248 {
249 $diff = array_diff_key($options, $this->knownOptions);
250
251 if (count($diff) > 0) {
252 ksort($this->knownOptions);
253 ksort($diff);
254
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))
259 ));
260 }
261 }
262
263 /**
264 * Validates that all required options are given and throws an exception
265 * otherwise.
266 *
267 * @param array $options An list of option names as keys.
268 *
269 * @throws MissingOptionsException If a required option is missing.
270 */
271 private function validateOptionsCompleteness(array $options)
272 {
273 $diff = array_diff_key($this->requiredOptions, $options);
274
275 if (count($diff) > 0) {
276 ksort($diff);
277
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))
281 ));
282 }
283 }
284
285 /**
286 * Validates that the given option values match the allowed values and
287 * throws an exception otherwise.
288 *
289 * @param array $options A list of option values.
290 *
291 * @throws InvalidOptionsException If any of the values does not match the
292 * allowed values of the option.
293 */
294 private function validateOptionValues(array $options)
295 {
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)));
299 }
300 }
301 }
302
303 /**
304 * Validates that the given options match the allowed types and
305 * throws an exception otherwise.
306 *
307 * @param array $options A list of options.
308 *
309 * @throws InvalidOptionsException If any of the types does not match the
310 * allowed types of the option.
311 */
312 private function validateOptionTypes(array $options)
313 {
314 foreach ($this->allowedTypes as $option => $allowedTypes) {
315 if (!array_key_exists($option, $options)) {
316 continue;
317 }
318
319 $value = $options[$option];
320 $allowedTypes = (array) $allowedTypes;
321
322 foreach ($allowedTypes as $type) {
323 $isFunction = 'is_'.$type;
324
325 if (function_exists($isFunction) && $isFunction($value)) {
326 continue 2;
327 } elseif ($value instanceof $type) {
328 continue 2;
329 }
330 }
331
332 $printableValue = is_object($value)
333 ? get_class($value)
334 : (is_array($value)
335 ? 'Array'
336 : (string) $value);
337
338 throw new InvalidOptionsException(sprintf(
339 'The option "%s" with value "%s" is expected to be of type "%s"',
340 $option,
341 $printableValue,
342 implode('", "', $allowedTypes)
343 ));
344 }
345 }
346}