Arr   F
last analyzed

Complexity

Total Complexity 75

Size/Duplication

Total Lines 542
Duplicated Lines 0 %

Test Coverage

Coverage 81.12%

Importance

Changes 0
Metric Value
eloc 125
c 0
b 0
f 0
dl 0
loc 542
ccs 116
cts 143
cp 0.8112
rs 2.4
wmc 75

24 Methods

Rating   Name   Duplication   Size   Complexity  
A accessible() 0 3 2
A getRequired() 0 6 3
A collapse() 0 15 4
A prepend() 0 9 2
A wrap() 0 3 2
A isAssoc() 0 5 1
B forget() 0 34 7
A only() 0 3 1
A flatten() 0 13 4
A dot() 0 13 4
A pluck() 0 23 6
A defaults() 0 5 2
A query() 0 3 1
A first() 0 19 6
A pull() 0 7 1
A exists() 0 7 2
A last() 0 7 3
A except() 0 5 1
A replaceKeys() 0 9 3
A where() 0 3 1
A set() 0 24 5
A get() 0 3 1
A groupBy() 0 3 1
C getData() 0 30 12

How to fix   Complexity   

Complex Class

Complex classes like Arr often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Arr, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @link http://www.newicon.net/neon
4
 * @copyright Copyright (c) 15/09/2016 Newicon Ltd
5
 * @license http://www.newicon.net/neon/license/
6
 */
7
8
namespace neon\core\helpers;
9
10
use neon\core\interfaces\IArrayable;
11
use yii\base\DynamicModel;
12
use yii\base\InvalidConfigException;
13
use \yii\helpers\ArrayHelper as YiiArrayHelper;
14
use \ArrayAccess;
15
16
class Arr extends YiiArrayHelper
17
{
18
	/**
19
	 * Determine whether the given value is array accessible.
20
	 *
21
	 * @param  mixed $value
22
	 * @return bool
23
	 */
24 72
	public static function accessible($value)
25
	{
26 72
		return is_array($value) || $value instanceof ArrayAccess;
27
	}
28
29
	/**
30
	 * Specify defaults for an array - if the keys don't exist in $array it 
31
	 * creates them with the corresponding value in the defaults array.
32
	 * The defaults array supports nesting by specifying dot notation keys
33
	 * 
34
	 * For eg:
35
	 * ```php
36
	 * $arr = [
37
	 *     'msg' => 'My message',
38
	 *     'other' => 'stuff',
39
	 * ];
40
	 * Arr::defaults($arr, [
41
	 *     'msg' => 'Default hi message',
42
	 *     'on' => false,
43
	 *     'some.nested' => 'default'
44
	 * ])
45
	 * // =>
46
	 * [
47
	 *     'msg' => 'My message',
48
	 *     'other' => 'stuff',
49
	 *     'on' => false,
50
	 *     'some' => [
51
	 *         'nested' => 'default'
52
	 *     ]
53
	 * ]
54
	 * ```
55
	 * @param array $array - the array to ensure the defaults exist in
56
	 */
57
	public static function defaults($array, $defaults)
58
	{
59
		foreach($defaults as $key => $default)
60
			Arr::set($array, $key, Arr::get($array, $key, $default));
61
		return $array;
62
	}
63
64
	/**
65
	 * Flatten a multi-dimensional associative array with dots.
66
	 *
67
	 * @param  array $array
68
	 * @param  string $prepend
69
	 * @return array
70
	 */
71 2
	public static function dot($array, $prepend = '')
72
	{
73 2
		$results = [];
74
75 2
		foreach ($array as $key => $value) {
76 2
			if (is_array($value) && !empty($value)) {
77 2
				$results = array_merge($results, static::dot($value, $prepend . $key . '.'));
78
			} else {
79 2
				$results[$prepend . $key] = $value;
80
			}
81
		}
82
83 2
		return $results;
84
	}
85
86
	/**
87
	 * Get all of the given array except for a specified array of items.
88
	 *
89
	 * @param  array $array
90
	 * @param  array|string $keys
91
	 * @return array
92
	 */
93 4
	public static function except($array, $keys)
94
	{
95 4
		Arr::forget($array, $keys);
96
97 4
		return $array;
98
	}
99
100
	/**
101
	 * Determine if the given key exists in the provided array.
102
	 *
103
	 * @param  \ArrayAccess|array $array
104
	 * @param  string|int $key
105
	 * @return bool
106
	 */
107 72
	public static function exists($array, $key)
108
	{
109 72
		if ($array instanceof ArrayAccess) {
110 2
			return $array->offsetExists($key);
111
		}
112
113 70
		return array_key_exists($key, $array);
114
	}
115
116
	/**
117
	 * Return the first element in an array passing a given truth test.
118
	 *
119
	 * ```php
120
	 * $array = [100, 200, 300];
121
	 * $value = Arr::first($array, function ($value, $key) {
122
	 *     return $value >= 150;
123
	 * });
124
	 * // returns: 200
125
	 * ```
126
	 *
127
	 * @param  array $array
128
	 * @param  callable|null $callback Where the first paramter
129
	 *         is an array items value and second parameter its key.
130
	 * @param  mixed $default
131
	 * @return mixed
132
	 */
133 46
	public static function first($array, callable $callback = null, $default = null)
134
	{
135 46
		if (is_null($callback)) {
136 12
			if (empty($array)) {
137 4
				return value($default);
138
			}
139
140 10
			foreach ($array as $item) {
141 10
				return $item;
142
			}
143
		}
144
145 36
		foreach ($array as $key => $value) {
146 24
			if (call_user_func($callback, $value, $key)) {
0 ignored issues
show
Bug introduced by
It seems like $callback can also be of type null; however, parameter $callback of call_user_func() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

146
			if (call_user_func(/** @scrutinizer ignore-type */ $callback, $value, $key)) {
Loading history...
147 20
				return $value;
148
			}
149
		}
150
151 26
		return value($default);
152
	}
153
154
	/**
155
	 * Return the last element in an array passing a given truth test.
156
	 *
157
	 * @param  array $array
158
	 * @param  callable|null $callback
159
	 * @param  mixed $default
160
	 * @return mixed
161
	 */
162 16
	public static function last($array, callable $callback = null, $default = null)
163
	{
164 16
		if (is_null($callback)) {
165 10
			return empty($array) ? value($default) : end($array);
166
		}
167
168 6
		return static::first(array_reverse($array, true), $callback, $default);
169
	}
170
171
	/**
172
	 * Flatten a multi-dimensional array into a single level.
173
	 *
174
	 * @param  array $array
175
	 * @param  int $depth
176
	 * @return array
177
	 */
178
	public static function flatten($array, $depth = INF)
179
	{
180 8
		return array_reduce($array, function ($result, $item) use ($depth) {
181 8
			$item = $item instanceof Collection ? $item->all() : $item;
182
183 8
			if (!is_array($item)) {
184 8
				return array_merge($result, [$item]);
185 8
			} elseif ($depth === 1) {
186 4
				return array_merge($result, array_values($item));
187
			} else {
188 8
				return array_merge($result, static::flatten($item, $depth - 1));
0 ignored issues
show
Bug introduced by
$depth - 1 of type double is incompatible with the type integer expected by parameter $depth of neon\core\helpers\Arr::flatten(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

188
				return array_merge($result, static::flatten($item, /** @scrutinizer ignore-type */ $depth - 1));
Loading history...
189
			}
190 8
		}, []);
191
	}
192
193
	/**
194
	 * Remove one or many array items from a given array using "dot" notation.
195
	 *
196
	 * @param  array $array
197
	 * @param  array|string $keys
198
	 * @return void
199
	 */
200 12
	public static function forget(&$array, $keys)
201
	{
202 12
		$original = &$array;
203
204 12
		$keys = (array)$keys;
205
206 12
		if (count($keys) === 0) {
207
			return;
208
		}
209
210 12
		foreach ($keys as $key) {
211
			// if the exact key exists in the top-level, remove it
212 12
			if (static::exists($array, $key)) {
213 10
				unset($array[$key]);
214
215 10
				continue;
216
			}
217
218 8
			$parts = explode('.', $key);
219
220
			// clean up before each pass
221 8
			$array = &$original;
222
223 8
			while (count($parts) > 1) {
224 4
				$part = array_shift($parts);
225
226 4
				if (isset($array[$part]) && is_array($array[$part])) {
227 4
					$array = &$array[$part];
228
				} else {
229
					continue 2;
230
				}
231
			}
232
233 8
			unset($array[array_shift($parts)]);
234
		}
235 12
	}
236
237
	/**
238
	 * Push an item onto the beginning of an array.
239
	 *
240
	 * @param  array $array
241
	 * @param  mixed $value
242
	 * @param  mixed $key
243
	 * @return array
244
	 */
245 4
	public static function prepend($array, $value, $key = null)
246
	{
247 4
		if (is_null($key)) {
248 4
			array_unshift($array, $value);
249
		} else {
250 2
			$array = [$key => $value] + $array;
251
		}
252
253 4
		return $array;
254
	}
255
256
	/**
257
	 * Get a value from the array, and remove it.
258
	 *
259
	 * @param  array $array
260
	 * @param  string $key can use dot notation
261
	 * @param  mixed $default
262
	 * @return mixed
263
	 */
264 8
	public static function pull(&$array, $key, $default = null)
265
	{
266 8
		$value = static::get($array, $key, $default);
267
268 8
		static::forget($array, $key);
269
270 8
		return $value;
271
	}
272
273
	/**
274
	 * Get an item from an array using "dot" or array or string notation.
275
	 *
276
	 * @param  \ArrayAccess|array $array
277
	 * @param  string|\Closure|array $key
278
	 * @param  mixed $default
279
	 * @return mixed
280
	 */
281 56
	public static function get($array, $key, $default = null)
282
	{
283 56
		return self::getValue($array, $key, value($default));
284
	}
285
286
	/**
287
	 * Get property from an array - throws an exception if it does not exist
288
	 *
289
	 * @param \ArrayAccess|array $array
290
	 * @param string $key - key in the array
291
	 * @param string message - the error message to use if the key does not exist
0 ignored issues
show
Bug introduced by
The type neon\core\helpers\message was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
292
	 * @throws \InvalidArgumentException if the property $key does not exist in the array
293
	 * @return mixed
294
	 */
295 2
	public static function getRequired($array, $key, $message=null)
296
	{
297 2
		if (!array_key_exists($key, $array)) {
298 2
			throw new \InvalidArgumentException($message ?: "You must define a '$key' property.");
299
		}
300
		return static::get($array, $key);
301
	}
302
303
	/**
304
	 * Get an item from an array or object using "dot" notation.
305
	 * It also works with Collection objects and allows for limited wildcard useage in dot notation
306
	 * for e.g. 'my.deep.nested.*';
307
	 *
308
	 * @param  mixed $target
309
	 * @param  string|array $key
310
	 * @param  mixed $default
311
	 * @return mixed
312
	 */
313 72
	public static function getData($target, $key, $default = null)
314
	{
315 72
		if (is_null($key)) {
0 ignored issues
show
introduced by
The condition is_null($key) is always false.
Loading history...
316
			return $target;
317
		}
318
319 72
		$key = is_array($key) ? $key : explode('.', $key);
320
321 72
		while (!is_null($segment = array_shift($key))) {
322 72
			if ($segment === '*') {
323
				if ($target instanceof Collection) {
324
					$target = $target->all();
325
				} elseif (!is_array($target)) {
326
					return value($default);
327
				}
328
329
				$result = Arr::pluck($target, $key);
330
331
				return in_array('*', $key) ? Arr::collapse($result) : $result;
332
			}
333
334 72
			if (Arr::accessible($target) && Arr::exists($target, $segment)) {
335 60
				$target = $target[$segment];
336 22
			} elseif (is_object($target) && isset($target->{$segment})) {
337 22
				$target = $target->{$segment};
338
			} else {
339
				return value($default);
340
			}
341
		}
342 72
		return $target;
343
	}
344
345
	/**
346
	 * Set an array item to a given value using "dot" notation.
347
	 *
348
	 * If no key is given to the method, the entire array will be replaced.
349
	 *
350
	 * @param  array $array
351
	 * @param  string $key
352
	 * @param  mixed $value
353
	 * @return array
354
	 */
355 2
	public static function set(&$array, $key, $value)
356
	{
357 2
		if (is_null($key)) {
0 ignored issues
show
introduced by
The condition is_null($key) is always false.
Loading history...
358
			return $array = $value;
359
		}
360
361 2
		$keys = explode('.', $key);
362
363 2
		while (count($keys) > 1) {
364 2
			$key = array_shift($keys);
365
366
			// If the key doesn't exist at this depth, we will just create an empty array
367
			// to hold the next value, allowing us to create the arrays to hold final
368
			// values at the correct depth. Then we'll keep digging into the array.
369 2
			if (!isset($array[$key]) || !is_array($array[$key])) {
370 2
				$array[$key] = [];
371
			}
372
373 2
			$array = &$array[$key];
374
		}
375
376 2
		$array[array_shift($keys)] = $value;
377
378 2
		return $array;
379
	}
380
381
	/**
382
	 * Filter the array using the given callback.
383
	 *
384
	 * ```php
385
	 * $array = [100, '200', 300, '400', 500];
386
	 *
387
	 * $array = Arr::where($array, function ($value, $key) {
388
	 *     return is_string($value);
389
	 * });
390
	 *
391
	 * // [1 => 200, 3 => 400]
392
	 * ```
393
	 *
394
	 * @param  array $array
395
	 * @param  callable $callback the callable format should be function ($value, $key) {}
396
	 *
397
	 * @return array
398
	 */
399 44
	public static function where($array, callable $callback)
400
	{
401 44
		return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
402
	}
403
404
	/**
405
	 * Group an associative array by a field or using a callback.
406
	 *
407
	 * @param  callable|string $groupBy
408
	 * @param  bool $preserveKeys
409
	 * @return array
410
	 */
411
	public static function groupBy($array, $groupBy, $preserveKeys = false)
412
	{
413
		return (new Collection($array))->groupBy($groupBy, $preserveKeys)->toArray();
414
	}
415
416
	/**
417
	 * Determines if an array is associative.
418
	 *
419
	 * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
420
	 *
421
	 * @param  array $array
422
	 * @return bool
423
	 */
424 8
	public static function isAssoc(array $array)
425
	{
426 8
		$keys = array_keys($array);
427
428 8
		return array_keys($keys) !== $keys;
429
	}
430
431
	/**
432
	 * Get a subset of the items from the given array.
433
	 *
434
	 * @param  array $array
435
	 * @param  array|string $keys
436
	 * @return array
437
	 */
438 12
	public static function only($array, $keys)
439
	{
440 12
		return array_intersect_key($array, array_flip((array)$keys));
441
	}
442
443
	/**
444
	 * Pluck an array of values from an array.
445
	 * Inspired by Laravels Pluck method
446
	 *
447
	 * example usage:
448
	 *
449
	 * ```php
450
	 * $array = [
451
	 *     ['user' => ['id' => 1, 'name' => 'Steve']],
452
	 *     ['user' => ['id' => 2, 'name' => 'Bob']]
453
	 * ]
454
	 *
455
	 * Arr::pluck($array, 'user.name')
456
	 *
457
	 * // Returns: ['Steve', 'Bob'];
458
	 * ```
459
	 *
460
	 * You may also specify how you wish the resulting list to be keyed:
461
	 *
462
	 * ```php
463
	 * $array = Arr::pluck($array, 'user.name', 'user.id');
464
	 *
465
	 * // Returns: [1 => 'Steve', 2 => 'Bob'];
466
	 * ```
467
	 *
468
	 * @param  array $array
469
	 * @param  string|array $value
470
	 * @param  string|array|null $key
471
	 * @return array
472
	 */
473 60
	public static function pluck($array, $value, $key = null)
474
	{
475 60
		$results = [];
476
477 60
		$value = is_string($value) ? explode('.', $value) : $value;
478 60
		$key = is_null($key) || is_array($key) ? $key : explode('.', $key);
479
480 60
		foreach ($array as $item) {
481 44
			$itemValue = Arr::getData($item, $value);
482
483
			// If the key is "null", we will just append the value to the array and keep
484
			// looping. Otherwise we will key the array using the value of the key we
485
			// received from the developer. Then we'll return the final array form.
486 44
			if (is_null($key)) {
487 44
				$results[] = $itemValue;
488
			} else {
489 6
				$itemKey = Arr::getData($item, $key);
490
491 6
				$results[$itemKey] = $itemValue;
492
			}
493
		}
494
495 60
		return $results;
496
	}
497
498
	/**
499
	 * If the given value is not an array, wrap it in one.
500
	 *
501
	 * @param  mixed $value
502
	 * @return array
503
	 */
504 2
	public static function wrap($value)
505
	{
506 2
		return !is_array($value) ? [$value] : $value;
507
	}
508
509
	/**
510
	 * Convert the array into a query string.
511
	 *
512
	 * @param  array  $array
513
	 * @return string
514
	 */
515
	public static function query($array)
516
	{
517
		return http_build_query($array, null, '&', PHP_QUERY_RFC3986);
0 ignored issues
show
Bug introduced by
null of type null is incompatible with the type string expected by parameter $numeric_prefix of http_build_query(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

517
		return http_build_query($array, /** @scrutinizer ignore-type */ null, '&', PHP_QUERY_RFC3986);
Loading history...
518
	}
519
520
	/**
521
	 * Collapse an array of arrays into a single array.
522
	 *
523
	 * @param  array $array
524
	 * @return array
525
	 */
526 8
	public static function collapse($array)
527
	{
528 8
		$results = [];
529
530 8
		foreach ($array as $values) {
531 8
			if ($values instanceof Collection) {
532 2
				$values = $values->all();
533 6
			} elseif (!is_array($values)) {
534
				continue;
535
			}
536
537 8
			$results = array_merge($results, $values);
538
		}
539
540 8
		return $results;
541
	}
542
543
	/**
544
	 * Replace keys in an array with a differnt name
545
	 * @param array $array
546
	 * @param $replace ['findKey' => 'replacekey']
0 ignored issues
show
Documentation Bug introduced by
The doc comment ['findKey' at position 0 could not be parsed: Unknown type name '[' at position 0 in ['findKey'.
Loading history...
547
	 * @return array
548
	 */
549
	public static function replaceKeys($array, $replace)
550
	{
551
		foreach ($replace as $find => $rep) {
552
			if (isset($array[$find])) {
553
				$array[$rep] = $array[$find];
554
				unset($array[$find]);
555
			}
556
		}
557
		return $array;
558
	}
559
}
560