[TASK] Re-work/simplify copyright header in PHP files - Part 8
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Utility / ArrayUtility.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Utility;
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
17 /**
18 * The array functions from good old GeneralUtility plus new code.
19 *
20 * @api
21 */
22 class ArrayUtility {
23
24 /**
25 * Explodes a $string delimited by $delimeter and casts each item in the array to (int).
26 * Corresponds to explode(), but with conversion to integers for all values.
27 *
28 * @param string $delimiter Delimiter string to explode with
29 * @param string $string The string to explode
30 * @return array Exploded values, all converted to integers
31 * @api
32 */
33 static public function integerExplode($delimiter, $string) {
34 $explodedValues = explode($delimiter, $string);
35 foreach ($explodedValues as &$value) {
36 $value = (int)$value;
37 }
38 unset($value);
39 return $explodedValues;
40 }
41
42 /**
43 * Explodes a string and trims all values for whitespace in the ends.
44 * If $onlyNonEmptyValues is set, then all blank ('') values are removed.
45 *
46 * @param string $delimiter Delimiter string to explode with
47 * @param string $string The string to explode
48 * @param boolean $onlyNonEmptyValues If set, all empty values (='') will NOT be set in output
49 * @return array Exploded values
50 * @api
51 */
52 static public function trimExplode($delimiter, $string, $onlyNonEmptyValues = FALSE) {
53 $chunksArr = explode($delimiter, $string);
54 $newChunksArr = array();
55 foreach ($chunksArr as $value) {
56 if ($onlyNonEmptyValues === FALSE || trim($value) !== '') {
57 $newChunksArr[] = trim($value);
58 }
59 }
60 reset($newChunksArr);
61 return $newChunksArr;
62 }
63
64 /**
65 * Merges two arrays recursively and "binary safe" (integer keys are overridden as well), overruling similar values in the first array ($firstArray) with the values of the second array ($secondArray)
66 * In case of identical keys, ie. keeping the values of the second.
67 *
68 * @param array $firstArray First array
69 * @param array $secondArray Second array, overruling the first array
70 * @param boolean $dontAddNewKeys If set, keys that are NOT found in $firstArray (first array) will not be set. Thus only existing value can/will be overruled from second array.
71 * @param boolean $emptyValuesOverride If set (which is the default), values from $secondArray will overrule if they are empty (according to PHP's empty() function)
72 * @return array Resulting array where $secondArray values has overruled $firstArray values
73 * @api
74 */
75 static public function arrayMergeRecursiveOverrule(array $firstArray, array $secondArray, $dontAddNewKeys = FALSE, $emptyValuesOverride = TRUE) {
76 foreach ($secondArray as $key => $value) {
77 if (array_key_exists($key, $firstArray) && is_array($firstArray[$key])) {
78 if (is_array($secondArray[$key])) {
79 $firstArray[$key] = self::arrayMergeRecursiveOverrule($firstArray[$key], $secondArray[$key], $dontAddNewKeys, $emptyValuesOverride);
80 } else {
81 $firstArray[$key] = $secondArray[$key];
82 }
83 } else {
84 if ($dontAddNewKeys) {
85 if (array_key_exists($key, $firstArray)) {
86 if ($emptyValuesOverride || !empty($value)) {
87 $firstArray[$key] = $value;
88 }
89 }
90 } else {
91 if ($emptyValuesOverride || !empty($value)) {
92 $firstArray[$key] = $value;
93 }
94 }
95 }
96 }
97 reset($firstArray);
98 return $firstArray;
99 }
100
101 /**
102 * Randomizes the order of array values. The array should not be an associative array
103 * as the key-value relations will be lost.
104 *
105 * @param array $array Array to reorder
106 * @return array The array with randomly ordered values
107 * @api
108 */
109 static public function randomizeArrayOrder(array $array) {
110 $reorderedArray = array();
111 if (count($array) > 1) {
112 $keysInRandomOrder = array_rand($array, count($array));
113 foreach ($keysInRandomOrder as $key) {
114 $reorderedArray[] = $array[$key];
115 }
116 } else {
117 $reorderedArray = $array;
118 }
119 return $reorderedArray;
120 }
121
122 /**
123 * Returns TRUE if the given array contains elements of varying types
124 *
125 * @param array $array
126 * @return boolean
127 * @api
128 */
129 static public function containsMultipleTypes(array $array) {
130 if (count($array) > 0) {
131 foreach ($array as $value) {
132 if (!isset($previousType)) {
133 $previousType = gettype($value);
134 } elseif ($previousType !== gettype($value)) {
135 return TRUE;
136 }
137 }
138 }
139 return FALSE;
140 }
141
142 /**
143 * Replacement for array_reduce that allows any type for $initial (instead
144 * of only integer)
145 *
146 * @param array $array the array to reduce
147 * @param string $function the reduce function with the same order of parameters as in the native array_reduce (i.e. accumulator first, then current array element)
148 * @param mixed $initial the initial accumulator value
149 * @return mixed
150 * @api
151 */
152 static public function array_reduce(array $array, $function, $initial = NULL) {
153 $accumlator = $initial;
154 foreach ($array as $value) {
155 $accumlator = $function($accumlator, $value);
156 }
157 return $accumlator;
158 }
159
160 /**
161 * Returns the value of a nested array by following the specifed path.
162 *
163 * @param array &$array The array to traverse as a reference
164 * @param array|string $path The path to follow. Either a simple array of keys or a string in the format 'foo.bar.baz'
165 * @throws \InvalidArgumentException
166 * @return mixed The value found, NULL if the path didn't exist
167 * @api
168 */
169 static public function getValueByPath(array &$array, $path) {
170 if (is_string($path)) {
171 $path = explode('.', $path);
172 } elseif (!is_array($path)) {
173 throw new \InvalidArgumentException('getValueByPath() expects $path to be string or array, "' . gettype($path) . '" given.', 1304950007);
174 }
175 $key = array_shift($path);
176 if (isset($array[$key])) {
177 if (count($path) > 0) {
178 return is_array($array[$key]) ? self::getValueByPath($array[$key], $path) : NULL;
179 } else {
180 return $array[$key];
181 }
182 } else {
183 return NULL;
184 }
185 }
186
187 /**
188 * Sets the given value in a nested array or object by following the specified path.
189 *
190 * @param array|\ArrayAccess $subject The array or ArrayAccess instance to work on
191 * @param array|string $path The path to follow. Either a simple array of keys or a string in the format 'foo.bar.baz'
192 * @param mixed $value The value to set
193 * @throws \InvalidArgumentException
194 * @return array The modified array or object
195 */
196 static public function setValueByPath($subject, $path, $value) {
197 if (!is_array($subject) && !$subject instanceof \ArrayAccess) {
198 throw new \InvalidArgumentException('setValueByPath() expects $subject to be array or an object implementing \\ArrayAccess, "' . (is_object($subject) ? get_class($subject) : gettype($subject)) . '" given.', 1306424308);
199 }
200 if (is_string($path)) {
201 $path = explode('.', $path);
202 } elseif (!is_array($path)) {
203 throw new \InvalidArgumentException('setValueByPath() expects $path to be string or array, "' . gettype($path) . '" given.', 1305111499);
204 }
205 $key = array_shift($path);
206 if (count($path) === 0) {
207 $subject[$key] = $value;
208 } else {
209 if (!isset($subject[$key]) || !is_array($subject[$key])) {
210 $subject[$key] = array();
211 }
212 $subject[$key] = self::setValueByPath($subject[$key], $path, $value);
213 }
214 return $subject;
215 }
216
217 /**
218 * Unsets an element/part of a nested array by following the specified path.
219 *
220 * @param array $array The array
221 * @param array|string $path The path to follow. Either a simple array of keys or a string in the format 'foo.bar.baz'
222 * @throws \InvalidArgumentException
223 * @return array The modified array
224 */
225 static public function unsetValueByPath(array $array, $path) {
226 if (is_string($path)) {
227 $path = explode('.', $path);
228 } elseif (!is_array($path)) {
229 throw new \InvalidArgumentException('unsetValueByPath() expects $path to be string or array, "' . gettype($path) . '" given.', 1305111513);
230 }
231 $key = array_shift($path);
232 if (count($path) === 0) {
233 unset($array[$key]);
234 } else {
235 if (!isset($array[$key]) || !is_array($array[$key])) {
236 return $array;
237 }
238 $array[$key] = self::unsetValueByPath($array[$key], $path);
239 }
240 return $array;
241 }
242
243 /**
244 * Sorts multidimensional arrays by recursively calling ksort on its elements.
245 *
246 * @param array &$array the array to sort
247 * @param integer $sortFlags may be used to modify the sorting behavior using these values (see http://www.php.net/manual/en/function.sort.php)
248 * @return boolean TRUE on success, FALSE on failure
249 * @see asort()
250 * @api
251 */
252 static public function sortKeysRecursively(array &$array, $sortFlags = NULL) {
253 foreach ($array as &$value) {
254 if (is_array($value)) {
255 if (self::sortKeysRecursively($value, $sortFlags) === FALSE) {
256 return FALSE;
257 }
258 }
259 }
260 return ksort($array, $sortFlags);
261 }
262
263 /**
264 * Recursively convert an object hierarchy into an associative array.
265 *
266 * @param mixed $subject An object or array of objects
267 * @throws \InvalidArgumentException
268 * @return array The subject represented as an array
269 */
270 static public function convertObjectToArray($subject) {
271 if (!is_object($subject) && !is_array($subject)) {
272 throw new \InvalidArgumentException('convertObjectToArray expects either array or object as input, ' . gettype($subject) . ' given.', 1287059709);
273 }
274 if (is_object($subject)) {
275 $subject = (array) $subject;
276 }
277 foreach ($subject as $key => $value) {
278 if (is_array($value) || is_object($value)) {
279 $subject[$key] = self::convertObjectToArray($value);
280 }
281 }
282 return $subject;
283 }
284
285 /**
286 * Recursively removes empty array elements.
287 *
288 * @param array $array
289 * @return array the modified array
290 */
291 static public function removeEmptyElementsRecursively(array $array) {
292 $result = $array;
293 foreach ($result as $key => $value) {
294 if (is_array($value)) {
295 $result[$key] = self::removeEmptyElementsRecursively($value);
296 if ($result[$key] === array()) {
297 unset($result[$key]);
298 }
299 } elseif ($value === NULL) {
300 unset($result[$key]);
301 }
302 }
303 return $result;
304 }
305
306 /**
307 * If the array contains numerical keys only, sort it in ascending order
308 *
309 * @param array $array
310 *
311 * @return array
312 */
313 public static function sortArrayWithIntegerKeys($array) {
314 $containsNumericalKeysOnly = TRUE;
315 array_walk($array, function($value, $key) use (&$containsNumericalKeysOnly) {
316 if (!is_integer($key)) {
317 $containsNumericalKeysOnly = FALSE;
318 return;
319 }
320 });
321 if ($containsNumericalKeysOnly === TRUE) {
322 ksort($array);
323 }
324 return $array;
325 }
326 }