0e2b738ccbf3f7efda608fa317755c666729363f
[yaffs-website] / vendor / zendframework / zend-stdlib / src / ArrayUtils.php
1 <?php
2 /**
3  * Zend Framework (http://framework.zend.com/)
4  *
5  * @link      http://github.com/zendframework/zf2 for the canonical source repository
6  * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7  * @license   http://framework.zend.com/license/new-bsd New BSD License
8  */
9
10 namespace Zend\Stdlib;
11
12 use Traversable;
13 use Zend\Stdlib\ArrayUtils\MergeRemoveKey;
14 use Zend\Stdlib\ArrayUtils\MergeReplaceKeyInterface;
15
16 /**
17  * Utility class for testing and manipulation of PHP arrays.
18  *
19  * Declared abstract, as we have no need for instantiation.
20  */
21 abstract class ArrayUtils
22 {
23     /**
24      * Compatibility Flag for ArrayUtils::filter
25      */
26     const ARRAY_FILTER_USE_BOTH = 1;
27
28     /**
29      * Compatibility Flag for ArrayUtils::filter
30      */
31     const ARRAY_FILTER_USE_KEY  = 2;
32
33     /**
34      * Test whether an array contains one or more string keys
35      *
36      * @param  mixed $value
37      * @param  bool  $allowEmpty    Should an empty array() return true
38      * @return bool
39      */
40     public static function hasStringKeys($value, $allowEmpty = false)
41     {
42         if (! is_array($value)) {
43             return false;
44         }
45
46         if (! $value) {
47             return $allowEmpty;
48         }
49
50         return count(array_filter(array_keys($value), 'is_string')) > 0;
51     }
52
53     /**
54      * Test whether an array contains one or more integer keys
55      *
56      * @param  mixed $value
57      * @param  bool  $allowEmpty    Should an empty array() return true
58      * @return bool
59      */
60     public static function hasIntegerKeys($value, $allowEmpty = false)
61     {
62         if (! is_array($value)) {
63             return false;
64         }
65
66         if (! $value) {
67             return $allowEmpty;
68         }
69
70         return count(array_filter(array_keys($value), 'is_int')) > 0;
71     }
72
73     /**
74      * Test whether an array contains one or more numeric keys.
75      *
76      * A numeric key can be one of the following:
77      * - an integer 1,
78      * - a string with a number '20'
79      * - a string with negative number: '-1000'
80      * - a float: 2.2120, -78.150999
81      * - a string with float:  '4000.99999', '-10.10'
82      *
83      * @param  mixed $value
84      * @param  bool  $allowEmpty    Should an empty array() return true
85      * @return bool
86      */
87     public static function hasNumericKeys($value, $allowEmpty = false)
88     {
89         if (! is_array($value)) {
90             return false;
91         }
92
93         if (! $value) {
94             return $allowEmpty;
95         }
96
97         return count(array_filter(array_keys($value), 'is_numeric')) > 0;
98     }
99
100     /**
101      * Test whether an array is a list
102      *
103      * A list is a collection of values assigned to continuous integer keys
104      * starting at 0 and ending at count() - 1.
105      *
106      * For example:
107      * <code>
108      * $list = array('a', 'b', 'c', 'd');
109      * $list = array(
110      *     0 => 'foo',
111      *     1 => 'bar',
112      *     2 => array('foo' => 'baz'),
113      * );
114      * </code>
115      *
116      * @param  mixed $value
117      * @param  bool  $allowEmpty    Is an empty list a valid list?
118      * @return bool
119      */
120     public static function isList($value, $allowEmpty = false)
121     {
122         if (! is_array($value)) {
123             return false;
124         }
125
126         if (! $value) {
127             return $allowEmpty;
128         }
129
130         return (array_values($value) === $value);
131     }
132
133     /**
134      * Test whether an array is a hash table.
135      *
136      * An array is a hash table if:
137      *
138      * 1. Contains one or more non-integer keys, or
139      * 2. Integer keys are non-continuous or misaligned (not starting with 0)
140      *
141      * For example:
142      * <code>
143      * $hash = array(
144      *     'foo' => 15,
145      *     'bar' => false,
146      * );
147      * $hash = array(
148      *     1995  => 'Birth of PHP',
149      *     2009  => 'PHP 5.3.0',
150      *     2012  => 'PHP 5.4.0',
151      * );
152      * $hash = array(
153      *     'formElement,
154      *     'options' => array( 'debug' => true ),
155      * );
156      * </code>
157      *
158      * @param  mixed $value
159      * @param  bool  $allowEmpty    Is an empty array() a valid hash table?
160      * @return bool
161      */
162     public static function isHashTable($value, $allowEmpty = false)
163     {
164         if (! is_array($value)) {
165             return false;
166         }
167
168         if (! $value) {
169             return $allowEmpty;
170         }
171
172         return (array_values($value) !== $value);
173     }
174
175     /**
176      * Checks if a value exists in an array.
177      *
178      * Due to "foo" == 0 === TRUE with in_array when strict = false, an option
179      * has been added to prevent this. When $strict = 0/false, the most secure
180      * non-strict check is implemented. if $strict = -1, the default in_array
181      * non-strict behaviour is used.
182      *
183      * @param mixed $needle
184      * @param array $haystack
185      * @param int|bool $strict
186      * @return bool
187      */
188     public static function inArray($needle, array $haystack, $strict = false)
189     {
190         if (! $strict) {
191             if (is_int($needle) || is_float($needle)) {
192                 $needle = (string) $needle;
193             }
194             if (is_string($needle)) {
195                 foreach ($haystack as &$h) {
196                     if (is_int($h) || is_float($h)) {
197                         $h = (string) $h;
198                     }
199                 }
200             }
201         }
202         return in_array($needle, $haystack, $strict);
203     }
204
205     /**
206      * Convert an iterator to an array.
207      *
208      * Converts an iterator to an array. The $recursive flag, on by default,
209      * hints whether or not you want to do so recursively.
210      *
211      * @param  array|Traversable  $iterator     The array or Traversable object to convert
212      * @param  bool               $recursive    Recursively check all nested structures
213      * @throws Exception\InvalidArgumentException if $iterator is not an array or a Traversable object
214      * @return array
215      */
216     public static function iteratorToArray($iterator, $recursive = true)
217     {
218         if (! is_array($iterator) && ! $iterator instanceof Traversable) {
219             throw new Exception\InvalidArgumentException(__METHOD__ . ' expects an array or Traversable object');
220         }
221
222         if (! $recursive) {
223             if (is_array($iterator)) {
224                 return $iterator;
225             }
226
227             return iterator_to_array($iterator);
228         }
229
230         if (method_exists($iterator, 'toArray')) {
231             return $iterator->toArray();
232         }
233
234         $array = [];
235         foreach ($iterator as $key => $value) {
236             if (is_scalar($value)) {
237                 $array[$key] = $value;
238                 continue;
239             }
240
241             if ($value instanceof Traversable) {
242                 $array[$key] = static::iteratorToArray($value, $recursive);
243                 continue;
244             }
245
246             if (is_array($value)) {
247                 $array[$key] = static::iteratorToArray($value, $recursive);
248                 continue;
249             }
250
251             $array[$key] = $value;
252         }
253
254         return $array;
255     }
256
257     /**
258      * Merge two arrays together.
259      *
260      * If an integer key exists in both arrays and preserveNumericKeys is false, the value
261      * from the second array will be appended to the first array. If both values are arrays, they
262      * are merged together, else the value of the second array overwrites the one of the first array.
263      *
264      * @param  array $a
265      * @param  array $b
266      * @param  bool  $preserveNumericKeys
267      * @return array
268      */
269     public static function merge(array $a, array $b, $preserveNumericKeys = false)
270     {
271         foreach ($b as $key => $value) {
272             if ($value instanceof MergeReplaceKeyInterface) {
273                 $a[$key] = $value->getData();
274             } elseif (isset($a[$key]) || array_key_exists($key, $a)) {
275                 if ($value instanceof MergeRemoveKey) {
276                     unset($a[$key]);
277                 } elseif (! $preserveNumericKeys && is_int($key)) {
278                     $a[] = $value;
279                 } elseif (is_array($value) && is_array($a[$key])) {
280                     $a[$key] = static::merge($a[$key], $value, $preserveNumericKeys);
281                 } else {
282                     $a[$key] = $value;
283                 }
284             } else {
285                 if (! $value instanceof MergeRemoveKey) {
286                     $a[$key] = $value;
287                 }
288             }
289         }
290
291         return $a;
292     }
293
294     /**
295      * Compatibility Method for array_filter on <5.6 systems
296      *
297      * @param array $data
298      * @param callable $callback
299      * @param null|int $flag
300      * @return array
301      */
302     public static function filter(array $data, $callback, $flag = null)
303     {
304         if (! is_callable($callback)) {
305             throw new Exception\InvalidArgumentException(sprintf(
306                 'Second parameter of %s must be callable',
307                 __METHOD__
308             ));
309         }
310
311         if (version_compare(PHP_VERSION, '5.6.0') >= 0) {
312             return array_filter($data, $callback, $flag);
313         }
314
315         $output = [];
316         foreach ($data as $key => $value) {
317             $params = [$value];
318
319             if ($flag === static::ARRAY_FILTER_USE_BOTH) {
320                 $params[] = $key;
321             }
322
323             if ($flag === static::ARRAY_FILTER_USE_KEY) {
324                 $params = [$key];
325             }
326
327             $response = call_user_func_array($callback, $params);
328             if ($response) {
329                 $output[$key] = $value;
330             }
331         }
332
333         return $output;
334     }
335 }