TYPO3 CMS  TYPO3_7-6
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 
21 {
55  public static function filterByValueRecursive($needle = '', array $haystack = [])
56  {
57  $resultArray = [];
58  // Define a lambda function to be applied to all members of this array dimension
59  // Call recursive if current value is of type array
60  // Write to $resultArray (by reference!) if types and value match
61  $callback = function (&$value, $key) use ($needle, &$resultArray) {
62  if ($value === $needle) {
63  ($resultArray[$key] = $value);
64  } elseif (is_array($value)) {
65  ($subArrayMatches = static::filterByValueRecursive($needle, $value));
66  if (!empty($subArrayMatches)) {
67  ($resultArray[$key] = $subArrayMatches);
68  }
69  }
70  };
71  // array_walk() is not affected by the internal pointers, no need to reset
72  array_walk($haystack, $callback);
73  // Pointers to result array are reset internally
74  return $resultArray;
75  }
76 
95  public static function isValidPath(array $array, $path, $delimiter = '/')
96  {
97  $isValid = true;
98  try {
99  // Use late static binding to enable mocking of this call in unit tests
100  static::getValueByPath($array, $path, $delimiter);
101  } catch (\RuntimeException $e) {
102  $isValid = false;
103  }
104  return $isValid;
105  }
106 
131  public static function getValueByPath(array $array, $path, $delimiter = '/')
132  {
133  if (!is_string($path)) {
134  throw new \RuntimeException('Path must be a string', 1477699595);
135  }
136  if ($path === '') {
137  throw new \RuntimeException('Path must not be empty', 1341397767);
138  }
139  // Extract parts of the path
140  $path = str_getcsv($path, $delimiter);
141  // Loop through each part and extract its value
142  $value = $array;
143  foreach ($path as $segment) {
144  if (array_key_exists($segment, $value)) {
145  // Replace current value with child
146  $value = $value[$segment];
147  } else {
148  // Fail if key does not exist
149  throw new \RuntimeException('Path does not exist in array', 1341397869);
150  }
151  }
152  return $value;
153  }
154 
181  public static function setValueByPath(array $array, $path, $value, $delimiter = '/')
182  {
183  if (!is_string($path)) {
184  throw new \RuntimeException('Path must be a string', 1341406402);
185  }
186  if ($path === '') {
187  throw new \RuntimeException('Path must not be empty', 1341406194);
188  }
189  // Extract parts of the path
190  $path = str_getcsv($path, $delimiter);
191  // Point to the root of the array
192  $pointer = &$array;
193  // Find path in given array
194  foreach ($path as $segment) {
195  // Fail if the part is empty
196  if ($segment === '') {
197  throw new \RuntimeException('Invalid path segment specified', 1341406846);
198  }
199  // Create cell if it doesn't exist
200  if (!array_key_exists($segment, $pointer)) {
201  $pointer[$segment] = [];
202  }
203  // Set pointer to new cell
204  $pointer = &$pointer[$segment];
205  }
206  // Set value of target cell
207  $pointer = $value;
208  return $array;
209  }
210 
220  public static function removeByPath(array $array, $path, $delimiter = '/')
221  {
222  if (!is_string($path)) {
223  throw new \RuntimeException('Path must be a string', 1371757719);
224  }
225  if ($path === '') {
226  throw new \RuntimeException('Path must not be empty', 1371757718);
227  }
228  // Extract parts of the path
229  $path = str_getcsv($path, $delimiter);
230  $pathDepth = count($path);
231  $currentDepth = 0;
232  $pointer = &$array;
233  // Find path in given array
234  foreach ($path as $segment) {
235  $currentDepth++;
236  // Fail if the part is empty
237  if ($segment === '') {
238  throw new \RuntimeException('Invalid path segment specified', 1371757720);
239  }
240  if (!array_key_exists($segment, $pointer)) {
241  throw new \RuntimeException('Path segment ' . $segment . ' does not exist in array', 1371758436);
242  }
243  if ($currentDepth === $pathDepth) {
244  unset($pointer[$segment]);
245  } else {
246  $pointer = &$pointer[$segment];
247  }
248  }
249  return $array;
250  }
251 
258  public static function sortByKeyRecursive(array $array)
259  {
260  ksort($array);
261  foreach ($array as $key => $value) {
262  if (is_array($value) && !empty($value)) {
263  $array[$key] = self::sortByKeyRecursive($value);
264  }
265  }
266  return $array;
267  }
268 
278  public static function sortArraysByKey(array $arrays, $key, $ascending = true)
279  {
280  if (empty($arrays)) {
281  return $arrays;
282  }
283  $sortResult = uasort($arrays, function (array $a, array $b) use ($key, $ascending) {
284  if (!isset($a[$key]) || !isset($b[$key])) {
285  throw new \RuntimeException('The specified sorting key "' . $key . '" is not available in the given array.', 1373727309);
286  }
287  return ($ascending) ? strcasecmp($a[$key], $b[$key]) : strcasecmp($b[$key], $a[$key]);
288  });
289  if (!$sortResult) {
290  throw new \RuntimeException('The function uasort() failed for unknown reasons.', 1373727329);
291  }
292  return $arrays;
293  }
294 
306  public static function arrayExport(array $array = [], $level = 0)
307  {
308  $lines = '[' . LF;
309  $level++;
310  $writeKeyIndex = false;
311  $expectedKeyIndex = 0;
312  foreach ($array as $key => $value) {
313  if ($key === $expectedKeyIndex) {
314  $expectedKeyIndex++;
315  } else {
316  // Found a non integer or non consecutive key, so we can break here
317  $writeKeyIndex = true;
318  break;
319  }
320  }
321  foreach ($array as $key => $value) {
322  // Indention
323  $lines .= str_repeat(' ', $level);
324  if ($writeKeyIndex) {
325  // Numeric / string keys
326  $lines .= is_int($key) ? $key . ' => ' : '\'' . $key . '\' => ';
327  }
328  if (is_array($value)) {
329  if (!empty($value)) {
330  $lines .= self::arrayExport($value, $level);
331  } else {
332  $lines .= '[],' . LF;
333  }
334  } elseif (is_int($value) || is_float($value)) {
335  $lines .= $value . ',' . LF;
336  } elseif (is_null($value)) {
337  $lines .= 'null' . ',' . LF;
338  } elseif (is_bool($value)) {
339  $lines .= $value ? 'true' : 'false';
340  $lines .= ',' . LF;
341  } elseif (is_string($value)) {
342  // Quote \ to \\
343  $stringContent = str_replace('\\', '\\\\', $value);
344  // Quote ' to \'
345  $stringContent = str_replace('\'', '\\\'', $stringContent);
346  $lines .= '\'' . $stringContent . '\'' . ',' . LF;
347  } else {
348  throw new \RuntimeException('Objects are not supported', 1342294987);
349  }
350  }
351  $lines .= str_repeat(' ', ($level - 1)) . ']' . ($level - 1 == 0 ? '' : ',' . LF);
352  return $lines;
353  }
354 
388  public static function flatten(array $array, $prefix = '')
389  {
390  $flatArray = [];
391  foreach ($array as $key => $value) {
392  // Ensure there is no trailling dot:
393  $key = rtrim($key, '.');
394  if (!is_array($value)) {
395  $flatArray[$prefix . $key] = $value;
396  } else {
397  $flatArray = array_merge($flatArray, self::flatten($value, $prefix . $key . '.'));
398  }
399  }
400  return $flatArray;
401  }
402 
438  public static function intersectRecursive(array $source, array $mask = [])
439  {
440  $intersection = [];
441  foreach ($source as $key => $_) {
442  if (!array_key_exists($key, $mask)) {
443  continue;
444  }
445  if (is_array($source[$key]) && is_array($mask[$key])) {
446  $value = self::intersectRecursive($source[$key], $mask[$key]);
447  if (!empty($value)) {
448  $intersection[$key] = $value;
449  }
450  } else {
451  $intersection[$key] = $source[$key];
452  }
453  }
454  return $intersection;
455  }
456 
482  public static function renumberKeysToAvoidLeapsIfKeysAreAllNumeric(array $array = [], $level = 0)
483  {
484  $level++;
485  $allKeysAreNumeric = true;
486  foreach ($array as $key => $_) {
487  if (is_numeric($key) === false) {
488  $allKeysAreNumeric = false;
489  break;
490  }
491  }
492  $renumberedArray = $array;
493  if ($allKeysAreNumeric === true) {
494  $renumberedArray = array_values($array);
495  }
496  foreach ($renumberedArray as $key => $value) {
497  if (is_array($value)) {
498  $renumberedArray[$key] = self::renumberKeysToAvoidLeapsIfKeysAreAllNumeric($value, $level);
499  }
500  }
501  return $renumberedArray;
502  }
503 
524  public static function mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys = true, $includeEmptyValues = true, $enableUnsetFeature = true)
525  {
526  foreach ($overrule as $key => $_) {
527  if ($enableUnsetFeature && $overrule[$key] === '__UNSET') {
528  unset($original[$key]);
529  continue;
530  }
531  if (isset($original[$key]) && is_array($original[$key])) {
532  if (is_array($overrule[$key])) {
533  self::mergeRecursiveWithOverrule($original[$key], $overrule[$key], $addKeys, $includeEmptyValues, $enableUnsetFeature);
534  }
535  } elseif (
536  ($addKeys || isset($original[$key])) &&
537  ($includeEmptyValues || $overrule[$key])
538  ) {
539  $original[$key] = $overrule[$key];
540  }
541  }
542  // This line is kept for backward compatibility reasons.
543  reset($original);
544  }
545 
568  public static function inArray(array $in_array, $item)
569  {
570  foreach ($in_array as $val) {
571  if (!is_array($val) && (string)$val === (string)$item) {
572  return true;
573  }
574  }
575  return false;
576  }
577 
585  public static function removeArrayEntryByValue(array $array, $cmpValue)
586  {
587  foreach ($array as $k => $v) {
588  if (is_array($v)) {
589  $array[$k] = self::removeArrayEntryByValue($v, $cmpValue);
590  } elseif ((string)$v === (string)$cmpValue) {
591  unset($array[$k]);
592  }
593  }
594  return $array;
595  }
596 
620  public static function keepItemsInArray(array $array, $keepItems, $getValueFunc = null)
621  {
622  if ($array) {
623  // Convert strings to arrays:
624  if (is_string($keepItems)) {
625  $keepItems = GeneralUtility::trimExplode(',', $keepItems);
626  }
627  // Check if valueFunc can be executed:
628  if (!is_callable($getValueFunc)) {
629  $getValueFunc = null;
630  }
631  // Do the filtering:
632  if (is_array($keepItems) && !empty($keepItems)) {
633  foreach ($array as $key => $value) {
634  // Get the value to compare by using the callback function:
635  $keepValue = isset($getValueFunc) ? call_user_func($getValueFunc, $value) : $value;
636  if (!in_array($keepValue, $keepItems)) {
637  unset($array[$key]);
638  }
639  }
640  }
641  }
642  return $array;
643  }
644 
651  public static function remapArrayKeys(array &$array, array $mappingTable)
652  {
653  foreach ($mappingTable as $old => $new) {
654  if ($new && isset($array[$old])) {
655  $array[$new] = $array[$old];
656  unset($array[$old]);
657  }
658  }
659  }
660 
669  public static function arrayDiffAssocRecursive(array $array1, array $array2)
670  {
671  $differenceArray = [];
672  foreach ($array1 as $key => $value) {
673  if (!array_key_exists($key, $array2)) {
674  $differenceArray[$key] = $value;
675  } elseif (is_array($value)) {
676  if (is_array($array2[$key])) {
677  $recursiveResult = self::arrayDiffAssocRecursive($value, $array2[$key]);
678  if (!empty($recursiveResult)) {
679  $differenceArray[$key] = $recursiveResult;
680  }
681  }
682  }
683  }
684  return $differenceArray;
685  }
686 
693  public static function naturalKeySortRecursive(array &$array)
694  {
695  uksort($array, 'strnatcasecmp');
696  foreach ($array as $key => &$value) {
697  if (is_array($value)) {
698  self::naturalKeySortRecursive($value);
699  }
700  }
701 
702  return true;
703  }
704 }
static filterByValueRecursive($needle='', array $haystack=[])
static setValueByPath(array $array, $path, $value, $delimiter='/')
static getValueByPath(array $array, $path, $delimiter='/')
static intersectRecursive(array $source, array $mask=[])
static flatten(array $array, $prefix='')
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static remapArrayKeys(array &$array, array $mappingTable)
static arrayExport(array $array=[], $level=0)
static naturalKeySortRecursive(array &$array)
static inArray(array $in_array, $item)
static removeByPath(array $array, $path, $delimiter='/')
static sortByKeyRecursive(array $array)
static renumberKeysToAvoidLeapsIfKeysAreAllNumeric(array $array=[], $level=0)
static isValidPath(array $array, $path, $delimiter='/')
static removeArrayEntryByValue(array $array, $cmpValue)
static keepItemsInArray(array $array, $keepItems, $getValueFunc=null)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
static sortArraysByKey(array $arrays, $key, $ascending=true)
static arrayDiffAssocRecursive(array $array1, array $array2)