TYPO3 CMS  TYPO3_6-2
ArrayUtility.php
Go to the documentation of this file.
1 <?php
3 
21 class ArrayUtility {
22 
56  static public function filterByValueRecursive($needle = '', array $haystack = array()) {
57  $resultArray = array();
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 = \TYPO3\CMS\Core\Utility\ArrayUtility::filterByValueRecursive($needle, $value));
66  if (count($subArrayMatches) > 0) {
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  static public function isValidPath(array $array, $path, $delimiter = '/') {
96  $isValid = TRUE;
97  try {
98  // Use late static binding to enable mocking of this call in unit tests
99  static::getValueByPath($array, $path, $delimiter);
100  } catch (\RuntimeException $e) {
101  $isValid = FALSE;
102  }
103  return $isValid;
104  }
105 
130  static public function getValueByPath(array $array, $path, $delimiter = '/') {
131  if (empty($path)) {
132  throw new \RuntimeException('Path must not be empty', 1341397767);
133  }
134  // Extract parts of the path
135  $path = str_getcsv($path, $delimiter);
136  // Loop through each part and extract its value
137  $value = $array;
138  foreach ($path as $segment) {
139  if (array_key_exists($segment, $value)) {
140  // Replace current value with child
141  $value = $value[$segment];
142  } else {
143  // Fail if key does not exist
144  throw new \RuntimeException('Path does not exist in array', 1341397869);
145  }
146  }
147  return $value;
148  }
149 
176  static public function setValueByPath(array $array, $path, $value, $delimiter = '/') {
177  if (empty($path)) {
178  throw new \RuntimeException('Path must not be empty', 1341406194);
179  }
180  if (!is_string($path)) {
181  throw new \RuntimeException('Path must be a string', 1341406402);
182  }
183  // Extract parts of the path
184  $path = str_getcsv($path, $delimiter);
185  // Point to the root of the array
186  $pointer = &$array;
187  // Find path in given array
188  foreach ($path as $segment) {
189  // Fail if the part is empty
190  if (empty($segment)) {
191  throw new \RuntimeException('Invalid path segment specified', 1341406846);
192  }
193  // Create cell if it doesn't exist
194  if (!array_key_exists($segment, $pointer)) {
195  $pointer[$segment] = array();
196  }
197  // Set pointer to new cell
198  $pointer = &$pointer[$segment];
199  }
200  // Set value of target cell
201  $pointer = $value;
202  return $array;
203  }
204 
214  static public function removeByPath(array $array, $path, $delimiter = '/') {
215  if (empty($path)) {
216  throw new \RuntimeException('Path must not be empty', 1371757718);
217  }
218  if (!is_string($path)) {
219  throw new \RuntimeException('Path must be a string', 1371757719);
220  }
221  // Extract parts of the path
222  $path = str_getcsv($path, $delimiter);
223  $pathDepth = count($path);
224  $currentDepth = 0;
225  $pointer = &$array;
226  // Find path in given array
227  foreach ($path as $segment) {
228  $currentDepth++;
229  // Fail if the part is empty
230  if (empty($segment)) {
231  throw new \RuntimeException('Invalid path segment specified', 1371757720);
232  }
233  if (!array_key_exists($segment, $pointer)) {
234  throw new \RuntimeException('Path segment ' . $segment . ' does not exist in array', 1371758436);
235  }
236  if ($currentDepth === $pathDepth) {
237  unset($pointer[$segment]);
238  } else {
239  $pointer = &$pointer[$segment];
240  }
241  }
242  return $array;
243  }
244 
251  static public function sortByKeyRecursive(array $array) {
252  ksort($array);
253  foreach ($array as $key => $value) {
254  if (is_array($value) && !empty($value)) {
255  $array[$key] = self::sortByKeyRecursive($value);
256  }
257  }
258  return $array;
259  }
260 
270  static public function sortArraysByKey(array $arrays, $key, $ascending = TRUE) {
271  if (empty($arrays)) {
272  return $arrays;
273  }
274  // @ operator used: Some PHP versions like 5.4.4-14+deb7u8 (debian wheezy) are
275  // affected by PHP bug https://bugs.php.net/bug.php?id=50688 and trigger a warning.
276  // The code itself is ok and covered by unit tests, so the @ operator is used to
277  // suppress output of the PHP bug. This can be removed if the core does not
278  // support PHP version affected by this issue anymore.
279  $sortResult = @uasort($arrays, function (array $a, array $b) use ($key, $ascending) {
280  if (!isset($a[$key]) || !isset($b[$key])) {
281  throw new \RuntimeException('The specified sorting key "' . $key . '" is not available in the given array.', 1373727309);
282  }
283  return ($ascending) ? strcasecmp($a[$key], $b[$key]) : strcasecmp($b[$key], $a[$key]);
284  });
285  if (!$sortResult) {
286  throw new \RuntimeException('The function uasort() failed for unknown reasons.', 1373727329);
287  }
288  return $arrays;
289  }
290 
302  static public function arrayExport(array $array = array(), $level = 0) {
303  $lines = 'array(' . LF;
304  $level++;
305  $writeKeyIndex = FALSE;
306  $expectedKeyIndex = 0;
307  foreach ($array as $key => $value) {
308  if ($key === $expectedKeyIndex) {
309  $expectedKeyIndex++;
310  } else {
311  // Found a non integer or non consecutive key, so we can break here
312  $writeKeyIndex = TRUE;
313  break;
314  }
315  }
316  foreach ($array as $key => $value) {
317  // Indention
318  $lines .= str_repeat(TAB, $level);
319  if ($writeKeyIndex) {
320  // Numeric / string keys
321  $lines .= is_int($key) ? $key . ' => ' : '\'' . $key . '\' => ';
322  }
323  if (is_array($value)) {
324  if (count($value) > 0) {
325  $lines .= self::arrayExport($value, $level);
326  } else {
327  $lines .= 'array(),' . LF;
328  }
329  } elseif (is_int($value) || is_float($value)) {
330  $lines .= $value . ',' . LF;
331  } elseif (is_null($value)) {
332  $lines .= 'NULL' . ',' . LF;
333  } elseif (is_bool($value)) {
334  $lines .= $value ? 'TRUE' : 'FALSE';
335  $lines .= ',' . LF;
336  } elseif (is_string($value)) {
337  // Quote \ to \\
338  $stringContent = str_replace('\\', '\\\\', $value);
339  // Quote ' to \'
340  $stringContent = str_replace('\'', '\\\'', $stringContent);
341  $lines .= '\'' . $stringContent . '\'' . ',' . LF;
342  } else {
343  throw new \RuntimeException('Objects are not supported', 1342294987);
344  }
345  }
346  $lines .= str_repeat(TAB, ($level - 1)) . ')' . ($level - 1 == 0 ? '' : ',' . LF);
347  return $lines;
348  }
349 
383  static public function flatten(array $array, $prefix = '') {
384  $flatArray = array();
385  foreach ($array as $key => $value) {
386  // Ensure there is no trailling dot:
387  $key = rtrim($key, '.');
388  if (!is_array($value)) {
389  $flatArray[$prefix . $key] = $value;
390  } else {
391  $flatArray = array_merge($flatArray, self::flatten($value, $prefix . $key . '.'));
392  }
393  }
394  return $flatArray;
395  }
396 
432  public static function intersectRecursive(array $source, array $mask = array()) {
433  $intersection = array();
434  foreach ($source as $key => $_) {
435  if (!array_key_exists($key, $mask)) {
436  continue;
437  }
438  if (is_array($source[$key]) && is_array($mask[$key])) {
439  $value = self::intersectRecursive($source[$key], $mask[$key]);
440  if (!empty($value)) {
441  $intersection[$key] = $value;
442  }
443  } else {
444  $intersection[$key] = $source[$key];
445  }
446  }
447  return $intersection;
448  }
449 
475  static public function renumberKeysToAvoidLeapsIfKeysAreAllNumeric(array $array = array(), $level = 0) {
476  $level++;
477  $allKeysAreNumeric = TRUE;
478  foreach ($array as $key => $_) {
479  if (is_numeric($key) === FALSE) {
480  $allKeysAreNumeric = FALSE;
481  break;
482  }
483  }
484  $renumberedArray = $array;
485  if ($allKeysAreNumeric === TRUE) {
486  $renumberedArray = array_values($array);
487  }
488  foreach ($renumberedArray as $key => $value) {
489  if (is_array($value)) {
490  $renumberedArray[$key] = self::renumberKeysToAvoidLeapsIfKeysAreAllNumeric($value, $level);
491  }
492  }
493  return $renumberedArray;
494  }
495 
496 
517  static public function mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys = TRUE, $includeEmptyValues = TRUE, $enableUnsetFeature = TRUE) {
518  foreach ($overrule as $key => $_) {
519  if ($enableUnsetFeature && $overrule[$key] === '__UNSET') {
520  unset($original[$key]);
521  continue;
522  }
523  if (isset($original[$key]) && is_array($original[$key])) {
524  if (is_array($overrule[$key])) {
525  self::mergeRecursiveWithOverrule($original[$key], $overrule[$key], $addKeys, $includeEmptyValues, $enableUnsetFeature);
526  }
527  } elseif (
528  ($addKeys || isset($original[$key])) &&
529  ($includeEmptyValues || $overrule[$key])
530  ) {
531  $original[$key] = $overrule[$key];
532  }
533  }
534  // This line is kept for backward compatibility reasons.
535  reset($original);
536  }
537 }
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=TRUE, $includeEmptyValues=TRUE, $enableUnsetFeature=TRUE)
static setValueByPath(array $array, $path, $value, $delimiter='/')
static getValueByPath(array $array, $path, $delimiter='/')
static flatten(array $array, $prefix='')
static sortArraysByKey(array $arrays, $key, $ascending=TRUE)
static removeByPath(array $array, $path, $delimiter='/')
static filterByValueRecursive($needle='', array $haystack=array())
static sortByKeyRecursive(array $array)
static renumberKeysToAvoidLeapsIfKeysAreAllNumeric(array $array=array(), $level=0)
static isValidPath(array $array, $path, $delimiter='/')
static intersectRecursive(array $source, array $mask=array())
static arrayExport(array $array=array(), $level=0)