Pull merge.
[yaffs-website] / web / core / lib / Drupal / Core / Render / Element.php
1 <?php
2
3 namespace Drupal\Core\Render;
4
5 use Drupal\Component\Render\FormattableMarkup;
6 use Drupal\Core\Access\AccessResultInterface;
7
8 /**
9  * Provides helper methods for Drupal render elements.
10  *
11  * @see \Drupal\Core\Render\Element\ElementInterface
12  *
13  * @ingroup theme_render
14  */
15 class Element {
16
17   /**
18    * Checks if the key is a property.
19    *
20    * @param string $key
21    *   The key to check.
22    *
23    * @return bool
24    *   TRUE of the key is a property, FALSE otherwise.
25    */
26   public static function property($key) {
27     return $key[0] == '#';
28   }
29
30   /**
31    * Gets properties of a structured array element (keys beginning with '#').
32    *
33    * @param array $element
34    *   An element array to return properties for.
35    *
36    * @return array
37    *   An array of property keys for the element.
38    */
39   public static function properties(array $element) {
40     return array_filter(array_keys($element), 'static::property');
41   }
42
43   /**
44    * Checks if the key is a child.
45    *
46    * @param string $key
47    *   The key to check.
48    *
49    * @return bool
50    *   TRUE if the element is a child, FALSE otherwise.
51    */
52   public static function child($key) {
53     return !isset($key[0]) || $key[0] != '#';
54   }
55
56   /**
57    * Identifies the children of an element array, optionally sorted by weight.
58    *
59    * The children of a element array are those key/value pairs whose key does
60    * not start with a '#'. See drupal_render() for details.
61    *
62    * @param array $elements
63    *   The element array whose children are to be identified. Passed by
64    *   reference.
65    * @param bool $sort
66    *   Boolean to indicate whether the children should be sorted by weight.
67    *
68    * @return array
69    *   The array keys of the element's children.
70    */
71   public static function children(array &$elements, $sort = FALSE) {
72     // Do not attempt to sort elements which have already been sorted.
73     $sort = isset($elements['#sorted']) ? !$elements['#sorted'] : $sort;
74
75     // Filter out properties from the element, leaving only children.
76     $count = count($elements);
77     $child_weights = [];
78     $i = 0;
79     $sortable = FALSE;
80     foreach ($elements as $key => $value) {
81       if ($key === '' || $key[0] !== '#') {
82         if (is_array($value)) {
83           if (isset($value['#weight'])) {
84             $weight = $value['#weight'];
85             $sortable = TRUE;
86           }
87           else {
88             $weight = 0;
89           }
90           // Supports weight with up to three digit precision and conserve
91           // the insertion order.
92           $child_weights[$key] = floor($weight * 1000) + $i / $count;
93         }
94         // Only trigger an error if the value is not null.
95         // @see https://www.drupal.org/node/1283892
96         elseif (isset($value)) {
97           trigger_error(new FormattableMarkup('"@key" is an invalid render array key', ['@key' => $key]), E_USER_ERROR);
98         }
99       }
100       $i++;
101     }
102
103     // Sort the children if necessary.
104     if ($sort && $sortable) {
105       asort($child_weights);
106       // Put the sorted children back into $elements in the correct order, to
107       // preserve sorting if the same element is passed through
108       // \Drupal\Core\Render\Element::children() twice.
109       foreach ($child_weights as $key => $weight) {
110         $value = $elements[$key];
111         unset($elements[$key]);
112         $elements[$key] = $value;
113       }
114       $elements['#sorted'] = TRUE;
115     }
116
117     return array_keys($child_weights);
118   }
119
120   /**
121    * Returns the visible children of an element.
122    *
123    * @param array $elements
124    *   The parent element.
125    *
126    * @return array
127    *   The array keys of the element's visible children.
128    */
129   public static function getVisibleChildren(array $elements) {
130     $visible_children = [];
131
132     foreach (static::children($elements) as $key) {
133       $child = $elements[$key];
134
135       // Skip value and hidden elements, since they are not rendered.
136       if (!static::isVisibleElement($child)) {
137         continue;
138       }
139
140       $visible_children[$key] = $child;
141     }
142
143     return array_keys($visible_children);
144   }
145
146   /**
147    * Determines if an element is visible.
148    *
149    * @param array $element
150    *   The element to check for visibility.
151    *
152    * @return bool
153    *   TRUE if the element is visible, otherwise FALSE.
154    */
155   public static function isVisibleElement($element) {
156     return (!isset($element['#type']) || !in_array($element['#type'], ['value', 'hidden', 'token']))
157       && (!isset($element['#access'])
158       || (($element['#access'] instanceof AccessResultInterface && $element['#access']->isAllowed()) || ($element['#access'] === TRUE)));
159   }
160
161   /**
162    * Sets HTML attributes based on element properties.
163    *
164    * @param array $element
165    *   The renderable element to process. Passed by reference.
166    * @param array $map
167    *   An associative array whose keys are element property names and whose
168    *   values are the HTML attribute names to set on the corresponding
169    *   property; e.g., array('#propertyname' => 'attributename'). If both names
170    *   are identical except for the leading '#', then an attribute name value is
171    *   sufficient and no property name needs to be specified.
172    */
173   public static function setAttributes(array &$element, array $map) {
174     foreach ($map as $property => $attribute) {
175       // If the key is numeric, the attribute name needs to be taken over.
176       if (is_int($property)) {
177         $property = '#' . $attribute;
178       }
179       // Do not overwrite already existing attributes.
180       if (isset($element[$property]) && !isset($element['#attributes'][$attribute])) {
181         $element['#attributes'][$attribute] = $element[$property];
182       }
183     }
184   }
185
186   /**
187    * Indicates whether the given element is empty.
188    *
189    * An element that only has #cache set is considered empty, because it will
190    * render to the empty string.
191    *
192    * @param array $elements
193    *   The element.
194    *
195    * @return bool
196    *   Whether the given element is empty.
197    */
198   public static function isEmpty(array $elements) {
199     return empty($elements) || (count($elements) === 1 && array_keys($elements) === ['#cache']);
200   }
201
202 }