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