diff options
Diffstat (limited to 'vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList')
5 files changed, 1156 insertions, 0 deletions
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php new file mode 100644 index 00000000..f9d381cd --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php | |||
@@ -0,0 +1,510 @@ | |||
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 | |||
12 | namespace Symfony\Component\Form\Extension\Core\ChoiceList; | ||
13 | |||
14 | use Symfony\Component\Form\FormConfigBuilder; | ||
15 | use Symfony\Component\Form\Exception\UnexpectedTypeException; | ||
16 | use Symfony\Component\Form\Exception\InvalidConfigurationException; | ||
17 | use Symfony\Component\Form\Exception\InvalidArgumentException; | ||
18 | use Symfony\Component\Form\Extension\Core\View\ChoiceView; | ||
19 | |||
20 | /** | ||
21 | * A choice list for choices of arbitrary data types. | ||
22 | * | ||
23 | * Choices and labels are passed in two arrays. The indices of the choices | ||
24 | * and the labels should match. Choices may also be given as hierarchy of | ||
25 | * unlimited depth by creating nested arrays. The title of the sub-hierarchy | ||
26 | * can be stored in the array key pointing to the nested array. The topmost | ||
27 | * level of the hierarchy may also be a \Traversable. | ||
28 | * | ||
29 | * <code> | ||
30 | * $choices = array(true, false); | ||
31 | * $labels = array('Agree', 'Disagree'); | ||
32 | * $choiceList = new ChoiceList($choices, $labels); | ||
33 | * </code> | ||
34 | * | ||
35 | * @author Bernhard Schussek <bschussek@gmail.com> | ||
36 | */ | ||
37 | class ChoiceList implements ChoiceListInterface | ||
38 | { | ||
39 | /** | ||
40 | * The choices with their indices as keys. | ||
41 | * | ||
42 | * @var array | ||
43 | */ | ||
44 | private $choices = array(); | ||
45 | |||
46 | /** | ||
47 | * The choice values with the indices of the matching choices as keys. | ||
48 | * | ||
49 | * @var array | ||
50 | */ | ||
51 | private $values = array(); | ||
52 | |||
53 | /** | ||
54 | * The preferred view objects as hierarchy containing also the choice groups | ||
55 | * with the indices of the matching choices as bottom-level keys. | ||
56 | * | ||
57 | * @var array | ||
58 | */ | ||
59 | private $preferredViews = array(); | ||
60 | |||
61 | /** | ||
62 | * The non-preferred view objects as hierarchy containing also the choice | ||
63 | * groups with the indices of the matching choices as bottom-level keys. | ||
64 | * | ||
65 | * @var array | ||
66 | */ | ||
67 | private $remainingViews = array(); | ||
68 | |||
69 | /** | ||
70 | * Creates a new choice list. | ||
71 | * | ||
72 | * @param array|\Traversable $choices The array of choices. Choices may also be given | ||
73 | * as hierarchy of unlimited depth. Hierarchies are | ||
74 | * created by creating nested arrays. The title of | ||
75 | * the sub-hierarchy can be stored in the array | ||
76 | * key pointing to the nested array. The topmost | ||
77 | * level of the hierarchy may also be a \Traversable. | ||
78 | * @param array $labels The array of labels. The structure of this array | ||
79 | * should match the structure of $choices. | ||
80 | * @param array $preferredChoices A flat array of choices that should be | ||
81 | * presented to the user with priority. | ||
82 | * | ||
83 | * @throws UnexpectedTypeException If the choices are not an array or \Traversable. | ||
84 | */ | ||
85 | public function __construct($choices, array $labels, array $preferredChoices = array()) | ||
86 | { | ||
87 | if (!is_array($choices) && !$choices instanceof \Traversable) { | ||
88 | throw new UnexpectedTypeException($choices, 'array or \Traversable'); | ||
89 | } | ||
90 | |||
91 | $this->initialize($choices, $labels, $preferredChoices); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Initializes the list with choices. | ||
96 | * | ||
97 | * Safe to be called multiple times. The list is cleared on every call. | ||
98 | * | ||
99 | * @param array|\Traversable $choices The choices to write into the list. | ||
100 | * @param array $labels The labels belonging to the choices. | ||
101 | * @param array $preferredChoices The choices to display with priority. | ||
102 | */ | ||
103 | protected function initialize($choices, array $labels, array $preferredChoices) | ||
104 | { | ||
105 | $this->choices = array(); | ||
106 | $this->values = array(); | ||
107 | $this->preferredViews = array(); | ||
108 | $this->remainingViews = array(); | ||
109 | |||
110 | $this->addChoices( | ||
111 | $this->preferredViews, | ||
112 | $this->remainingViews, | ||
113 | $choices, | ||
114 | $labels, | ||
115 | $preferredChoices | ||
116 | ); | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * {@inheritdoc} | ||
121 | */ | ||
122 | public function getChoices() | ||
123 | { | ||
124 | return $this->choices; | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * {@inheritdoc} | ||
129 | */ | ||
130 | public function getValues() | ||
131 | { | ||
132 | return $this->values; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * {@inheritdoc} | ||
137 | */ | ||
138 | public function getPreferredViews() | ||
139 | { | ||
140 | return $this->preferredViews; | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * {@inheritdoc} | ||
145 | */ | ||
146 | public function getRemainingViews() | ||
147 | { | ||
148 | return $this->remainingViews; | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * {@inheritdoc} | ||
153 | */ | ||
154 | public function getChoicesForValues(array $values) | ||
155 | { | ||
156 | $values = $this->fixValues($values); | ||
157 | $choices = array(); | ||
158 | |||
159 | foreach ($values as $j => $givenValue) { | ||
160 | foreach ($this->values as $i => $value) { | ||
161 | if ($value === $givenValue) { | ||
162 | $choices[] = $this->choices[$i]; | ||
163 | unset($values[$j]); | ||
164 | |||
165 | if (0 === count($values)) { | ||
166 | break 2; | ||
167 | } | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | |||
172 | return $choices; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * {@inheritdoc} | ||
177 | */ | ||
178 | public function getValuesForChoices(array $choices) | ||
179 | { | ||
180 | $choices = $this->fixChoices($choices); | ||
181 | $values = array(); | ||
182 | |||
183 | foreach ($this->choices as $i => $choice) { | ||
184 | foreach ($choices as $j => $givenChoice) { | ||
185 | if ($choice === $givenChoice) { | ||
186 | $values[] = $this->values[$i]; | ||
187 | unset($choices[$j]); | ||
188 | |||
189 | if (0 === count($choices)) { | ||
190 | break 2; | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | |||
196 | return $values; | ||
197 | } | ||
198 | |||
199 | /** | ||
200 | * {@inheritdoc} | ||
201 | */ | ||
202 | public function getIndicesForChoices(array $choices) | ||
203 | { | ||
204 | $choices = $this->fixChoices($choices); | ||
205 | $indices = array(); | ||
206 | |||
207 | foreach ($this->choices as $i => $choice) { | ||
208 | foreach ($choices as $j => $givenChoice) { | ||
209 | if ($choice === $givenChoice) { | ||
210 | $indices[] = $i; | ||
211 | unset($choices[$j]); | ||
212 | |||
213 | if (0 === count($choices)) { | ||
214 | break 2; | ||
215 | } | ||
216 | } | ||
217 | } | ||
218 | } | ||
219 | |||
220 | return $indices; | ||
221 | } | ||
222 | |||
223 | /** | ||
224 | * {@inheritdoc} | ||
225 | */ | ||
226 | public function getIndicesForValues(array $values) | ||
227 | { | ||
228 | $values = $this->fixValues($values); | ||
229 | $indices = array(); | ||
230 | |||
231 | foreach ($this->values as $i => $value) { | ||
232 | foreach ($values as $j => $givenValue) { | ||
233 | if ($value === $givenValue) { | ||
234 | $indices[] = $i; | ||
235 | unset($values[$j]); | ||
236 | |||
237 | if (0 === count($values)) { | ||
238 | break 2; | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | } | ||
243 | |||
244 | return $indices; | ||
245 | } | ||
246 | |||
247 | /** | ||
248 | * Recursively adds the given choices to the list. | ||
249 | * | ||
250 | * @param array $bucketForPreferred The bucket where to store the preferred | ||
251 | * view objects. | ||
252 | * @param array $bucketForRemaining The bucket where to store the | ||
253 | * non-preferred view objects. | ||
254 | * @param array|\Traversable $choices The list of choices. | ||
255 | * @param array $labels The labels corresponding to the choices. | ||
256 | * @param array $preferredChoices The preferred choices. | ||
257 | * | ||
258 | * @throws InvalidArgumentException If the structures of the choices and labels array do not match. | ||
259 | * @throws InvalidConfigurationException If no valid value or index could be created for a choice. | ||
260 | */ | ||
261 | protected function addChoices(array &$bucketForPreferred, array &$bucketForRemaining, $choices, array $labels, array $preferredChoices) | ||
262 | { | ||
263 | // Add choices to the nested buckets | ||
264 | foreach ($choices as $group => $choice) { | ||
265 | if (!array_key_exists($group, $labels)) { | ||
266 | throw new InvalidArgumentException('The structures of the choices and labels array do not match.'); | ||
267 | } | ||
268 | |||
269 | if (is_array($choice)) { | ||
270 | // Don't do the work if the array is empty | ||
271 | if (count($choice) > 0) { | ||
272 | $this->addChoiceGroup( | ||
273 | $group, | ||
274 | $bucketForPreferred, | ||
275 | $bucketForRemaining, | ||
276 | $choice, | ||
277 | $labels[$group], | ||
278 | $preferredChoices | ||
279 | ); | ||
280 | } | ||
281 | } else { | ||
282 | $this->addChoice( | ||
283 | $bucketForPreferred, | ||
284 | $bucketForRemaining, | ||
285 | $choice, | ||
286 | $labels[$group], | ||
287 | $preferredChoices | ||
288 | ); | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | /** | ||
294 | * Recursively adds a choice group. | ||
295 | * | ||
296 | * @param string $group The name of the group. | ||
297 | * @param array $bucketForPreferred The bucket where to store the preferred | ||
298 | * view objects. | ||
299 | * @param array $bucketForRemaining The bucket where to store the | ||
300 | * non-preferred view objects. | ||
301 | * @param array $choices The list of choices in the group. | ||
302 | * @param array $labels The labels corresponding to the choices in the group. | ||
303 | * @param array $preferredChoices The preferred choices. | ||
304 | * | ||
305 | * @throws InvalidConfigurationException If no valid value or index could be created for a choice. | ||
306 | */ | ||
307 | protected function addChoiceGroup($group, array &$bucketForPreferred, array &$bucketForRemaining, array $choices, array $labels, array $preferredChoices) | ||
308 | { | ||
309 | // If this is a choice group, create a new level in the choice | ||
310 | // key hierarchy | ||
311 | $bucketForPreferred[$group] = array(); | ||
312 | $bucketForRemaining[$group] = array(); | ||
313 | |||
314 | $this->addChoices( | ||
315 | $bucketForPreferred[$group], | ||
316 | $bucketForRemaining[$group], | ||
317 | $choices, | ||
318 | $labels, | ||
319 | $preferredChoices | ||
320 | ); | ||
321 | |||
322 | // Remove child levels if empty | ||
323 | if (empty($bucketForPreferred[$group])) { | ||
324 | unset($bucketForPreferred[$group]); | ||
325 | } | ||
326 | if (empty($bucketForRemaining[$group])) { | ||
327 | unset($bucketForRemaining[$group]); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | /** | ||
332 | * Adds a new choice. | ||
333 | * | ||
334 | * @param array $bucketForPreferred The bucket where to store the preferred | ||
335 | * view objects. | ||
336 | * @param array $bucketForRemaining The bucket where to store the | ||
337 | * non-preferred view objects. | ||
338 | * @param mixed $choice The choice to add. | ||
339 | * @param string $label The label for the choice. | ||
340 | * @param array $preferredChoices The preferred choices. | ||
341 | * | ||
342 | * @throws InvalidConfigurationException If no valid value or index could be created. | ||
343 | */ | ||
344 | protected function addChoice(array &$bucketForPreferred, array &$bucketForRemaining, $choice, $label, array $preferredChoices) | ||
345 | { | ||
346 | $index = $this->createIndex($choice); | ||
347 | |||
348 | if ('' === $index || null === $index || !FormConfigBuilder::isValidName((string) $index)) { | ||
349 | throw new InvalidConfigurationException(sprintf('The index "%s" created by the choice list is invalid. It should be a valid, non-empty Form name.', $index)); | ||
350 | } | ||
351 | |||
352 | $value = $this->createValue($choice); | ||
353 | |||
354 | if (!is_string($value)) { | ||
355 | throw new InvalidConfigurationException(sprintf('The value created by the choice list is of type "%s", but should be a string.', gettype($value))); | ||
356 | } | ||
357 | |||
358 | $view = new ChoiceView($choice, $value, $label); | ||
359 | |||
360 | $this->choices[$index] = $this->fixChoice($choice); | ||
361 | $this->values[$index] = $value; | ||
362 | |||
363 | if ($this->isPreferred($choice, $preferredChoices)) { | ||
364 | $bucketForPreferred[$index] = $view; | ||
365 | } else { | ||
366 | $bucketForRemaining[$index] = $view; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | /** | ||
371 | * Returns whether the given choice should be preferred judging by the | ||
372 | * given array of preferred choices. | ||
373 | * | ||
374 | * Extension point to optimize performance by changing the structure of the | ||
375 | * $preferredChoices array. | ||
376 | * | ||
377 | * @param mixed $choice The choice to test. | ||
378 | * @param array $preferredChoices An array of preferred choices. | ||
379 | * | ||
380 | * @return Boolean Whether the choice is preferred. | ||
381 | */ | ||
382 | protected function isPreferred($choice, array $preferredChoices) | ||
383 | { | ||
384 | return false !== array_search($choice, $preferredChoices, true); | ||
385 | } | ||
386 | |||
387 | /** | ||
388 | * Creates a new unique index for this choice. | ||
389 | * | ||
390 | * Extension point to change the indexing strategy. | ||
391 | * | ||
392 | * @param mixed $choice The choice to create an index for | ||
393 | * | ||
394 | * @return integer|string A unique index containing only ASCII letters, | ||
395 | * digits and underscores. | ||
396 | */ | ||
397 | protected function createIndex($choice) | ||
398 | { | ||
399 | return count($this->choices); | ||
400 | } | ||
401 | |||
402 | /** | ||
403 | * Creates a new unique value for this choice. | ||
404 | * | ||
405 | * By default, an integer is generated since it cannot be guaranteed that | ||
406 | * all values in the list are convertible to (unique) strings. Subclasses | ||
407 | * can override this behaviour if they can guarantee this property. | ||
408 | * | ||
409 | * @param mixed $choice The choice to create a value for | ||
410 | * | ||
411 | * @return string A unique string. | ||
412 | */ | ||
413 | protected function createValue($choice) | ||
414 | { | ||
415 | return (string) count($this->values); | ||
416 | } | ||
417 | |||
418 | /** | ||
419 | * Fixes the data type of the given choice value to avoid comparison | ||
420 | * problems. | ||
421 | * | ||
422 | * @param mixed $value The choice value. | ||
423 | * | ||
424 | * @return string The value as string. | ||
425 | */ | ||
426 | protected function fixValue($value) | ||
427 | { | ||
428 | return (string) $value; | ||
429 | } | ||
430 | |||
431 | /** | ||
432 | * Fixes the data types of the given choice values to avoid comparison | ||
433 | * problems. | ||
434 | * | ||
435 | * @param array $values The choice values. | ||
436 | * | ||
437 | * @return array The values as strings. | ||
438 | */ | ||
439 | protected function fixValues(array $values) | ||
440 | { | ||
441 | foreach ($values as $i => $value) { | ||
442 | $values[$i] = $this->fixValue($value); | ||
443 | } | ||
444 | |||
445 | return $values; | ||
446 | } | ||
447 | |||
448 | /** | ||
449 | * Fixes the data type of the given choice index to avoid comparison | ||
450 | * problems. | ||
451 | * | ||
452 | * @param mixed $index The choice index. | ||
453 | * | ||
454 | * @return integer|string The index as PHP array key. | ||
455 | */ | ||
456 | protected function fixIndex($index) | ||
457 | { | ||
458 | if (is_bool($index) || (string) (int) $index === (string) $index) { | ||
459 | return (int) $index; | ||
460 | } | ||
461 | |||
462 | return (string) $index; | ||
463 | } | ||
464 | |||
465 | /** | ||
466 | * Fixes the data types of the given choice indices to avoid comparison | ||
467 | * problems. | ||
468 | * | ||
469 | * @param array $indices The choice indices. | ||
470 | * | ||
471 | * @return array The indices as strings. | ||
472 | */ | ||
473 | protected function fixIndices(array $indices) | ||
474 | { | ||
475 | foreach ($indices as $i => $index) { | ||
476 | $indices[$i] = $this->fixIndex($index); | ||
477 | } | ||
478 | |||
479 | return $indices; | ||
480 | } | ||
481 | |||
482 | /** | ||
483 | * Fixes the data type of the given choice to avoid comparison problems. | ||
484 | * | ||
485 | * Extension point. In this implementation, choices are guaranteed to | ||
486 | * always maintain their type and thus can be typesafely compared. | ||
487 | * | ||
488 | * @param mixed $choice The choice. | ||
489 | * | ||
490 | * @return mixed The fixed choice. | ||
491 | */ | ||
492 | protected function fixChoice($choice) | ||
493 | { | ||
494 | return $choice; | ||
495 | } | ||
496 | |||
497 | /** | ||
498 | * Fixes the data type of the given choices to avoid comparison problems. | ||
499 | * | ||
500 | * @param array $choices The choices. | ||
501 | * | ||
502 | * @return array The fixed choices. | ||
503 | * | ||
504 | * @see fixChoice | ||
505 | */ | ||
506 | protected function fixChoices(array $choices) | ||
507 | { | ||
508 | return $choices; | ||
509 | } | ||
510 | } | ||
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.php new file mode 100644 index 00000000..099ace82 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.php | |||
@@ -0,0 +1,149 @@ | |||
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 | |||
12 | namespace Symfony\Component\Form\Extension\Core\ChoiceList; | ||
13 | |||
14 | /** | ||
15 | * Contains choices that can be selected in a form field. | ||
16 | * | ||
17 | * Each choice has three different properties: | ||
18 | * | ||
19 | * - Choice: The choice that should be returned to the application by the | ||
20 | * choice field. Can be any scalar value or an object, but no | ||
21 | * array. | ||
22 | * - Label: A text representing the choice that is displayed to the user. | ||
23 | * - Value: A uniquely identifying value that can contain arbitrary | ||
24 | * characters, but no arrays or objects. This value is displayed | ||
25 | * in the HTML "value" attribute. | ||
26 | * | ||
27 | * @author Bernhard Schussek <bschussek@gmail.com> | ||
28 | */ | ||
29 | interface ChoiceListInterface | ||
30 | { | ||
31 | /** | ||
32 | * Returns the list of choices | ||
33 | * | ||
34 | * @return array The choices with their indices as keys | ||
35 | */ | ||
36 | public function getChoices(); | ||
37 | |||
38 | /** | ||
39 | * Returns the values for the choices | ||
40 | * | ||
41 | * @return array The values with the corresponding choice indices as keys | ||
42 | */ | ||
43 | public function getValues(); | ||
44 | |||
45 | /** | ||
46 | * Returns the choice views of the preferred choices as nested array with | ||
47 | * the choice groups as top-level keys. | ||
48 | * | ||
49 | * Example: | ||
50 | * | ||
51 | * <source> | ||
52 | * array( | ||
53 | * 'Group 1' => array( | ||
54 | * 10 => ChoiceView object, | ||
55 | * 20 => ChoiceView object, | ||
56 | * ), | ||
57 | * 'Group 2' => array( | ||
58 | * 30 => ChoiceView object, | ||
59 | * ), | ||
60 | * ) | ||
61 | * </source> | ||
62 | * | ||
63 | * @return array A nested array containing the views with the corresponding | ||
64 | * choice indices as keys on the lowest levels and the choice | ||
65 | * group names in the keys of the higher levels | ||
66 | */ | ||
67 | public function getPreferredViews(); | ||
68 | |||
69 | /** | ||
70 | * Returns the choice views of the choices that are not preferred as nested | ||
71 | * array with the choice groups as top-level keys. | ||
72 | * | ||
73 | * Example: | ||
74 | * | ||
75 | * <source> | ||
76 | * array( | ||
77 | * 'Group 1' => array( | ||
78 | * 10 => ChoiceView object, | ||
79 | * 20 => ChoiceView object, | ||
80 | * ), | ||
81 | * 'Group 2' => array( | ||
82 | * 30 => ChoiceView object, | ||
83 | * ), | ||
84 | * ) | ||
85 | * </source> | ||
86 | * | ||
87 | * @return array A nested array containing the views with the corresponding | ||
88 | * choice indices as keys on the lowest levels and the choice | ||
89 | * group names in the keys of the higher levels | ||
90 | * | ||
91 | * @see getPreferredValues | ||
92 | */ | ||
93 | public function getRemainingViews(); | ||
94 | |||
95 | /** | ||
96 | * Returns the choices corresponding to the given values. | ||
97 | * | ||
98 | * The choices can have any data type. | ||
99 | * | ||
100 | * @param array $values An array of choice values. Not existing values in | ||
101 | * this array are ignored | ||
102 | * | ||
103 | * @return array An array of choices with ascending, 0-based numeric keys | ||
104 | */ | ||
105 | public function getChoicesForValues(array $values); | ||
106 | |||
107 | /** | ||
108 | * Returns the values corresponding to the given choices. | ||
109 | * | ||
110 | * The values must be strings. | ||
111 | * | ||
112 | * @param array $choices An array of choices. Not existing choices in this | ||
113 | * array are ignored | ||
114 | * | ||
115 | * @return array An array of choice values with ascending, 0-based numeric | ||
116 | * keys | ||
117 | */ | ||
118 | public function getValuesForChoices(array $choices); | ||
119 | |||
120 | /** | ||
121 | * Returns the indices corresponding to the given choices. | ||
122 | * | ||
123 | * The indices must be positive integers or strings accepted by | ||
124 | * {@link FormConfigBuilder::validateName()}. | ||
125 | * | ||
126 | * The index "placeholder" is internally reserved. | ||
127 | * | ||
128 | * @param array $choices An array of choices. Not existing choices in this | ||
129 | * array are ignored | ||
130 | * | ||
131 | * @return array An array of indices with ascending, 0-based numeric keys | ||
132 | */ | ||
133 | public function getIndicesForChoices(array $choices); | ||
134 | |||
135 | /** | ||
136 | * Returns the indices corresponding to the given values. | ||
137 | * | ||
138 | * The indices must be positive integers or strings accepted by | ||
139 | * {@link FormConfigBuilder::validateName()}. | ||
140 | * | ||
141 | * The index "placeholder" is internally reserved. | ||
142 | * | ||
143 | * @param array $values An array of choice values. Not existing values in | ||
144 | * this array are ignored | ||
145 | * | ||
146 | * @return array An array of indices with ascending, 0-based numeric keys | ||
147 | */ | ||
148 | public function getIndicesForValues(array $values); | ||
149 | } | ||
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.php new file mode 100644 index 00000000..996f900c --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.php | |||
@@ -0,0 +1,149 @@ | |||
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 | |||
12 | namespace Symfony\Component\Form\Extension\Core\ChoiceList; | ||
13 | |||
14 | use Symfony\Component\Form\Exception\InvalidArgumentException; | ||
15 | |||
16 | /** | ||
17 | * A choice list that is loaded lazily | ||
18 | * | ||
19 | * This list loads itself as soon as any of the getters is accessed for the | ||
20 | * first time. You should implement loadChoiceList() in your child classes, | ||
21 | * which should return a ChoiceListInterface instance. | ||
22 | * | ||
23 | * @author Bernhard Schussek <bschussek@gmail.com> | ||
24 | */ | ||
25 | abstract class LazyChoiceList implements ChoiceListInterface | ||
26 | { | ||
27 | /** | ||
28 | * The loaded choice list | ||
29 | * | ||
30 | * @var ChoiceListInterface | ||
31 | */ | ||
32 | private $choiceList; | ||
33 | |||
34 | /** | ||
35 | * {@inheritdoc} | ||
36 | */ | ||
37 | public function getChoices() | ||
38 | { | ||
39 | if (!$this->choiceList) { | ||
40 | $this->load(); | ||
41 | } | ||
42 | |||
43 | return $this->choiceList->getChoices(); | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * {@inheritdoc} | ||
48 | */ | ||
49 | public function getValues() | ||
50 | { | ||
51 | if (!$this->choiceList) { | ||
52 | $this->load(); | ||
53 | } | ||
54 | |||
55 | return $this->choiceList->getValues(); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * {@inheritdoc} | ||
60 | */ | ||
61 | public function getPreferredViews() | ||
62 | { | ||
63 | if (!$this->choiceList) { | ||
64 | $this->load(); | ||
65 | } | ||
66 | |||
67 | return $this->choiceList->getPreferredViews(); | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * {@inheritdoc} | ||
72 | */ | ||
73 | public function getRemainingViews() | ||
74 | { | ||
75 | if (!$this->choiceList) { | ||
76 | $this->load(); | ||
77 | } | ||
78 | |||
79 | return $this->choiceList->getRemainingViews(); | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * {@inheritdoc} | ||
84 | */ | ||
85 | public function getChoicesForValues(array $values) | ||
86 | { | ||
87 | if (!$this->choiceList) { | ||
88 | $this->load(); | ||
89 | } | ||
90 | |||
91 | return $this->choiceList->getChoicesForValues($values); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * {@inheritdoc} | ||
96 | */ | ||
97 | public function getValuesForChoices(array $choices) | ||
98 | { | ||
99 | if (!$this->choiceList) { | ||
100 | $this->load(); | ||
101 | } | ||
102 | |||
103 | return $this->choiceList->getValuesForChoices($choices); | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * {@inheritdoc} | ||
108 | */ | ||
109 | public function getIndicesForChoices(array $choices) | ||
110 | { | ||
111 | if (!$this->choiceList) { | ||
112 | $this->load(); | ||
113 | } | ||
114 | |||
115 | return $this->choiceList->getIndicesForChoices($choices); | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * {@inheritdoc} | ||
120 | */ | ||
121 | public function getIndicesForValues(array $values) | ||
122 | { | ||
123 | if (!$this->choiceList) { | ||
124 | $this->load(); | ||
125 | } | ||
126 | |||
127 | return $this->choiceList->getIndicesForValues($values); | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Loads the choice list | ||
132 | * | ||
133 | * Should be implemented by child classes. | ||
134 | * | ||
135 | * @return ChoiceListInterface The loaded choice list | ||
136 | */ | ||
137 | abstract protected function loadChoiceList(); | ||
138 | |||
139 | private function load() | ||
140 | { | ||
141 | $choiceList = $this->loadChoiceList(); | ||
142 | |||
143 | if (!$choiceList instanceof ChoiceListInterface) { | ||
144 | throw new InvalidArgumentException(sprintf('loadChoiceList() should return a ChoiceListInterface instance. Got %s', gettype($choiceList))); | ||
145 | } | ||
146 | |||
147 | $this->choiceList = $choiceList; | ||
148 | } | ||
149 | } | ||
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php new file mode 100644 index 00000000..0a153883 --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php | |||
@@ -0,0 +1,184 @@ | |||
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 | |||
12 | namespace Symfony\Component\Form\Extension\Core\ChoiceList; | ||
13 | |||
14 | use Symfony\Component\Form\Exception\StringCastException; | ||
15 | use Symfony\Component\Form\Exception\InvalidArgumentException; | ||
16 | use Symfony\Component\PropertyAccess\PropertyPath; | ||
17 | use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; | ||
18 | use Symfony\Component\PropertyAccess\PropertyAccess; | ||
19 | use Symfony\Component\PropertyAccess\PropertyAccessorInterface; | ||
20 | |||
21 | /** | ||
22 | * A choice list for object choices. | ||
23 | * | ||
24 | * Supports generation of choice labels, choice groups and choice values | ||
25 | * by calling getters of the object (or associated objects). | ||
26 | * | ||
27 | * <code> | ||
28 | * $choices = array($user1, $user2); | ||
29 | * | ||
30 | * // call getName() to determine the choice labels | ||
31 | * $choiceList = new ObjectChoiceList($choices, 'name'); | ||
32 | * </code> | ||
33 | * | ||
34 | * @author Bernhard Schussek <bschussek@gmail.com> | ||
35 | */ | ||
36 | class ObjectChoiceList extends ChoiceList | ||
37 | { | ||
38 | /** | ||
39 | * @var PropertyAccessorInterface | ||
40 | */ | ||
41 | private $propertyAccessor; | ||
42 | |||
43 | /** | ||
44 | * The property path used to obtain the choice label. | ||
45 | * | ||
46 | * @var PropertyPath | ||
47 | */ | ||
48 | private $labelPath; | ||
49 | |||
50 | /** | ||
51 | * The property path used for object grouping. | ||
52 | * | ||
53 | * @var PropertyPath | ||
54 | */ | ||
55 | private $groupPath; | ||
56 | |||
57 | /** | ||
58 | * The property path used to obtain the choice value. | ||
59 | * | ||
60 | * @var PropertyPath | ||
61 | */ | ||
62 | private $valuePath; | ||
63 | |||
64 | /** | ||
65 | * Creates a new object choice list. | ||
66 | * | ||
67 | * @param array|\Traversable $choices The array of choices. Choices may also be given | ||
68 | * as hierarchy of unlimited depth by creating nested | ||
69 | * arrays. The title of the sub-hierarchy can be | ||
70 | * stored in the array key pointing to the nested | ||
71 | * array. The topmost level of the hierarchy may also | ||
72 | * be a \Traversable. | ||
73 | * @param string $labelPath A property path pointing to the property used | ||
74 | * for the choice labels. The value is obtained | ||
75 | * by calling the getter on the object. If the | ||
76 | * path is NULL, the object's __toString() method | ||
77 | * is used instead. | ||
78 | * @param array $preferredChoices A flat array of choices that should be | ||
79 | * presented to the user with priority. | ||
80 | * @param string $groupPath A property path pointing to the property used | ||
81 | * to group the choices. Only allowed if | ||
82 | * the choices are given as flat array. | ||
83 | * @param string $valuePath A property path pointing to the property used | ||
84 | * for the choice values. If not given, integers | ||
85 | * are generated instead. | ||
86 | * @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths. | ||
87 | */ | ||
88 | public function __construct($choices, $labelPath = null, array $preferredChoices = array(), $groupPath = null, $valuePath = null, PropertyAccessorInterface $propertyAccessor = null) | ||
89 | { | ||
90 | $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor(); | ||
91 | $this->labelPath = null !== $labelPath ? new PropertyPath($labelPath) : null; | ||
92 | $this->groupPath = null !== $groupPath ? new PropertyPath($groupPath) : null; | ||
93 | $this->valuePath = null !== $valuePath ? new PropertyPath($valuePath) : null; | ||
94 | |||
95 | parent::__construct($choices, array(), $preferredChoices); | ||
96 | } | ||
97 | |||
98 | /** | ||
99 | * Initializes the list with choices. | ||
100 | * | ||
101 | * Safe to be called multiple times. The list is cleared on every call. | ||
102 | * | ||
103 | * @param array|\Traversable $choices The choices to write into the list. | ||
104 | * @param array $labels Ignored. | ||
105 | * @param array $preferredChoices The choices to display with priority. | ||
106 | * | ||
107 | * @throws InvalidArgumentException When passing a hierarchy of choices and using | ||
108 | * the "groupPath" option at the same time. | ||
109 | */ | ||
110 | protected function initialize($choices, array $labels, array $preferredChoices) | ||
111 | { | ||
112 | if (null !== $this->groupPath) { | ||
113 | $groupedChoices = array(); | ||
114 | |||
115 | foreach ($choices as $i => $choice) { | ||
116 | if (is_array($choice)) { | ||
117 | throw new InvalidArgumentException('You should pass a plain object array (without groups) when using the "groupPath" option.'); | ||
118 | } | ||
119 | |||
120 | try { | ||
121 | $group = $this->propertyAccessor->getValue($choice, $this->groupPath); | ||
122 | } catch (NoSuchPropertyException $e) { | ||
123 | // Don't group items whose group property does not exist | ||
124 | // see https://github.com/symfony/symfony/commit/d9b7abb7c7a0f28e0ce970afc5e305dce5dccddf | ||
125 | $group = null; | ||
126 | } | ||
127 | |||
128 | if (null === $group) { | ||
129 | $groupedChoices[$i] = $choice; | ||
130 | } else { | ||
131 | if (!isset($groupedChoices[$group])) { | ||
132 | $groupedChoices[$group] = array(); | ||
133 | } | ||
134 | |||
135 | $groupedChoices[$group][$i] = $choice; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | $choices = $groupedChoices; | ||
140 | } | ||
141 | |||
142 | $labels = array(); | ||
143 | |||
144 | $this->extractLabels($choices, $labels); | ||
145 | |||
146 | parent::initialize($choices, $labels, $preferredChoices); | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * Creates a new unique value for this choice. | ||
151 | * | ||
152 | * If a property path for the value was given at object creation, | ||
153 | * the getter behind that path is now called to obtain a new value. | ||
154 | * Otherwise a new integer is generated. | ||
155 | * | ||
156 | * @param mixed $choice The choice to create a value for | ||
157 | * | ||
158 | * @return integer|string A unique value without character limitations. | ||
159 | */ | ||
160 | protected function createValue($choice) | ||
161 | { | ||
162 | if ($this->valuePath) { | ||
163 | return (string) $this->propertyAccessor->getValue($choice, $this->valuePath); | ||
164 | } | ||
165 | |||
166 | return parent::createValue($choice); | ||
167 | } | ||
168 | |||
169 | private function extractLabels($choices, array &$labels) | ||
170 | { | ||
171 | foreach ($choices as $i => $choice) { | ||
172 | if (is_array($choice)) { | ||
173 | $labels[$i] = array(); | ||
174 | $this->extractLabels($choice, $labels[$i]); | ||
175 | } elseif ($this->labelPath) { | ||
176 | $labels[$i] = $this->propertyAccessor->getValue($choice, $this->labelPath); | ||
177 | } elseif (method_exists($choice, '__toString')) { | ||
178 | $labels[$i] = (string) $choice; | ||
179 | } else { | ||
180 | throw new StringCastException(sprintf('A "__toString()" method was not found on the objects of type "%s" passed to the choice field. To read a custom getter instead, set the argument $labelPath to the desired property path.', get_class($choice))); | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | } | ||
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.php new file mode 100644 index 00000000..914dbe5f --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.php | |||
@@ -0,0 +1,164 @@ | |||
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 | |||
12 | namespace Symfony\Component\Form\Extension\Core\ChoiceList; | ||
13 | |||
14 | /** | ||
15 | * A choice list for choices of type string or integer. | ||
16 | * | ||
17 | * Choices and their associated labels can be passed in a single array. Since | ||
18 | * choices are passed as array keys, only strings or integer choices are | ||
19 | * allowed. Choices may also be given as hierarchy of unlimited depth by | ||
20 | * creating nested arrays. The title of the sub-hierarchy can be stored in the | ||
21 | * array key pointing to the nested array. | ||
22 | * | ||
23 | * <code> | ||
24 | * $choiceList = new SimpleChoiceList(array( | ||
25 | * 'creditcard' => 'Credit card payment', | ||
26 | * 'cash' => 'Cash payment', | ||
27 | * )); | ||
28 | * </code> | ||
29 | * | ||
30 | * @author Bernhard Schussek <bschussek@gmail.com> | ||
31 | */ | ||
32 | class SimpleChoiceList extends ChoiceList | ||
33 | { | ||
34 | /** | ||
35 | * Creates a new simple choice list. | ||
36 | * | ||
37 | * @param array $choices The array of choices with the choices as keys and | ||
38 | * the labels as values. Choices may also be given | ||
39 | * as hierarchy of unlimited depth by creating nested | ||
40 | * arrays. The title of the sub-hierarchy is stored | ||
41 | * in the array key pointing to the nested array. | ||
42 | * @param array $preferredChoices A flat array of choices that should be | ||
43 | * presented to the user with priority. | ||
44 | */ | ||
45 | public function __construct(array $choices, array $preferredChoices = array()) | ||
46 | { | ||
47 | // Flip preferred choices to speed up lookup | ||
48 | parent::__construct($choices, $choices, array_flip($preferredChoices)); | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * {@inheritdoc} | ||
53 | */ | ||
54 | public function getChoicesForValues(array $values) | ||
55 | { | ||
56 | $values = $this->fixValues($values); | ||
57 | |||
58 | // The values are identical to the choices, so we can just return them | ||
59 | // to improve performance a little bit | ||
60 | return $this->fixChoices(array_intersect($values, $this->getValues())); | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * {@inheritdoc} | ||
65 | */ | ||
66 | public function getValuesForChoices(array $choices) | ||
67 | { | ||
68 | $choices = $this->fixChoices($choices); | ||
69 | |||
70 | // The choices are identical to the values, so we can just return them | ||
71 | // to improve performance a little bit | ||
72 | return $this->fixValues(array_intersect($choices, $this->getValues())); | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * Recursively adds the given choices to the list. | ||
77 | * | ||
78 | * Takes care of splitting the single $choices array passed in the | ||
79 | * constructor into choices and labels. | ||
80 | * | ||
81 | * @param array $bucketForPreferred The bucket where to store the preferred | ||
82 | * view objects. | ||
83 | * @param array $bucketForRemaining The bucket where to store the | ||
84 | * non-preferred view objects. | ||
85 | * @param array|\Traversable $choices The list of choices. | ||
86 | * @param array $labels Ignored. | ||
87 | * @param array $preferredChoices The preferred choices. | ||
88 | */ | ||
89 | protected function addChoices(array &$bucketForPreferred, array &$bucketForRemaining, $choices, array $labels, array $preferredChoices) | ||
90 | { | ||
91 | // Add choices to the nested buckets | ||
92 | foreach ($choices as $choice => $label) { | ||
93 | if (is_array($label)) { | ||
94 | // Don't do the work if the array is empty | ||
95 | if (count($label) > 0) { | ||
96 | $this->addChoiceGroup( | ||
97 | $choice, | ||
98 | $bucketForPreferred, | ||
99 | $bucketForRemaining, | ||
100 | $label, | ||
101 | $label, | ||
102 | $preferredChoices | ||
103 | ); | ||
104 | } | ||
105 | } else { | ||
106 | $this->addChoice( | ||
107 | $bucketForPreferred, | ||
108 | $bucketForRemaining, | ||
109 | $choice, | ||
110 | $label, | ||
111 | $preferredChoices | ||
112 | ); | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * Returns whether the given choice should be preferred judging by the | ||
119 | * given array of preferred choices. | ||
120 | * | ||
121 | * Optimized for performance by treating the preferred choices as array | ||
122 | * where choices are stored in the keys. | ||
123 | * | ||
124 | * @param mixed $choice The choice to test. | ||
125 | * @param array $preferredChoices An array of preferred choices. | ||
126 | * | ||
127 | * @return Boolean Whether the choice is preferred. | ||
128 | */ | ||
129 | protected function isPreferred($choice, array $preferredChoices) | ||
130 | { | ||
131 | // Optimize performance over the default implementation | ||
132 | return isset($preferredChoices[$choice]); | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * Converts the choice to a valid PHP array key. | ||
137 | * | ||
138 | * @param mixed $choice The choice. | ||
139 | * | ||
140 | * @return string|integer A valid PHP array key. | ||
141 | */ | ||
142 | protected function fixChoice($choice) | ||
143 | { | ||
144 | return $this->fixIndex($choice); | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * {@inheritdoc} | ||
149 | */ | ||
150 | protected function fixChoices(array $choices) | ||
151 | { | ||
152 | return $this->fixIndices($choices); | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * {@inheritdoc} | ||
157 | */ | ||
158 | protected function createValue($choice) | ||
159 | { | ||
160 | // Choices are guaranteed to be unique and scalar, so we can simply | ||
161 | // convert them to strings | ||
162 | return (string) $choice; | ||
163 | } | ||
164 | } | ||