‪TYPO3CMS  9.5
ArrayUtility.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
18 
23 {
32  public static function ‪assertAllArrayKeysAreValid(array $arrayToTest, array $allowedArrayKeys)
33  {
34  $notAllowedArrayKeys = array_keys(array_diff_key($arrayToTest, array_flip($allowedArrayKeys)));
35  if (count($notAllowedArrayKeys) !== 0) {
36  throw new \InvalidArgumentException(
37  sprintf(
38  'The options "%s" were not allowed (allowed were: "%s")',
39  implode(', ', $notAllowedArrayKeys),
40  implode(', ', $allowedArrayKeys)
41  ),
42  1325697085
43  );
44  }
45  }
46 
53  public static function ‪convertBooleanStringsToBooleanRecursive(array $array): array
54  {
55  $result = $array;
56  foreach ($result as $key => $value) {
57  if (is_array($value)) {
59  } else {
60  if ($value === 'true') {
61  $result[$key] = true;
62  } elseif ($value === 'false') {
63  $result[$key] = false;
64  }
65  }
66  }
67  return $result;
68  }
69 
103  public static function ‪filterByValueRecursive($needle = '', array $haystack = [])
104  {
105  $resultArray = [];
106  // Define a lambda function to be applied to all members of this array dimension
107  // Call recursive if current value is of type array
108  // Write to $resultArray (by reference!) if types and value match
109  $callback = function (&$value, $key) use ($needle, &$resultArray) {
110  if ($value === $needle) {
111  $resultArray[$key] = $value;
112  } elseif (is_array($value)) {
113  $subArrayMatches = static::filterByValueRecursive($needle, $value);
114  if (!empty($subArrayMatches)) {
115  $resultArray[$key] = $subArrayMatches;
116  }
117  }
118  };
119  // array_walk() is not affected by the internal pointers, no need to reset
120  array_walk($haystack, $callback);
121  // Pointers to result array are reset internally
122  return $resultArray;
123  }
124 
143  public static function ‪isValidPath(array $array, $path, $delimiter = '/')
144  {
145  $isValid = true;
146  try {
147  static::getValueByPath($array, $path, $delimiter);
148  } catch (‪MissingArrayPathException $e) {
149  $isValid = false;
150  }
151  return $isValid;
152  }
153 
179  public static function ‪getValueByPath(array $array, $path, $delimiter = '/')
180  {
181  // Extract parts of the path
182  if (is_string($path)) {
183  if ($path === '') {
184  // Programming error has to be sanitized before calling the method -> global exception
185  throw new \RuntimeException('Path must not be empty', 1341397767);
186  }
187  $path = str_getcsv($path, $delimiter);
188  } elseif (!is_array($path)) {
189  // Programming error has to be sanitized before calling the method -> global exception
190  throw new \InvalidArgumentException('getValueByPath() expects $path to be string or array, "' . gettype($path) . '" given.', 1476557628);
191  }
192  // Loop through each part and extract its value
193  $value = $array;
194  foreach ($path as $segment) {
195  if (is_array($value) && array_key_exists($segment, $value)) {
196  // Replace current value with child
197  $value = $value[$segment];
198  } else {
199  // Throw specific exception if there is no such path
200  throw new ‪MissingArrayPathException('Segment ' . $segment . ' of path ' . implode($delimiter, $path) . ' does not exist in array', 1341397869);
201  }
202  }
203  return $value;
204  }
205 
213  public static function ‪reIndexNumericArrayKeysRecursive(array $array): array
214  {
215  if (count(array_filter(array_keys($array), 'is_string')) === 0) {
216  $array = array_values($array);
217  }
218  foreach ($array as $key => $value) {
219  if (is_array($value) && !empty($value)) {
220  $array[$key] = ‪self::reIndexNumericArrayKeysRecursive($value);
221  }
222  }
223  return $array;
224  }
225 
232  public static function ‪removeNullValuesRecursive(array $array): array
233  {
234  $result = $array;
235  foreach ($result as $key => $value) {
236  if (is_array($value)) {
237  $result[$key] = ‪self::removeNullValuesRecursive($value);
238  } elseif ($value === null) {
239  unset($result[$key]);
240  }
241  }
242  return $result;
243  }
244 
271  public static function ‪setValueByPath(array $array, $path, $value, $delimiter = '/')
272  {
273  if (is_string($path)) {
274  if ($path === '') {
275  throw new \RuntimeException('Path must not be empty', 1341406194);
276  }
277  // Extract parts of the path
278  $path = str_getcsv($path, $delimiter);
279  } elseif (!is_array($path) && !$path instanceof \ArrayAccess) {
280  throw new \InvalidArgumentException('setValueByPath() expects $path to be string, array or an object implementing \\ArrayAccess, "' . (is_object($path) ? get_class($path) : gettype($path)) . '" given.', 1478781081);
281  }
282  // Point to the root of the array
283  $pointer = &$array;
284  // Find path in given array
285  foreach ($path as $segment) {
286  // Fail if the part is empty
287  if ($segment === '') {
288  throw new \RuntimeException('Invalid path segment specified', 1341406846);
289  }
290  // Create cell if it doesn't exist
291  if (!array_key_exists($segment, $pointer)) {
292  $pointer[$segment] = [];
293  }
294  // Set pointer to new cell
295  $pointer = &$pointer[$segment];
296  }
297  // Set value of target cell
298  $pointer = $value;
299  return $array;
300  }
301 
311  public static function ‪removeByPath(array $array, $path, $delimiter = '/')
312  {
313  if (!is_string($path)) {
314  throw new \RuntimeException('Path must be a string', 1371757719);
315  }
316  if ($path === '') {
317  throw new \RuntimeException('Path must not be empty', 1371757718);
318  }
319  // Extract parts of the path
320  $path = str_getcsv($path, $delimiter);
321  $pathDepth = count($path);
322  $currentDepth = 0;
323  $pointer = &$array;
324  // Find path in given array
325  foreach ($path as $segment) {
326  $currentDepth++;
327  // Fail if the part is empty
328  if ($segment === '') {
329  throw new \RuntimeException('Invalid path segment specified', 1371757720);
330  }
331  if (!array_key_exists($segment, $pointer)) {
332  throw new ‪MissingArrayPathException('Segment ' . $segment . ' of path ' . implode($delimiter, $path) . ' does not exist in array', 1371758436);
333  }
334  if ($currentDepth === $pathDepth) {
335  unset($pointer[$segment]);
336  } else {
337  $pointer = &$pointer[$segment];
338  }
339  }
340  return $array;
341  }
342 
349  public static function ‪sortByKeyRecursive(array $array)
350  {
351  ksort($array);
352  foreach ($array as $key => $value) {
353  if (is_array($value) && !empty($value)) {
354  $array[$key] = ‪self::sortByKeyRecursive($value);
355  }
356  }
357  return $array;
358  }
359 
369  public static function ‪sortArraysByKey(array $arrays, $key, $ascending = true)
370  {
371  if (empty($arrays)) {
372  return $arrays;
373  }
374  $sortResult = uasort($arrays, function (array $a, array $b) use ($key, $ascending) {
375  if (!isset($a[$key]) || !isset($b[$key])) {
376  throw new \RuntimeException('The specified sorting key "' . $key . '" is not available in the given array.', 1373727309);
377  }
378  return $ascending ? strcasecmp($a[$key], $b[$key]) : strcasecmp($b[$key], $a[$key]);
379  });
380  if (!$sortResult) {
381  throw new \RuntimeException('The function uasort() failed for unknown reasons.', 1373727329);
382  }
383  return $arrays;
384  }
385 
397  public static function ‪arrayExport(array $array = [], $level = 0)
398  {
399  $lines = "[\n";
400  $level++;
401  $writeKeyIndex = false;
402  $expectedKeyIndex = 0;
403  foreach ($array as $key => $value) {
404  if ($key === $expectedKeyIndex) {
405  $expectedKeyIndex++;
406  } else {
407  // Found a non integer or non consecutive key, so we can break here
408  $writeKeyIndex = true;
409  break;
410  }
411  }
412  foreach ($array as $key => $value) {
413  // Indention
414  $lines .= str_repeat(' ', $level);
415  if ($writeKeyIndex) {
416  // Numeric / string keys
417  $lines .= is_int($key) ? $key . ' => ' : '\'' . $key . '\' => ';
418  }
419  if (is_array($value)) {
420  if (!empty($value)) {
421  $lines .= self::arrayExport($value, $level);
422  } else {
423  $lines .= "[],\n";
424  }
425  } elseif (is_int($value) || is_float($value)) {
426  $lines .= $value . ",\n";
427  } elseif ($value === null) {
428  $lines .= "null,\n";
429  } elseif (is_bool($value)) {
430  $lines .= $value ? 'true' : 'false';
431  $lines .= ",\n";
432  } elseif (is_string($value)) {
433  // Quote \ to \\
434  // Quote ' to \'
435  $stringContent = str_replace(['\\', '\''], ['\\\\', '\\\''], $value);
436  $lines .= '\'' . $stringContent . "',\n";
437  } else {
438  throw new \RuntimeException('Objects are not supported', 1342294987);
439  }
440  }
441  $lines .= str_repeat(' ', $level - 1) . ']' . ($level - 1 == 0 ? '' : ",\n");
442  return $lines;
443  }
444 
479  public static function ‪flatten(array $array, $prefix = '')
480  {
481  $flatArray = [];
482  foreach ($array as $key => $value) {
483  // Ensure there is no trailing dot:
484  $key = rtrim($key, '.');
485  if (!is_array($value)) {
486  $flatArray[$prefix . $key] = $value;
487  } else {
488  $flatArray = array_merge($flatArray, self::flatten($value, $prefix . $key . '.'));
489  }
490  }
491  return $flatArray;
492  }
493 
529  public static function ‪intersectRecursive(array $source, array $mask = [])
530  {
531  $intersection = [];
532  foreach ($source as $key => $_) {
533  if (!array_key_exists($key, $mask)) {
534  continue;
535  }
536  if (is_array($source[$key]) && is_array($mask[$key])) {
537  $value = ‪self::intersectRecursive($source[$key], $mask[$key]);
538  if (!empty($value)) {
539  $intersection[$key] = $value;
540  }
541  } else {
542  $intersection[$key] = $source[$key];
543  }
544  }
545  return $intersection;
546  }
547 
573  public static function ‪renumberKeysToAvoidLeapsIfKeysAreAllNumeric(array $array = [], $level = 0)
574  {
575  $level++;
576  $allKeysAreNumeric = true;
577  foreach ($array as $key => $_) {
578  if (is_int($key) === false) {
579  $allKeysAreNumeric = false;
580  break;
581  }
582  }
583  $renumberedArray = $array;
584  if ($allKeysAreNumeric === true) {
585  $renumberedArray = array_values($array);
586  }
587  foreach ($renumberedArray as $key => $value) {
588  if (is_array($value)) {
589  $renumberedArray[$key] = ‪self::renumberKeysToAvoidLeapsIfKeysAreAllNumeric($value, $level);
590  }
591  }
592  return $renumberedArray;
593  }
594 
614  public static function ‪mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys = true, $includeEmptyValues = true, $enableUnsetFeature = true)
615  {
616  foreach ($overrule as $key => $_) {
617  if ($enableUnsetFeature && $overrule[$key] === '__UNSET') {
618  unset($original[$key]);
619  continue;
620  }
621  if (isset($original[$key]) && is_array($original[$key])) {
622  if (is_array($overrule[$key])) {
623  ‪self::mergeRecursiveWithOverrule($original[$key], $overrule[$key], $addKeys, $includeEmptyValues, $enableUnsetFeature);
624  }
625  } elseif (
626  ($addKeys || isset($original[$key])) &&
627  ($includeEmptyValues || $overrule[$key])
628  ) {
629  $original[$key] = $overrule[$key];
630  }
631  }
632  // This line is kept for backward compatibility reasons.
633  reset($original);
634  }
635 
643  public static function ‪removeArrayEntryByValue(array $array, $cmpValue)
644  {
645  foreach ($array as $k => $v) {
646  if (is_array($v)) {
647  $array[$k] = ‪self::removeArrayEntryByValue($v, $cmpValue);
648  } elseif ((string)$v === (string)$cmpValue) {
649  unset($array[$k]);
650  }
651  }
652  return $array;
653  }
654 
678  public static function ‪keepItemsInArray(array $array, $keepItems, $getValueFunc = null)
679  {
680  if ($array) {
681  // Convert strings to arrays:
682  if (is_string($keepItems)) {
683  $keepItems = GeneralUtility::trimExplode(',', $keepItems);
684  }
685  // Check if valueFunc can be executed:
686  if (!is_callable($getValueFunc)) {
687  $getValueFunc = null;
688  }
689  // Do the filtering:
690  if (is_array($keepItems) && !empty($keepItems)) {
691  $keepItems = array_flip($keepItems);
692  foreach ($array as $key => $value) {
693  // Get the value to compare by using the callback function:
694  $keepValue = isset($getValueFunc) ? call_user_func($getValueFunc, $value) : $value;
695  if (!isset($keepItems[$keepValue])) {
696  unset($array[$key]);
697  }
698  }
699  }
700  }
701  return $array;
702  }
703 
710  public static function ‪remapArrayKeys(array &$array, array $mappingTable)
711  {
712  foreach ($mappingTable as $old => $new) {
713  if ($new && isset($array[$old])) {
714  $array[$new] = $array[$old];
715  unset($array[$old]);
716  }
717  }
718  }
719 
728  public static function ‪arrayDiffAssocRecursive(array $array1, array $array2)
729  {
730  $differenceArray = [];
731  foreach ($array1 as $key => $value) {
732  if (!array_key_exists($key, $array2)) {
733  $differenceArray[$key] = $value;
734  } elseif (is_array($value)) {
735  if (is_array($array2[$key])) {
736  $recursiveResult = ‪self::arrayDiffAssocRecursive($value, $array2[$key]);
737  if (!empty($recursiveResult)) {
738  $differenceArray[$key] = $recursiveResult;
739  }
740  }
741  }
742  }
743  return $differenceArray;
744  }
745 
752  public static function ‪naturalKeySortRecursive(array &$array)
753  {
754  uksort($array, 'strnatcasecmp');
755  foreach ($array as $key => &$value) {
756  if (is_array($value)) {
758  }
759  }
760 
761  return true;
762  }
763 
772  public static function ‪filterAndSortByNumericKeys($setupArr, $acceptAnyKeys = false)
773  {
774  $filteredKeys = [];
775  $keys = array_keys($setupArr);
776  foreach ($keys as $key) {
777  if ($acceptAnyKeys || ‪MathUtility::canBeInterpretedAsInteger($key)) {
778  $filteredKeys[] = (int)$key;
779  }
780  }
781  $filteredKeys = array_unique($filteredKeys);
782  sort($filteredKeys);
783  return $filteredKeys;
784  }
785 
793  public static function ‪sortArrayWithIntegerKeys(array $array)
794  {
795  if (count(array_filter(array_keys($array), 'is_string')) === 0) {
796  ksort($array);
797  }
798  return $array;
799  }
800 
808  public static function ‪sortArrayWithIntegerKeysRecursive(array $array): array
809  {
810  $array = static::sortArrayWithIntegerKeys($array);
811  foreach ($array as $key => $value) {
812  if (is_array($value) && !empty($value)) {
813  $array[$key] = ‪self::sortArrayWithIntegerKeysRecursive($value);
814  }
815  }
816  return $array;
817  }
818 
825  public static function ‪stripTagsFromValuesRecursive(array $array): array
826  {
827  $result = $array;
828  foreach ($result as $key => $value) {
829  if (is_array($value)) {
830  $result[$key] = ‪self::stripTagsFromValuesRecursive($value);
831  } elseif (is_string($value) || (is_object($value) && method_exists($value, '__toString'))) {
832  $result[$key] = strip_tags($value);
833  }
834  }
835  return $result;
836  }
837 
846  public static function ‪filterRecursive(array $array, callable $callback = null): array
847  {
848  $callback = $callback ?: function ($value) {
849  return (bool)$value;
850  };
851 
852  foreach ($array as $key => $value) {
853  if (is_array($value)) {
854  $array[$key] = ‪self::filterRecursive($value, $callback);
855  }
856 
857  if (!call_user_func($callback, $value)) {
858  unset($array[$key]);
859  }
860  }
861 
862  return $array;
863  }
864 }
‪TYPO3\CMS\Core\Utility\ArrayUtility\keepItemsInArray
‪static array keepItemsInArray(array $array, $keepItems, $getValueFunc=null)
Definition: ArrayUtility.php:678
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:73
‪TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException
Definition: MissingArrayPathException.php:26
‪TYPO3\CMS\Core\Utility\ArrayUtility\isValidPath
‪static bool isValidPath(array $array, $path, $delimiter='/')
Definition: ArrayUtility.php:143
‪TYPO3\CMS\Core\Utility
Definition: ArrayUtility.php:2
‪TYPO3\CMS\Core\Utility\ArrayUtility\filterRecursive
‪static array filterRecursive(array $array, callable $callback=null)
Definition: ArrayUtility.php:846
‪TYPO3\CMS\Core\Utility\ArrayUtility\sortByKeyRecursive
‪static array sortByKeyRecursive(array $array)
Definition: ArrayUtility.php:349
‪TYPO3\CMS\Core\Utility\ArrayUtility\arrayExport
‪static string arrayExport(array $array=[], $level=0)
Definition: ArrayUtility.php:397
‪TYPO3\CMS\Core\Utility\ArrayUtility\mergeRecursiveWithOverrule
‪static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
Definition: ArrayUtility.php:614
‪TYPO3\CMS\Core\Utility\ArrayUtility\getValueByPath
‪static mixed getValueByPath(array $array, $path, $delimiter='/')
Definition: ArrayUtility.php:179
‪TYPO3\CMS\Core\Utility\ArrayUtility\filterByValueRecursive
‪static array filterByValueRecursive($needle='', array $haystack=[])
Definition: ArrayUtility.php:103
‪TYPO3\CMS\Core\Utility\ArrayUtility\removeArrayEntryByValue
‪static array removeArrayEntryByValue(array $array, $cmpValue)
Definition: ArrayUtility.php:643
‪TYPO3\CMS\Core\Utility\ArrayUtility\intersectRecursive
‪static array intersectRecursive(array $source, array $mask=[])
Definition: ArrayUtility.php:529
‪TYPO3\CMS\Core\Utility\ArrayUtility\removeByPath
‪static array removeByPath(array $array, $path, $delimiter='/')
Definition: ArrayUtility.php:311
‪TYPO3\CMS\Core\Utility\ArrayUtility\stripTagsFromValuesRecursive
‪static array stripTagsFromValuesRecursive(array $array)
Definition: ArrayUtility.php:825
‪TYPO3\CMS\Core\Utility\ArrayUtility\setValueByPath
‪static array setValueByPath(array $array, $path, $value, $delimiter='/')
Definition: ArrayUtility.php:271
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:23
‪TYPO3\CMS\Core\Utility\ArrayUtility\assertAllArrayKeysAreValid
‪static assertAllArrayKeysAreValid(array $arrayToTest, array $allowedArrayKeys)
Definition: ArrayUtility.php:32
‪TYPO3\CMS\Core\Utility\ArrayUtility\flatten
‪static array flatten(array $array, $prefix='')
Definition: ArrayUtility.php:479
‪TYPO3\CMS\Core\Utility\ArrayUtility\removeNullValuesRecursive
‪static array removeNullValuesRecursive(array $array)
Definition: ArrayUtility.php:232
‪TYPO3\CMS\Core\Utility\ArrayUtility\reIndexNumericArrayKeysRecursive
‪static array reIndexNumericArrayKeysRecursive(array $array)
Definition: ArrayUtility.php:213
‪TYPO3\CMS\Core\Utility\ArrayUtility\sortArrayWithIntegerKeysRecursive
‪static array sortArrayWithIntegerKeysRecursive(array $array)
Definition: ArrayUtility.php:808
‪TYPO3\CMS\Core\Utility\ArrayUtility\sortArraysByKey
‪static array sortArraysByKey(array $arrays, $key, $ascending=true)
Definition: ArrayUtility.php:369
‪TYPO3\CMS\Core\Utility\ArrayUtility\renumberKeysToAvoidLeapsIfKeysAreAllNumeric
‪static array renumberKeysToAvoidLeapsIfKeysAreAllNumeric(array $array=[], $level=0)
Definition: ArrayUtility.php:573
‪TYPO3\CMS\Core\Utility\ArrayUtility\naturalKeySortRecursive
‪static bool naturalKeySortRecursive(array &$array)
Definition: ArrayUtility.php:752
‪TYPO3\CMS\Core\Utility\ArrayUtility\remapArrayKeys
‪static remapArrayKeys(array &$array, array $mappingTable)
Definition: ArrayUtility.php:710
‪TYPO3\CMS\Core\Utility\ArrayUtility\sortArrayWithIntegerKeys
‪static array sortArrayWithIntegerKeys(array $array)
Definition: ArrayUtility.php:793
‪TYPO3\CMS\Core\Utility\ArrayUtility\filterAndSortByNumericKeys
‪static array filterAndSortByNumericKeys($setupArr, $acceptAnyKeys=false)
Definition: ArrayUtility.php:772
‪TYPO3\CMS\Core\Utility\ArrayUtility\arrayDiffAssocRecursive
‪static array arrayDiffAssocRecursive(array $array1, array $array2)
Definition: ArrayUtility.php:728
‪TYPO3\CMS\Core\Utility\ArrayUtility\convertBooleanStringsToBooleanRecursive
‪static array convertBooleanStringsToBooleanRecursive(array $array)
Definition: ArrayUtility.php:53