5 * Contains Drupal\Console\Core\NestedArray.
8 namespace Drupal\Console\Core\Utils;
12 * @package Drupal\Console\Core\Utils
17 * Based on drupal class Drupal\Component\Utility\NestedArray
19 * Retrieves a value from a nested array with variable depth.
21 * This helper function should be used when the depth of the array element
22 * being retrieved may vary (that is, the number of parent keys is variable).
23 * It is primarily used for form structures and renderable arrays.
25 * Without this helper function the only way to get a nested array value with
26 * variable depth in one line would be using eval(), which should be avoided:
29 * // Do not do this! Avoid eval().
30 * // May also throw a PHP notice, if the variable array keys do not exist.
31 * eval('$value = $array[\'' . implode("']['", $parents) . "'];");
34 * Instead, use this helper function:
36 * $value = NestedArray::getValue($form, $parents);
39 * A return value of NULL is ambiguous, and can mean either that the requested
40 * key does not exist, or that the actual value is NULL. If it is required to
41 * know whether the nested array key actually exists, pass a third argument
42 * that is altered by reference:
45 * $value = NestedArray::getValue($form, $parents, $key_exists);
47 * // Do something with $value.
51 * However if the number of array parent keys is static, the value should
52 * always be retrieved directly rather than calling this function.
55 * $value = $form['signature_settings']['signature'];
59 * The array from which to get the value.
60 * @param array $parents
61 * An array of parent keys of the value, starting with the outermost key.
62 * @param bool $key_exists
63 * (optional) If given, an already defined variable that is altered by
67 * The requested nested value. Possibly NULL if the value is NULL or not all
68 * nested parent keys exist. $key_exists is altered by reference and is a
69 * Boolean that indicates whether all nested parent keys exist (TRUE) or not
70 * (FALSE). This allows to distinguish between the two possibilities when
73 * @see NestedArray::setValue()
74 * @see NestedArray::unsetValue()
76 public static function &getValue(array &$array, array $parents, &$key_exists = null)
79 foreach ($parents as $parent) {
80 if (is_array($ref) && array_key_exists($parent, $ref)) {
81 $ref = &$ref[$parent];
93 * Sets a value in a nested array with variable depth.
95 * This helper function should be used when the depth of the array element you
96 * are changing may vary (that is, the number of parent keys is variable). It
97 * is primarily used for form structures and renderable arrays.
102 * // Assume you have a 'signature' element somewhere in a form. It might be:
103 * $form['signature_settings']['signature'] = array(
104 * '#type' => 'text_format',
105 * '#title' => t('Signature'),
107 * // Or, it might be further nested:
108 * $form['signature_settings']['user']['signature'] = array(
109 * '#type' => 'text_format',
110 * '#title' => t('Signature'),
114 * To deal with the situation, the code needs to figure out the route to the
115 * element, given an array of parents that is either
116 * @code array('signature_settings', 'signature') @endcode
117 * in the first case or
118 * @code array('signature_settings', 'user', 'signature') @endcode
119 * in the second case.
121 * Without this helper function the only way to set the signature element in
122 * one line would be using eval(), which should be avoided:
124 * // Do not do this! Avoid eval().
125 * eval('$form[\'' . implode("']['", $parents) . '\'] = $element;');
128 * Instead, use this helper function:
130 * NestedArray::setValue($form, $parents, $element);
133 * However if the number of array parent keys is static, the value should
134 * always be set directly rather than calling this function. For instance,
135 * for the first example we could just do:
137 * $form['signature_settings']['signature'] = $element;
140 * @param array $array
141 * A reference to the array to modify.
142 * @param array $parents
143 * An array of parent keys, starting with the outermost key.
144 * @param mixed $value
147 * (optional) If TRUE, the value is forced into the structure even if it
148 * requires the deletion of an already existing non-array parent value. If
149 * FALSE, PHP throws an error if trying to add into a value that is not an
150 * array. Defaults to FALSE.
152 * @see NestedArray::unsetValue()
153 * @see NestedArray::getValue()
155 public static function setValue(array &$array, array $parents, $value, $force = false)
158 foreach ($parents as $parent) {
159 // PHP auto-creates container arrays and NULL entries without error if $ref
160 // is NULL, but throws an error if $ref is set, but not an array.
161 if ($force && isset($ref) && !is_array($ref)) {
164 $ref = &$ref[$parent];
170 * Replace a YAML key maintaining values
171 * @param array $array
172 * @param array $parents
175 public static function replaceKey(array &$array, array $parents, $new_key)
178 foreach ($parents as $parent) {
181 $ref = &$ref[$parent];
184 $father[$new_key] = $father[$key];
185 unset($father[$key]);
191 * @param bool $negate if Negate is true only if values are equal are returned.
192 * @param$$statistics mixed array
195 public function arrayDiff($array1, $array2, $negate = false, &$statistics)
198 foreach ($array1 as $key => $val) {
199 if (isset($array2[$key])) {
200 if (is_array($val) && $array2[$key]) {
201 $result[$key] = $this->arrayDiff($val, $array2[$key], $negate, $statistics);
202 if (empty($result[$key])) {
203 unset($result[$key]);
206 $statistics['total'] += 1;
207 if ($val == $array2[$key] && $negate) {
208 $result[$key] = $array2[$key];
209 $statistics['equal'] += 1;
210 } elseif ($val != $array2[$key] && $negate) {
211 $statistics['diff'] += 1;
212 } elseif ($val != $array2[$key] && !$negate) {
213 $result[$key] = $array2[$key];
214 $statistics['diff'] += 1;
215 } elseif ($val == $array2[$key] && !$negate) {
216 $result[$key] = $array2[$key];
217 $statistics['equal'] += 1;
221 if (is_array($val)) {
222 $statistics['diff'] += count($val, COUNT_RECURSIVE);
223 $statistics['total'] += count($val, COUNT_RECURSIVE);
225 $statistics['diff'] +=1;
226 $statistics['total'] += 1;
236 * @param array $array
237 * @param array $flatten_array
238 * @param string $key_flatten
240 public function yamlFlattenArray(array &$array, &$flatten_array, &$key_flatten = '')
242 foreach ($array as $key => $value) {
243 if (!empty($key_flatten)) {
248 if (is_array($value)) {
249 $this->yamlFlattenArray($value, $flatten_array, $key_flatten);
251 if (!empty($value)) {
252 $flatten_array[$key_flatten] = $value;
253 $key_flatten = substr($key_flatten, 0, strrpos($key_flatten, "."));
255 // Return to previous key
256 $key_flatten = substr($key_flatten, 0, strrpos($key_flatten, "."));
261 // Start again with flatten key after recursive call
262 $key_flatten = substr($key_flatten, 0, strrpos($key_flatten, "."));
266 * @param array $array
267 * @param array $split_array
268 * @param int $indent_level
269 * @param array $key_flatten
270 * @param int $key_level
271 * @param bool $exclude_parents_key
273 public function yamlSplitArray(array &$array, array &$split_array, $indent_level = '', &$key_flatten, &$key_level, $exclude_parents_key)
275 foreach ($array as $key => $value) {
276 if (!$exclude_parents_key && !empty($key_flatten)) {
280 if ($exclude_parents_key) {
283 $key_flatten .= $key;
286 if ($key_level == $indent_level) {
287 if (!empty($value)) {
288 $split_array[$key_flatten] = $value;
290 if (!$exclude_parents_key) {
291 $key_flatten = substr($key_flatten, 0, strrpos($key_flatten, "."));
295 if (is_array($value)) {
297 $this->yamlSplitArray($value, $split_array, $indent_level, $key_flatten, $key_level, $exclude_parents_key);
302 // Start again with flatten key after recursive call
303 if (!$exclude_parents_key) {
304 $key_flatten = substr($key_flatten, 0, strrpos($key_flatten, "."));
310 * Unsets a value in a nested array with variable depth.
312 * This helper function should be used when the depth of the array element you
313 * are changing may vary (that is, the number of parent keys is variable). It
314 * is primarily used for form structures and renderable arrays.
319 * // Assume you have a 'signature' element somewhere in a form. It might be:
320 * $form['signature_settings']['signature'] = array(
321 * '#type' => 'text_format',
322 * '#title' => t('Signature'),
324 * // Or, it might be further nested:
325 * $form['signature_settings']['user']['signature'] = array(
326 * '#type' => 'text_format',
327 * '#title' => t('Signature'),
331 * To deal with the situation, the code needs to figure out the route to the
332 * element, given an array of parents that is either
333 * @code array('signature_settings', 'signature') @endcode
334 * in the first case or
335 * @code array('signature_settings', 'user', 'signature') @endcode
336 * in the second case.
338 * Without this helper function the only way to unset the signature element in
339 * one line would be using eval(), which should be avoided:
341 * // Do not do this! Avoid eval().
342 * eval('unset($form[\'' . implode("']['", $parents) . '\']);');
345 * Instead, use this helper function:
347 * NestedArray::unset_nested_value($form, $parents, $element);
350 * However if the number of array parent keys is static, the value should
351 * always be set directly rather than calling this function. For instance, for
352 * the first example we could just do:
354 * unset($form['signature_settings']['signature']);
357 * @param array $array
358 * A reference to the array to modify.
359 * @param array $parents
360 * An array of parent keys, starting with the outermost key and including
361 * the key to be unset.
362 * @param bool $key_existed
363 * (optional) If given, an already defined variable that is altered by
366 * @see NestedArray::setValue()
367 * @see NestedArray::getValue()
369 public static function unsetValue(array &$array, array $parents, &$key_existed = null)
371 $unset_key = array_pop($parents);
372 $ref = &self::getValue($array, $parents, $key_existed);
373 if ($key_existed && is_array($ref) && array_key_exists($unset_key, $ref)) {
375 unset($ref[$unset_key]);
377 $key_existed = false;
382 * Determines whether a nested array contains the requested keys.
384 * This helper function should be used when the depth of the array element to
385 * be checked may vary (that is, the number of parent keys is variable). See
386 * NestedArray::setValue() for details. It is primarily used for form
387 * structures and renderable arrays.
389 * If it is required to also get the value of the checked nested key, use
390 * NestedArray::getValue() instead.
392 * If the number of array parent keys is static, this helper function is
393 * unnecessary and the following code can be used instead:
396 * $value_exists = isset($form['signature_settings']['signature']);
397 * $key_exists = array_key_exists('signature', $form['signature_settings']);
400 * @param array $array
401 * The array with the value to check for.
402 * @param array $parents
403 * An array of parent keys of the value, starting with the outermost key.
406 * TRUE if all the parent keys exist, FALSE otherwise.
408 * @see NestedArray::getValue()
410 public static function keyExists(array $array, array $parents)
412 // Although this function is similar to PHP's array_key_exists(), its
413 // arguments should be consistent with getValue().
415 self::getValue($array, $parents, $key_exists);
420 * Merges multiple arrays, recursively, and returns the merged array.
422 * This function is similar to PHP's array_merge_recursive() function, but it
423 * handles non-array values differently. When merging values that are not both
424 * arrays, the latter value replaces the former rather than merging with it.
429 * $link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => t('X'), 'class' => array('a', 'b')));
430 * $link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => t('Y'), 'class' => array('c', 'd')));
432 * // This results in array('fragment' => array('x', 'y'), 'attributes' => array('title' => array(t('X'), t('Y')), 'class' => array('a', 'b', 'c', 'd'))).
433 * $incorrect = array_merge_recursive($link_options_1, $link_options_2);
435 * // This results in array('fragment' => 'y', 'attributes' => array('title' => t('Y'), 'class' => array('a', 'b', 'c', 'd'))).
436 * $correct = NestedArray::mergeDeep($link_options_1, $link_options_2);
445 * @see NestedArray::mergeDeepArray()
447 public static function mergeDeep()
449 return self::mergeDeepArray(func_get_args());
453 * Merges multiple arrays, recursively, and returns the merged array.
455 * This function is equivalent to NestedArray::mergeDeep(), except the
456 * input arrays are passed as a single array parameter rather than a variable
459 * The following are equivalent:
460 * - NestedArray::mergeDeep($a, $b);
461 * - NestedArray::mergeDeepArray(array($a, $b));
463 * The following are also equivalent:
464 * - call_user_func_array('NestedArray::mergeDeep', $arrays_to_merge);
465 * - NestedArray::mergeDeepArray($arrays_to_merge);
467 * @param array $arrays
468 * An arrays of arrays to merge.
469 * @param bool $preserve_integer_keys
470 * (optional) If given, integer keys will be preserved and merged instead of
471 * appended. Defaults to FALSE.
476 * @see NestedArray::mergeDeep()
478 public static function mergeDeepArray(array $arrays, $preserve_integer_keys = false)
481 foreach ($arrays as $array) {
482 foreach ($array as $key => $value) {
483 // Renumber integer keys as array_merge_recursive() does unless
484 // $preserve_integer_keys is set to TRUE. Note that PHP automatically
485 // converts array keys that are integer strings (e.g., '1') to integers.
486 if (is_integer($key) && !$preserve_integer_keys) {
489 // Recurse when both values are arrays.
490 elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
491 $result[$key] = self::mergeDeepArray(array($result[$key], $value), $preserve_integer_keys);
493 // Otherwise, use the latter value, overriding any previous value.
495 $result[$key] = $value;