8000 [OptionsResolver] Added a light-weight, low-level API for basic optio… · symfony/symfony@869ebc0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 869ebc0

Browse files
committed
[OptionsResolver] Added a light-weight, low-level API for basic option resolving
1 parent 2293556 commit 869ebc0

File tree

5 files changed

+689
-146
lines changed

5 files changed

+689
-146
lines changed

src/Symfony/Component/OptionsResolver/Options.php

Lines changed: 181 additions & 6 deletions
220
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\OptionsResolver;
1313

14+
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
15+
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
1416
use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
1517

1618
/**
@@ -56,6 +58,179 @@ class Options implements \ArrayAccess, \Iterator, \Countable
5658
*/
5759
private $reading = false;
5860

61+
/**
62+
* Merges options with an array of default values and throws an exception if
63+
* any of the options does not exist.
64+
*
65+
* @param array $options A list of option names and values
66+
* @param array $defaults The accepted options and their default values
67+
*
68+
* @return array The merged and validated options
69+
*
70+
* @throws InvalidOptionsException If any of the options is not present in
71+
* the defaults array
72+
*/
73+
public static function resolve(array $options, array $defaults)
74+
{
75+
static::accept($options, $defaults, true);
76+
77+
return array_replace($defaults, $options);
78+
}
79+
80+
/**
81+
* Validates that the given option names exist and throws an exception
82+
* otherwise.
83+
*
84+
* @param array $options A list of option names and values
85+
* @param string|array $acceptedOptions The accepted option(s), either passed
86+
* as single string or in the values of
87+
* the given array
88+
* @param bool $optionsAsKeys If set to true, the option names
89+
* should be passed in the keys of the
90+
* accepted options array
91+
*
92+
* @throws InvalidOptionsException If any of the options is not present in
93+
* the accepted options
94+
*/
95+
public static function accept(array $options, $acceptedOptions, $optionsAsKeys = false)
96+
{
97+
$acceptedOptions = (array) $acceptedOptions;
98+
99+
if (!$optionsAsKeys) {
100+
$acceptedOptions = array_flip($acceptedOptions);
101+
}
102+
103+
$diff = array_diff_key($options, $acceptedOptions);
104+
105+
if (count($diff) > 0) {
106+
ksort($acceptedOptions);
107+
ksort($diff);
108+
109+
throw new InvalidOptionsException(sprintf(
110+
(count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Known options are: "%s"',
111+
implode('", "', array_keys($diff)),
112+
implode('", "', array_keys($acceptedOptions))
113+
));
114+
}
115+
}
116+
117+
/**
118+
* Validates that the required options are given and throws an exception
119+
* otherwise.
120+
*
121+
* The option names may be any strings that don't consist exclusively of
122+
* digits. For example, "case1" is a valid option name, "1" is not.
123+
*
124+
* @param array $options A list of option names and values
125+
* @param string|array $requiredOptions The required option(s), either
126+
* passed as single string or in the
127+
* values of the given array
128+
* @param bool $optionsAsKeys If set to true, the option names
129+
* should be passed in the keys of the
130+
* required options array
131+
*
132+
* @throws MissingOptionsException If a required option is missing
133+
*/
134+
public static function require_(array $options, $requiredOptions, $optionsAsKeys = false)
135+
{
136+
$requiredOptions = (array) $requiredOptions;
137+
138+
if (!$optionsAsKeys) {
139+
$requiredOptions = array_flip($requiredOptions);
140+
}
141+
142+
$diff = array_diff_key($requiredOptions, $options);
143+
144+
if (count($diff) > 0) {
145+
ksort($diff);
146+
147+
throw new MissingOptionsException(sprintf(
148+
count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.',
149+
implode('", "', array_keys($diff))
150+
));
151+
}
152+
}
153+
154+
/**
155+
* Validates that the given options match the accepted types and
156+
* throws an exception otherwise.
157+
*
158+
* Accepted type names are any types for which a native "is_*()" function
159+
* exists. For example, "int" is an acceptable type name and will be checked
160+
* with the "is_int()" function.
161+
*
162+
* Types may also be passed as closures which return true or false.
163+
*
164+
* @param array $options A list of option names and values
165+
* @param array $acceptedTypes A mapping of option names to accepted option
166+
* types. The types may be given as
167+
* string/closure or as array of strings/closures
168+
*
169+
* @throws InvalidOptionsException If any of the types does not match the
170+
* accepted types of the option
171+
*/
172+
public static function validateTypes(array $options, array $acceptedTypes)
173+
{
174+
foreach ($acceptedTypes as $option => $optionTypes) {
175+
if (!array_key_exists($option, $options)) {
176+
continue;
177+
}
178+
179+
$value = $options[$option];
180+
$optionTypes = (array) $optionTypes;
181+
182+
foreach ($optionTypes as $type) {
183+
$isFunction = 'is_'.$type;
184+
185+
if (function_exists($isFunction) && $isFunction($value)) {
186+
continue 2;
187+
} elseif ($value instanceof $type) {
188+
continue 2;
189+
}
190+
}
191+
192+
$printableValue = is_object($value)
193+
? get_class($value)
194+
: (is_array($value)
195+
? 'Array'
196+
: (string) $value);
197+
198+
throw new InvalidOptionsException(sprintf(
199+
'The option "%s" with value "%s" is expected to be of type "%s"',
200+
$option,
201+
$printableValue,
202+
implode('", "', $optionTypes)
203+
));
204+
}
205+
}
206+
207+
/**
208+
* Validates that the given option values match the accepted values and
209+
* throws an exception otherwise.
210+
*
211+
* @param array $options A list of option names and values
212+
* @param array $acceptedValues A mapping of option names to accepted option
213+
* values. The option values must be given as
214+
* arrays
215+
*
216+
* @throws InvalidOptionsException If any of the values does not match the
217+
* accepted values of the option
218+
*/
219+
public static function validateValues(array $options, array $acceptedValues)
+
{
221+
foreach ($acceptedValues as $option => $optionValues) {
222+
if (isset($options[$option])) {
223+
if (is_array($optionValues) && !in_array($options[$option], $optionValues, true)) {
224+
throw new InvalidOptionsException(sprintf('The option "%s" has the value "%s", but is expected to be one of "%s"', $option, $options[$option], implode('", "', $optionValues)));
225+
}
226+
227+
if (is_callable($optionValues) && !call_user_func($optionValues, $options[$option])) {
228+
throw new InvalidOptionsException(sprintf('The option "%s" has the value "%s", which it is not valid', $option, $options[$option]));
229+
}
230+
}
231+
}
232+
}
233+
59234
/**
60235
* Sets the value of a given option.
61236
*
@@ -229,11 +404,11 @@ public function get($option)
229404
}
230405

231406
if (isset($this->lazy[$option])) {
232-
$this->resolve($option);
407+
$this->resolveOption($option);
233408
}
234409

235410
if (isset($this->normalizers[$option])) {
236-
$this->normalize($option);
411+
$this->normalizeOption($option);
237412
}
238413

239414
return $this->options[$option];
@@ -306,13 +481,13 @@ public function all()
306481
// Double check, in case the option has already been resolved
307482
// by cascade in the previous cycles
308483
if (isset($this->lazy[$option])) {
309-
$this->resolve($option);
484+
$this->resolveOption($option);
310485
}
311486
}
312487

313488
foreach ($this->normalizers as $option => $normalizer) {
314489
if (isset($this->normalizers[$option])) {
315-
$this->normalize($option);
490+
$this->normalizeOption($option);
316491
}
317492
}
318493

@@ -444,7 +619,7 @@ public function count()
444619
* @throws OptionDefinitionException If the option has a cyclic dependency
445620
* on another option.
446621
*/
447-
private function resolve($option)
622+
private function resolveOption($option)
448623
{
449624
// The code duplication with normalize() exists for performance
450625
// reasons, in order to save a method call.
@@ -482,7 +657,7 @@ private function resolve($option)
482657
* @throws OptionDefinitionException If the option has a cyclic dependency
483658
* on another option.
484659
*/
485-
private function normalize($option)
660+
private function normalizeOption($option)
486661
{
487662
// The code duplication with resolve() exists for performance
488663
// reasons, in order to save a method call.

0 commit comments

Comments
 (0)
0