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