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