1 <?php
2 /**
3 * ArrayX class file.
4 *
5 * @author Antonio Ramirez <amigo.cobos@gmail.com>
6 * @link http://www.ramirezcobos.com/
7 * @link http://www.2amigos.us/
8 * @copyright 2013 2amigOS! Consultation Group LLC
9 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
10 */
11 namespace Yiinitializr\Helpers;
12
13 /**
14 * ArrayX provides a set of useful functions
15 *
16 * @author Antonio Ramirez <amigo.cobos@gmail.com>
17 * @package Yiinitializr.helpers
18 * @since 1.0
19 */
20 class ArrayX
21 {
22 /**
23 * Get an item from an array using "dot" notation.
24 *
25 * <code>
26 * // Get the $array['user']['name'] value from the array
27 * $name = ArrayX::get($array, 'user.name');
28 *
29 * // Return a default from if the specified item doesn't exist
30 * $name = ArrayX::get($array, 'user.name', 'Taylor');
31 * </code>
32 *
33 * @param array $array
34 * @param string $key
35 * @param mixed $default
36 * @return mixed
37 */
38 public static function get($array, $key, $default = null)
39 {
40 if (is_null($key)) return $array;
41
42 // To retrieve the array item using dot syntax, we'll iterate through
43 // each segment in the key and look for that value. If it exists, we
44 // will return it, otherwise we will set the depth of the array and
45 // look for the next segment.
46 foreach (explode('.', $key) as $segment)
47 {
48 if (!is_array($array) || !array_key_exists($segment, $array))
49 {
50 return self::value($default);
51 }
52
53 $array = $array[$segment];
54 }
55
56 return $array;
57 }
58
59 /**
60 * Removes an item from the given options and returns the value.
61 *
62 * If no key is found, then default value will be returned.
63 *
64 * @param $array
65 * @param $key
66 * @param null $default
67 * @return mixed|null
68 */
69 public static function pop(&$array, $key, $default = null)
70 {
71 if (is_array($array))
72 {
73 $value = self::get($array, $key, $default);
74 unset($array[$key]);
75 return $value;
76 } else
77 return $default;
78 }
79
80 /**
81 * Set an array item to a given value using "dot" notation.
82 *
83 * If no key is given to the method, the entire array will be replaced.
84 *
85 * <code>
86 * // Set the $array['user']['name'] value on the array
87 * ArrayX::set($array, 'user.name', 'Taylor');
88 *
89 * // Set the $array['user']['name']['first'] value on the array
90 * ArrayX::set($array, 'user.name.first', 'Michael');
91 * </code>
92 *
93 * @param array $array
94 * @param string $key
95 * @param mixed $value
96 * @return void
97 */
98 public static function set(&$array, $key, $value)
99 {
100 if (is_null($key)) return $array = $value;
101
102 $keys = explode('.', $key);
103
104 // This loop allows us to dig down into the array to a dynamic depth by
105 // setting the array value for each level that we dig into. Once there
106 // is one key left, we can fall out of the loop and set the value as
107 // we should be at the proper depth.
108 while (count($keys) > 1)
109 {
110 $key = array_shift($keys);
111
112 // If the key doesn't exist at this depth, we will just create an
113 // empty array to hold the next value, allowing us to create the
114 // arrays to hold the final value.
115 if (!isset($array[$key]) || !is_array($array[$key]))
116 $array[$key] = array();
117
118 $array =& $array[$key];
119 }
120
121 $array[array_shift($keys)] = $value;
122 }
123
124 /**
125 * Remove an array item from a given array using "dot" notation.
126 *
127 * <code>
128 * // Remove the $array['user']['name'] item from the array
129 * ArrayX::forget($array, 'user.name');
130 *
131 * // Remove the $array['user']['name']['first'] item from the array
132 * ArrayX::forget($array, 'user.name.first');
133 * </code>
134 *
135 * @param array $array
136 * @param string $key
137 * @return void
138 */
139 public static function forget(&$array, $key)
140 {
141 $keys = explode('.', $key);
142
143 // This loop functions very similarly to the loop in the "set" method.
144 // We will iterate over the keys, setting the array value to the new
145 // depth at each iteration. Once there is only one key left, we will
146 // be at the proper depth in the array.
147 while (count($keys) > 1)
148 {
149 $key = array_shift($keys);
150
151 // Since this method is supposed to remove a value from the array,
152 // if a value higher up in the chain doesn't exist, there is no
153 // need to keep digging into the array, since it is impossible
154 // for the final value to even exist.
155 if (!isset($array[$key]) || !is_array($array[$key]))
156 return;
157
158 $array =& $array[$key];
159 }
160
161 unset($array[array_shift($keys)]);
162 }
163
164 /**
165 * Return the first element in an array which passes a given truth test.
166 *
167 * <code>
168 * // Return the first array element that equals "Taylor"
169 * $value = ArrayX::first($array, function($k, $v) {return $v == 'Taylor';});
170 *
171 * // Return a default value if no matching element is found
172 * $value = ArrayX::first($array, function($k, $v) {return $v == 'Taylor'}, 'Default');
173 * </code>
174 *
175 * @param array $array
176 * @param Closure $callback
177 * @param mixed $default
178 * @return mixed
179 */
180 public static function first($array, $callback, $default = null)
181 {
182 foreach ($array as $key => $value)
183 {
184 if (call_user_func($callback, $key, $value)) return $value;
185 }
186
187 return value($default);
188 }
189
190 /**
191 * Recursively remove slashes from array keys and values.
192 *
193 * @param array $array
194 * @return array
195 */
196 public static function stripSlashes($array)
197 {
198 $result = array();
199
200 foreach ($array as $key => $value)
201 {
202 $key = stripslashes($key);
203
204 // If the value is an array, we will just recurse back into the
205 // function to keep stripping the slashes out of the array,
206 // otherwise we will set the stripped value.
207 if (is_array($value))
208 {
209 $result[$key] = array_strip_slashes($value);
210 } else
211 {
212 $result[$key] = stripslashes($value);
213 }
214 }
215
216 return $result;
217 }
218
219 /**
220 * Divide an array into two arrays. One with keys and the other with values.
221 *
222 * @param array $array
223 * @return array
224 */
225 public static function divide($array)
226 {
227 return array(array_keys($array), array_values($array));
228 }
229
230 /**
231 * Pluck an array of values from an array.
232 *
233 * @param array $array
234 * @param string $key
235 * @return array
236 */
237 public static function pluck($array, $key)
238 {
239 return array_map(function ($v) use ($key)
240 {
241 return is_object($v) ? $v->$key : $v[$key];
242
243 }, $array);
244 }
245
246 /**
247 * Get a subset of the items from the given array.
248 *
249 * @param array $array
250 * @param array $keys
251 * @return array
252 */
253 public static function only($array, $keys)
254 {
255 return array_intersect_key($array, array_flip((array)$keys));
256 }
257
258 /**
259 * Get all of the given array except for a specified array of items.
260 *
261 * @param array $array
262 * @param array $keys
263 * @return array
264 */
265 public static function except($array, $keys)
266 {
267 return array_diff_key($array, array_flip((array)$keys));
268 }
269
270
271 /**
272 * Return the first element of an array.
273 *
274 * This is simply a convenient wrapper around the "reset" method.
275 *
276 * @param array $array
277 * @return mixed
278 */
279 public static function head($array)
280 {
281 return reset($array);
282 }
283
284 /**
285 * Merges two or more arrays into one recursively.
286 * If each array has an element with the same string key value, the latter
287 * will overwrite the former (different from array_merge_recursive).
288 * Recursive merging will be conducted if both arrays have an element of array
289 * type and are having the same key.
290 * For integer-keyed elements, the elements from the latter array will
291 * be appended to the former array.
292 * @param array $a array to be merged to
293 * @param array $b array to be merged from. You can specifiy additional
294 * arrays via third argument, fourth argument etc.
295 * @return array the merged array (the original arrays are not changed.)
296 */
297 public static function merge($a, $b)
298 {
299 $args = func_get_args();
300 $res = array_shift($args);
301 while (!empty($args))
302 {
303 $next = array_shift($args);
304 foreach ($next as $k => $v)
305 {
306 if (is_integer($k))
307 isset($res[$k]) ? $res[] = $v : $res[$k] = $v;
308 elseif (is_array($v) && isset($res[$k]) && is_array($res[$k]))
309 $res[$k] = self::merge($res[$k], $v); else
310 $res[$k] = $v;
311 }
312 }
313 return $res;
314 }
315
316 /**
317 * Searches for a given value in an array of arrays, objects and scalar
318 * values. You can optionally specify a field of the nested arrays and
319 * objects to search in.
320 *
321 * Credits to Util.php
322 *
323 * @param array $array The array to search
324 * @param string $search The value to search for
325 * @param bool $field The field to search in, if not specified all fields will be searched
326 * @return bool|mixed|string False on failure or the array key on
327 * @link https://github.com/brandonwamboldt/utilphp/blob/master/util.php
328 */
329 public static function deepSearch(array $array, $search, $field = FALSE)
330 {
331 // *grumbles* stupid PHP type system
332 $search = (string)$search;
333
334 foreach ($array as $key => $elem)
335 {
336
337 // *grumbles* stupid PHP type system
338 $key = (string)$key;
339
340 if ($field)
341 {
342 if (is_object($elem) && $elem->{$field} === $search)
343 {
344 return $key;
345 } else if (is_array($elem) && $elem[$field] === $search)
346 {
347 return $key;
348 } else if (is_scalar($elem) && $elem === $search)
349 {
350 return $key;
351 }
352 } else
353 {
354 if (is_object($elem))
355 {
356 $elem = (array)$elem;
357
358 if (in_array($search, $elem))
359 {
360 return $key;
361 }
362 } else if (is_array($elem) && in_array($search, $elem))
363 {
364 return array_search($search, $elem);
365 } else if (is_scalar($elem) && $elem === $search)
366 {
367 return $key;
368 }
369 }
370 }
371
372 return false;
373 }
374
375 /**
376 * Returns an array containing all the elements of arr1 after applying
377 * the callback function to each one.
378 *
379 * Credits to Util.php
380 *
381 * @param array $array an array to run through the callback function
382 * @param $callback Callback function to run for each element in each array
383 * @param bool $on_nonscalar whether or not to call the callback function on nonscalar values (objects, resr, etc)
384 * @return array
385 * @link https://github.com/brandonwamboldt/utilphp/blob/master/util.php
386 */
387 public static function deepMap(array $array, $callback, $on_nonscalar = FALSE)
388 {
389 foreach ($array as $key => $value)
390 {
391 if (is_array($value))
392 {
393 $args = array($value, $callback, $on_nonscalar);
394 $array[$key] = call_user_func_array(array(__CLASS__, __FUNCTION__), $args);
395 } else if (is_scalar($value) || $on_nonscalar)
396 {
397 $array[$key] = call_user_func($callback, $value);
398 }
399 }
400
401 return $array;
402 }
403
404 /**
405 * Return the value of the given item.
406 *
407 * If the given item is a Closure the result of the Closure will be returned.
408 *
409 * @param mixed $value
410 * @return mixed
411 */
412 public static function value($value)
413 {
414 return (is_callable($value) and !is_string($value)) ? call_user_func($value) : $value;
415 }
416 }