Completed
Push — master ( 8d39d6...58e1d9 )
by Xeriab
02:50
created

Arr::pluck()   B

Complexity

Conditions 13
Paths 50

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 20
rs 7.3977
cc 13
eloc 13
nc 50
nop 3

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Konfig
5
 *
6
 * Yet another simple configuration loader library.
7
 *
8
 * @author  Xeriab Nabil (aka KodeBurner) <[email protected]>
9
 * @license https://raw.github.com/xeriab/konfig/master/LICENSE MIT
10
 * @link    https://xeriab.github.io/projects/konfig
11
 */
12
13
namespace Exen\Konfig;
14
15
use ArrayAccess;
16
use InvalidArgumentException;
17
18
use Exen\Konfig\Utils;
19
20
final class Arr
21
{
22
    /**
23
     * Gets a dot-notated key from an array, with a default value if it does
24
     * not exist.
25
     *
26
     * @param array $array The search array
27
     * @param mixed $key The dot-notated key or array of keys
28
     * @param string $default The default value
29
     * @return mixed
30
     * @codeCoverageIgnore
31
     * @since 0.1.0
32
     */
33
    public static function get(array $array, $key, $default = null)
34
    {
35
        if (!is_array($array) && !$array instanceof ArrayAccess) {
36
            throw new InvalidArgumentException('First parameter must be an array or ArrayAccess object.');
37
        }
38
39
        if (is_null($key)) {
40
            return $array;
41
        }
42
43
        if (is_array($key)) {
44
            $return = [];
45
46
            foreach ($key as $k) {
47
                $return[$k] = self::get($array, $k, $default);
48
            }
49
50
            return $return;
51
        }
52
53
        foreach (explode('.', $key) as $key_part) {
54
            if (($array instanceof ArrayAccess && isset($array[$key_part])) === false) {
55
                if (!is_array($array) || !array_key_exists($key_part, $array)) {
56
                    return Utils::checkValue($default);
57
                }
58
            }
59
60
            $array = $array[$key_part];
61
        }
62
63
        return $array;
64
    }
65
66
    /**
67
     * Set an array item (dot-notated) to the value.
68
     *
69
     * @param array $array The array to insert it into
70
     * @param mixed $key The dot-notated key to set or array of keys
71
     * @param mixed $value The value
72
     * @return void
73
     * @codeCoverageIgnore
74
     * @since 0.1.0
75
     */
76
    public static function set(array &$array, $key, $value = null)
77
    {
78
        if (is_null($key)) {
79
            $array = $value;
80
            return;
81
        }
82
83
        if (is_array($key)) {
84
            foreach ($key as $k => $v) {
85
                self::set($array, $k, $v);
86
            }
87
        } else {
88
            $keys = explode('.', $key);
89
90
            while (count($keys) > 1) {
91
                $key = array_shift($keys);
92
93
                if (!isset($array[$key]) || ! is_array($array[$key])) {
94
                    $array[$key] = [];
95
                }
96
97
                $array = &$array[$key];
98
            }
99
100
            $array[array_shift($keys)] = $value;
101
        }
102
    }
103
104
    /**
105
     * Merge two arrays recursively, differs in two important ways from array_merge_recursive()
106
     * - When there's two different values and not both arrays, the latter value overwrites the earlier
107
     *   instead of merging both into an array
108
     * - Numeric keys that don't conflict aren't changed, only when a numeric key already exists is the
109
     *   value added using array_push()
110
     *
111
     * @param array multiple variables all of which must be arrays
112
     * @throws InvalidArgumentException
113
     * @return array
114
     * @codeCoverageIgnore
115
     * @since 0.1.0
116
     */
117
    public static function merge()
118
    {
119
        $array = func_get_arg(0);
120
        $arrays = array_slice(func_get_args(), 1);
121
122
        if (!is_array($array)) {
123
            throw new InvalidArgumentException('Exen\Konfig\Arr::merge() - all arguments must be arrays.');
124
        }
125
126
        foreach ($arrays as $arr) {
127
            if (!is_array($arr)) {
128
                throw new InvalidArgumentException('Exen\Konfig\Arr::merge() - all arguments must be arrays.');
129
            }
130
131
            foreach ($arr as $key => $value) {
132
                // Numeric keys are appended
133
                if (is_int($key)) {
134
                    array_key_exists($key, $array) ? array_push($array, $value) : $array[$key] = $value;
135
                } elseif (is_array($value) && array_key_exists($key, $array) && is_array($array[$key])) {
136
                    $array[$key] = self::merge($array[$key], $value);
137
                } else {
138
                    $array[$key] = $value;
139
                }
140
            }
141
        }
142
143
        return $array;
144
    }
145
    
146
    /**
147
     * Merge 2 arrays recursively, differs in 2 important ways from array_merge_recursive()
148
     * - When there's 2 different values and not both arrays, the latter value overwrites the earlier
149
     *   instead of merging both into an array
150
     * - Numeric keys are never changed
151
     *
152
     * @param array multiple variables all of which must be arrays
153
     * @throws InvalidArgumentException
154
     * @return array
155
     * @codeCoverageIgnore
156
     * @since 0.1.0
157
     */
158
    public static function mergeAssoc()
159
    {
160
        $array = func_get_arg(0);
161
        $arrays = array_slice(func_get_args(), 1);
162
163
        if (!is_array($array)) {
164
            throw new InvalidArgumentException('Exen\Konfig\Arr::mergeAssoc() - all arguments must be arrays.');
165
        }
166
167
        foreach ($arrays as $arr) {
168
            if (!is_array($arr)) {
169
                throw new InvalidArgumentException('Exen\Konfig\Arr::mergeAssoc() - all arguments must be arrays.');
170
            }
171
172
            foreach ($arr as $key => $value) {
173
                if (is_array($value) && array_key_exists($key, $array) && is_array($array[$key])) {
174
                    $array[$key] = self::mergeAssoc($array[$key], $value);
175
                } else {
176
                    $array[$key] = $value;
177
                }
178
            }
179
        }
180
181
        return $array;
182
    }
183
184
    /**
185
     * Un-sets dot-notated key from an array
186
     *
187
     * @param array $array The search array
188
     * @param mixed $key The dot-notated key or array of keys
189
     * @return mixed
190
     * @codeCoverageIgnore
191
     * @since 0.1.0
192
     */
193
    public static function delete(array &$array, $key)
194
    {
195
        if (is_null($key)) {
196
            return false;
197
        }
198
199
        if (is_array($key)) {
200
            $return = [];
201
202
            foreach ($key as $k) {
203
                $return[$k] = self::delete($array, $k);
204
            }
205
206
            return $return;
207
        }
208
209
        $key_parts = explode('.', $key);
210
211
        if (!is_array($array) || ! array_key_exists($key_parts[0], $array)) {
212
            return false;
213
        }
214
215
        $this_key = array_shift($key_parts);
216
217
        if (!empty($key_parts)) {
218
            $key = implode('.', $key_parts);
219
            return self::delete($array[$this_key], $key);
220
        } else {
221
            unset($array[$this_key]);
222
        }
223
224
        return true;
225
    }
226
227
    /**
228
     * Get array keys recursively
229
     *
230
     * @param array $array The search array
231
     * @param int $maxDepth The search maximum depth
232
     * @param int $depth The search depth
233
     * @param array $arraykeys The array keys
234
     * @return array
235
     * @codeCoverageIgnore
236
     * @since 0.1.0
237
     */
238
    public static function keys(array $array, $maxDepth = INF, $depth = 0, array $arraykeys = [])
239
    {
240
        if ($depth < $maxDepth) {
241
            $depth++;
242
            $keys = array_keys($array);
243
244
            foreach ($keys as $key) {
245
                if (is_array($array[$key])) {
246
                    $arraykeys[$key] = self::keys($array[$key], $maxDepth, $depth);
247
                }
248
            }
249
        }
250
251
        return $arraykeys;
252
    }
253
    
254
    /**
255
     * Get array keys recursively
256
     *
257
     * @param array $array The search array
258
     * @param string $search The search value
259
     * @return array
260
     * @codeCoverageIgnore
261
     * @since 0.1.2
262
     */
263
    public static function recursiveKeys(array $array, $search = null)
264
    {
265
        $return = (
266
            $search !== null ?
267
            array_keys($array, $search) :
268
            array_keys($array)
269
        );
270
        
271
        foreach ($array as $sub) {
272
            if (is_array($sub)) {
273
                $return = (
274
                    $search !== null ?
275
                    self::merge($return, self::recursiveKeys($sub, $search)) :
276
                    self::merge($return, self::recursiveKeys($sub))
277
                );
278
            }
279
        }
280
        
281
        return $return;
282
    }
283
284
    /**
285
     * Pluck an array of values from an array.
286
     *
287
     * @param  array   $array  collection of arrays to pluck from
288
     * @param  string  $key    key of the value to pluck
289
     * @param  string  $index  optional return array index key, true for original index
290
     * @return array   array of plucked values
291
     * @codeCoverageIgnore
292
     * @since 0.2.4
293
     */
294
    public static function pluck($array, $key, $index = null)
295
    {
296
        $return = [];
297
        $get_deep = strpos($key, '.') !== false;
298
299
        if (!$index) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $index of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
300
            foreach ($array as $i => $a) {
301
                $return[] = (is_object($a) && ! ($a instanceof ArrayAccess)) ? $a->{$key} :
302
                    ($get_deep ? self::get($a, $key) : $a[$key]);
303
            }
304
        } else {
305
            foreach ($array as $i => $a) {
306
                $index !== true && $i = (is_object($a) && ! ($a instanceof ArrayAccess)) ? $a->{$index} : $a[$index];
307
                $return[$i] = (is_object($a) && ! ($a instanceof ArrayAccess)) ? $a->{$key} :
308
                    ($get_deep ? self::get($a, $key) : $a[$key]);
309
            }
310
        }
311
312
        return $return;
313
    }
314
315
    /**
316
     * Sorts a multi-dimensional array by it's values.
317
     *
318
     * @access  public
319
     * @param   array   The array to fetch from
320
     * @param   string  The key to sort by
321
     * @param   string  The order (asc or desc)
322
     * @param   int     The php sort type flag
323
     * @return  array
324
     * @codeCoverageIgnore
325
     * @since 0.2.4
326
     */
327
    public static function sort(array $array, $key, $order = 'asc', $sort_flags = SORT_REGULAR)
328
    {
329
        if (!is_array($array)) {
330
            throw new InvalidArgumentException('Exen\Konfig\Arr::sort() - $array must be an array.');
331
        }
332
333
        if (empty($array)) {
334
            return $array;
335
        }
336
337
        $b = [];
338
        $c = [];
339
340
        foreach ($array as $k => $v) {
341
            $b[$k] = self::get($v, $key);
342
        }
343
344
        switch ($order) {
345
            case 'asc':
346
                asort($b, $sort_flags);
347
                break;
348
349
            case 'desc':
350
                arsort($b, $sort_flags);
351
                break;
352
353
            default:
354
                throw new InvalidArgumentException('Exen\Konfig\Arr::sort() - $order must be asc or desc.');
355
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
356
        }
357
358
        foreach ($b as $key => $val) {
359
            $c[] = $array[$key];
360
        }
361
362
        return $c;
363
    }
364
365
    /**
366
     * Sorts an array on multitiple values, with deep sorting support.
367
     *
368
     * @param   array  $array        collection of arrays/objects to sort
369
     * @param   array  $conditions   sorting conditions
370
     * @param   bool   @ignore_case  wether to sort case insensitive
371
     */
372
    public static function multiSort(array $array, $conditions, $ignore_case = false)
373
    {
374
        $temp = [];
375
        $keys = array_keys($conditions);
376
377
        foreach ($keys as $key) {
378
            $temp[$key] = self::pluck($array, $key, true);
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
379
            is_array($conditions[$key]) || $conditions[$key] = array($conditions[$key]);
380
        }
381
382
        $args = [];
383
384
        foreach ($keys as $key) {
385
            $args[] = $ignore_case ? array_map('strtolower', $temp[$key]) : $temp[$key];
386
            foreach ($conditions[$key] as $flag) {
387
                $args[] = $flag;
388
            }
389
        }
390
391
        $args[] = &$array;
392
393
        Utils::callFuncArray('array_multisort', $args);
394
395
        return $array;
396
    }
397
398
    /**
399
     * @codeCoverageIgnore
400
     * @return string
401
     * @since 0.1.2
402
     */
403
    public function __toString()
404
    {
405
        return 'Exen\Konfig\Arr' . PHP_EOL;
406
    }
407
}
408
409
// END OF ./src/Arr.php FILE
410