Security update for Core, with self-updated composer
[yaffs-website] / web / core / lib / Drupal / Core / Render / Element / Checkboxes.php
1 <?php
2
3 namespace Drupal\Core\Render\Element;
4
5 use Drupal\Core\Form\FormStateInterface;
6
7 /**
8  * Provides a form element for a set of checkboxes.
9  *
10  * Properties:
11  * - #options: An associative array whose keys are the values returned for each
12  *   checkbox, and whose values are the labels next to each checkbox. The
13  *   #options array cannot have a 0 key, as it would not be possible to discern
14  *   checked and unchecked states.
15  *
16  * Usage example:
17  * @code
18  * $form['high_school']['tests_taken'] = array(
19  *   '#type' => 'checkboxes',
20  *   '#options' => array('SAT' => $this->t('SAT'), 'ACT' => $this->t('ACT')),
21  *   '#title' => $this->t('What standardized tests did you take?'),
22  *   ...
23  * );
24  * @endcode
25  *
26  * @see \Drupal\Core\Render\Element\Radios
27  * @see \Drupal\Core\Render\Element\Checkbox
28  *
29  * @FormElement("checkboxes")
30  */
31 class Checkboxes extends FormElement {
32
33   use CompositeFormElementTrait;
34
35   /**
36    * {@inheritdoc}
37    */
38   public function getInfo() {
39     $class = get_class($this);
40     return [
41       '#input' => TRUE,
42       '#process' => [
43         [$class, 'processCheckboxes'],
44       ],
45       '#pre_render' => [
46         [$class, 'preRenderCompositeFormElement'],
47       ],
48       '#theme_wrappers' => ['checkboxes'],
49     ];
50   }
51
52   /**
53    * Processes a checkboxes form element.
54    */
55   public static function processCheckboxes(&$element, FormStateInterface $form_state, &$complete_form) {
56     $value = is_array($element['#value']) ? $element['#value'] : [];
57     $element['#tree'] = TRUE;
58     if (count($element['#options']) > 0) {
59       if (!isset($element['#default_value']) || $element['#default_value'] == 0) {
60         $element['#default_value'] = [];
61       }
62       $weight = 0;
63       foreach ($element['#options'] as $key => $choice) {
64         // Integer 0 is not a valid #return_value, so use '0' instead.
65         // @see \Drupal\Core\Render\Element\Checkbox::valueCallback().
66         // @todo For Drupal 8, cast all integer keys to strings for consistency
67         //   with \Drupal\Core\Render\Element\Radios::processRadios().
68         if ($key === 0) {
69           $key = '0';
70         }
71         // Maintain order of options as defined in #options, in case the element
72         // defines custom option sub-elements, but does not define all option
73         // sub-elements.
74         $weight += 0.001;
75
76         $element += [$key => []];
77         $element[$key] += [
78           '#type' => 'checkbox',
79           '#title' => $choice,
80           '#return_value' => $key,
81           '#default_value' => isset($value[$key]) ? $key : NULL,
82           '#attributes' => $element['#attributes'],
83           '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
84           // Errors should only be shown on the parent checkboxes element.
85           '#error_no_message' => TRUE,
86           '#weight' => $weight,
87         ];
88       }
89     }
90     return $element;
91   }
92
93   /**
94    * {@inheritdoc}
95    */
96   public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
97     if ($input === FALSE) {
98       $value = [];
99       $element += ['#default_value' => []];
100       foreach ($element['#default_value'] as $key) {
101         $value[$key] = $key;
102       }
103       return $value;
104     }
105     elseif (is_array($input)) {
106       // Programmatic form submissions use NULL to indicate that a checkbox
107       // should be unchecked. We therefore remove all NULL elements from the
108       // array before constructing the return value, to simulate the behavior
109       // of web browsers (which do not send unchecked checkboxes to the server
110       // at all). This will not affect non-programmatic form submissions, since
111       // all values in \Drupal::request()->request are strings.
112       // @see \Drupal\Core\Form\FormBuilderInterface::submitForm()
113       foreach ($input as $key => $value) {
114         if (!isset($value)) {
115           unset($input[$key]);
116         }
117       }
118       return array_combine($input, $input);
119     }
120     else {
121       return [];
122     }
123   }
124
125   /**
126    * Determines which checkboxes were checked when a form is submitted.
127    *
128    * @param array $input
129    *   An array returned by the FormAPI for a set of checkboxes.
130    *
131    * @return array
132    *   An array of keys that were checked.
133    */
134   public static function getCheckedCheckboxes(array $input) {
135     // Browsers do not include unchecked options in a form submission. The
136     // FormAPI tries to normalize this to keep checkboxes consistent with other
137     // form elements. Checkboxes show up as an array in the form of option_id =>
138     // option_id|0, where integer 0 is an unchecked option.
139     //
140     // @see \Drupal\Core\Render\Element\Checkboxes::valueCallback()
141     // @see https://www.w3.org/TR/html401/interact/forms.html#checkbox
142     $checked = array_filter($input, function ($value) {
143       return $value !== 0;
144     });
145     return array_keys($checked);
146   }
147
148   /**
149    * Determines if all checkboxes in a set are unchecked.
150    *
151    * @param array $input
152    *   An array returned by the FormAPI for a set of checkboxes.
153    *
154    * @return bool
155    *   TRUE if all options are unchecked. FALSE otherwise.
156    */
157   public static function detectEmptyCheckboxes(array $input) {
158     return empty(static::getCheckedCheckboxes($input));
159   }
160
161 }