Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / vendor / symfony / dom-crawler / Field / ChoiceFormField.php
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\DomCrawler\Field;
13
14 /**
15  * ChoiceFormField represents a choice form field.
16  *
17  * It is constructed from a HTML select tag, or a HTML checkbox, or radio inputs.
18  *
19  * @author Fabien Potencier <fabien@symfony.com>
20  */
21 class ChoiceFormField extends FormField
22 {
23     /**
24      * @var string
25      */
26     private $type;
27     /**
28      * @var bool
29      */
30     private $multiple;
31     /**
32      * @var array
33      */
34     private $options;
35     /**
36      * @var bool
37      */
38     private $validationDisabled = false;
39
40     /**
41      * Returns true if the field should be included in the submitted values.
42      *
43      * @return bool true if the field should be included in the submitted values, false otherwise
44      */
45     public function hasValue()
46     {
47         // don't send a value for unchecked checkboxes
48         if (\in_array($this->type, array('checkbox', 'radio')) && null === $this->value) {
49             return false;
50         }
51
52         return true;
53     }
54
55     /**
56      * Check if the current selected option is disabled.
57      *
58      * @return bool
59      */
60     public function isDisabled()
61     {
62         if (parent::isDisabled() && 'select' === $this->type) {
63             return true;
64         }
65
66         foreach ($this->options as $option) {
67             if ($option['value'] == $this->value && $option['disabled']) {
68                 return true;
69             }
70         }
71
72         return false;
73     }
74
75     /**
76      * Sets the value of the field.
77      *
78      * @param string|array $value The value of the field
79      */
80     public function select($value)
81     {
82         $this->setValue($value);
83     }
84
85     /**
86      * Ticks a checkbox.
87      *
88      * @throws \LogicException When the type provided is not correct
89      */
90     public function tick()
91     {
92         if ('checkbox' !== $this->type) {
93             throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
94         }
95
96         $this->setValue(true);
97     }
98
99     /**
100      * Unticks a checkbox.
101      *
102      * @throws \LogicException When the type provided is not correct
103      */
104     public function untick()
105     {
106         if ('checkbox' !== $this->type) {
107             throw new \LogicException(sprintf('You cannot untick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
108         }
109
110         $this->setValue(false);
111     }
112
113     /**
114      * Sets the value of the field.
115      *
116      * @param string|array $value The value of the field
117      *
118      * @throws \InvalidArgumentException When value type provided is not correct
119      */
120     public function setValue($value)
121     {
122         if ('checkbox' === $this->type && false === $value) {
123             // uncheck
124             $this->value = null;
125         } elseif ('checkbox' === $this->type && true === $value) {
126             // check
127             $this->value = $this->options[0]['value'];
128         } else {
129             if (\is_array($value)) {
130                 if (!$this->multiple) {
131                     throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name));
132                 }
133
134                 foreach ($value as $v) {
135                     if (!$this->containsOption($v, $this->options)) {
136                         throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $v, implode(', ', $this->availableOptionValues())));
137                     }
138                 }
139             } elseif (!$this->containsOption($value, $this->options)) {
140                 throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $value, implode(', ', $this->availableOptionValues())));
141             }
142
143             if ($this->multiple) {
144                 $value = (array) $value;
145             }
146
147             if (\is_array($value)) {
148                 $this->value = $value;
149             } else {
150                 parent::setValue($value);
151             }
152         }
153     }
154
155     /**
156      * Adds a choice to the current ones.
157      *
158      * @param \DOMElement $node
159      *
160      * @throws \LogicException When choice provided is not multiple nor radio
161      *
162      * @internal
163      */
164     public function addChoice(\DOMElement $node)
165     {
166         if (!$this->multiple && 'radio' !== $this->type) {
167             throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name));
168         }
169
170         $option = $this->buildOptionValue($node);
171         $this->options[] = $option;
172
173         if ($node->hasAttribute('checked')) {
174             $this->value = $option['value'];
175         }
176     }
177
178     /**
179      * Returns the type of the choice field (radio, select, or checkbox).
180      *
181      * @return string The type
182      */
183     public function getType()
184     {
185         return $this->type;
186     }
187
188     /**
189      * Returns true if the field accepts multiple values.
190      *
191      * @return bool true if the field accepts multiple values, false otherwise
192      */
193     public function isMultiple()
194     {
195         return $this->multiple;
196     }
197
198     /**
199      * Initializes the form field.
200      *
201      * @throws \LogicException When node type is incorrect
202      */
203     protected function initialize()
204     {
205         if ('input' !== $this->node->nodeName && 'select' !== $this->node->nodeName) {
206             throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName));
207         }
208
209         if ('input' === $this->node->nodeName && 'checkbox' !== strtolower($this->node->getAttribute('type')) && 'radio' !== strtolower($this->node->getAttribute('type'))) {
210             throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is %s).', $this->node->getAttribute('type')));
211         }
212
213         $this->value = null;
214         $this->options = array();
215         $this->multiple = false;
216
217         if ('input' == $this->node->nodeName) {
218             $this->type = strtolower($this->node->getAttribute('type'));
219             $optionValue = $this->buildOptionValue($this->node);
220             $this->options[] = $optionValue;
221
222             if ($this->node->hasAttribute('checked')) {
223                 $this->value = $optionValue['value'];
224             }
225         } else {
226             $this->type = 'select';
227             if ($this->node->hasAttribute('multiple')) {
228                 $this->multiple = true;
229                 $this->value = array();
230                 $this->name = str_replace('[]', '', $this->name);
231             }
232
233             $found = false;
234             foreach ($this->xpath->query('descendant::option', $this->node) as $option) {
235                 $optionValue = $this->buildOptionValue($option);
236                 $this->options[] = $optionValue;
237
238                 if ($option->hasAttribute('selected')) {
239                     $found = true;
240                     if ($this->multiple) {
241                         $this->value[] = $optionValue['value'];
242                     } else {
243                         $this->value = $optionValue['value'];
244                     }
245                 }
246             }
247
248             // if no option is selected and if it is a simple select box, take the first option as the value
249             if (!$found && !$this->multiple && !empty($this->options)) {
250                 $this->value = $this->options[0]['value'];
251             }
252         }
253     }
254
255     /**
256      * Returns option value with associated disabled flag.
257      *
258      * @param \DOMElement $node
259      *
260      * @return array
261      */
262     private function buildOptionValue(\DOMElement $node)
263     {
264         $option = array();
265
266         $defaultDefaultValue = 'select' === $this->node->nodeName ? '' : 'on';
267         $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : $defaultDefaultValue;
268         $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue;
269         $option['disabled'] = $node->hasAttribute('disabled');
270
271         return $option;
272     }
273
274     /**
275      * Checks whether given value is in the existing options.
276      *
277      * @param string $optionValue
278      * @param array  $options
279      *
280      * @return bool
281      */
282     public function containsOption($optionValue, $options)
283     {
284         if ($this->validationDisabled) {
285             return true;
286         }
287
288         foreach ($options as $option) {
289             if ($option['value'] == $optionValue) {
290                 return true;
291             }
292         }
293
294         return false;
295     }
296
297     /**
298      * Returns list of available field options.
299      *
300      * @return array
301      */
302     public function availableOptionValues()
303     {
304         $values = array();
305
306         foreach ($this->options as $option) {
307             $values[] = $option['value'];
308         }
309
310         return $values;
311     }
312
313     /**
314      * Disables the internal validation of the field.
315      *
316      * @return self
317      */
318     public function disableValidation()
319     {
320         $this->validationDisabled = true;
321
322         return $this;
323     }
324 }