GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 2ab419...940f7c )
by Robert
32:50
created

BaseArrayHelper::removeValue()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 11
cts 11
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 2
nop 2
crap 4
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\helpers;
9
10
use Yii;
11
use yii\base\Arrayable;
12
use yii\base\InvalidParamException;
13
14
/**
15
 * BaseArrayHelper provides concrete implementation for [[ArrayHelper]].
16
 *
17
 * Do not use BaseArrayHelper. Use [[ArrayHelper]] instead.
18
 *
19
 * @author Qiang Xue <[email protected]>
20
 * @since 2.0
21
 */
22
class BaseArrayHelper
23
{
24
    /**
25
     * Converts an object or an array of objects into an array.
26
     * @param object|array|string $object the object to be converted into an array
27
     * @param array $properties a mapping from object class names to the properties that need to put into the resulting arrays.
28
     * The properties specified for each class is an array of the following format:
29
     *
30
     * ```php
31
     * [
32
     *     'app\models\Post' => [
33
     *         'id',
34
     *         'title',
35
     *         // the key name in array result => property name
36
     *         'createTime' => 'created_at',
37
     *         // the key name in array result => anonymous function
38
     *         'length' => function ($post) {
39
     *             return strlen($post->content);
40
     *         },
41
     *     ],
42
     * ]
43
     * ```
44
     *
45
     * The result of `ArrayHelper::toArray($post, $properties)` could be like the following:
46
     *
47
     * ```php
48
     * [
49
     *     'id' => 123,
50
     *     'title' => 'test',
51
     *     'createTime' => '2013-01-01 12:00AM',
52
     *     'length' => 301,
53
     * ]
54
     * ```
55
     *
56
     * @param boolean $recursive whether to recursively converts properties which are objects into arrays.
57
     * @return array the array representation of the object
58
     */
59 11
    public static function toArray($object, $properties = [], $recursive = true)
60
    {
61 11
        if (is_array($object)) {
62 11
            if ($recursive) {
63 11
                foreach ($object as $key => $value) {
64 11
                    if (is_array($value) || is_object($value)) {
65 1
                        $object[$key] = static::toArray($value, $properties, true);
66 1
                    }
67 11
                }
68 11
            }
69
70 11
            return $object;
71 1
        } elseif (is_object($object)) {
72 1
            if (!empty($properties)) {
73 1
                $className = get_class($object);
74 1
                if (!empty($properties[$className])) {
75 1
                    $result = [];
76 1
                    foreach ($properties[$className] as $key => $name) {
77 1
                        if (is_int($key)) {
78 1
                            $result[$name] = $object->$name;
79 1
                        } else {
80 1
                            $result[$key] = static::getValue($object, $name);
81
                        }
82 1
                    }
83
84 1
                    return $recursive ? static::toArray($result, $properties) : $result;
85
                }
86 1
            }
87 1
            if ($object instanceof Arrayable) {
88
                $result = $object->toArray([], [], $recursive);
89
            } else {
90 1
                $result = [];
91 1
                foreach ($object as $key => $value) {
92 1
                    $result[$key] = $value;
93 1
                }
94
            }
95
96 1
            return $recursive ? static::toArray($result, $properties) : $result;
97
        } else {
98
            return [$object];
99
        }
100
    }
101
102
    /**
103
     * Merges two or more arrays into one recursively.
104
     * If each array has an element with the same string key value, the latter
105
     * will overwrite the former (different from array_merge_recursive).
106
     * Recursive merging will be conducted if both arrays have an element of array
107
     * type and are having the same key.
108
     * For integer-keyed elements, the elements from the latter array will
109
     * be appended to the former array.
110
     * You can use [[UnsetArrayValue]] object to unset value from previous array or
111
     * [[ReplaceArrayValue]] to force replace former value instead of recursive merging.
112
     * @param array $a array to be merged to
113
     * @param array $b array to be merged from. You can specify additional
114
     * arrays via third argument, fourth argument etc.
115
     * @return array the merged array (the original arrays are not changed.)
116
     */
117 2147
    public static function merge($a, $b)
0 ignored issues
show
Unused Code introduced by
The parameter $a is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $b is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
118
    {
119 2147
        $args = func_get_args();
120 2147
        $res = array_shift($args);
121 2147
        while (!empty($args)) {
122 2147
            $next = array_shift($args);
123 2147
            foreach ($next as $k => $v) {
124 574
                if ($v instanceof UnsetArrayValue) {
125 1
                    unset($res[$k]);
126 574
                } elseif ($v instanceof ReplaceArrayValue) {
127 1
                    $res[$k] = $v->value;
128 574
                } elseif (is_int($k)) {
129 3
                    if (isset($res[$k])) {
130 3
                        $res[] = $v;
131 3
                    } else {
132
                        $res[$k] = $v;
133
                    }
134 574
                } elseif (is_array($v) && isset($res[$k]) && is_array($res[$k])) {
135 47
                    $res[$k] = self::merge($res[$k], $v);
136 47
                } else {
137 574
                    $res[$k] = $v;
138
                }
139 2147
            }
140 2147
        }
141
142 2147
        return $res;
143
    }
144
145
    /**
146
     * Retrieves the value of an array element or object property with the given key or property name.
147
     * If the key does not exist in the array or object, the default value will be returned instead.
148
     *
149
     * The key may be specified in a dot format to retrieve the value of a sub-array or the property
150
     * of an embedded object. In particular, if the key is `x.y.z`, then the returned value would
151
     * be `$array['x']['y']['z']` or `$array->x->y->z` (if `$array` is an object). If `$array['x']`
152
     * or `$array->x` is neither an array nor an object, the default value will be returned.
153
     * Note that if the array already has an element `x.y.z`, then its value will be returned
154
     * instead of going through the sub-arrays. So it is better to be done specifying an array of key names
155
     * like `['x', 'y', 'z']`.
156
     *
157
     * Below are some usage examples,
158
     *
159
     * ```php
160
     * // working with array
161
     * $username = \yii\helpers\ArrayHelper::getValue($_POST, 'username');
162
     * // working with object
163
     * $username = \yii\helpers\ArrayHelper::getValue($user, 'username');
164
     * // working with anonymous function
165
     * $fullName = \yii\helpers\ArrayHelper::getValue($user, function ($user, $defaultValue) {
166
     *     return $user->firstName . ' ' . $user->lastName;
167
     * });
168
     * // using dot format to retrieve the property of embedded object
169
     * $street = \yii\helpers\ArrayHelper::getValue($users, 'address.street');
170
     * // using an array of keys to retrieve the value
171
     * $value = \yii\helpers\ArrayHelper::getValue($versions, ['1.0', 'date']);
172
     * ```
173
     *
174
     * @param array|object $array array or object to extract value from
175
     * @param string|\Closure|array $key key name of the array element, an array of keys or property name of the object,
176
     * or an anonymous function returning the value. The anonymous function signature should be:
177
     * `function($array, $defaultValue)`.
178
     * The possibility to pass an array of keys is available since version 2.0.4.
179
     * @param mixed $default the default value to be returned if the specified array key does not exist. Not used when
180
     * getting value from an object.
181
     * @return mixed the value of the element if found, default value otherwise
182
     */
183 71
    public static function getValue($array, $key, $default = null)
184
    {
185 71
        if ($key instanceof \Closure) {
186 5
            return $key($array, $default);
187
        }
188
189 70
        if (is_array($key)) {
190 2
            $lastKey = array_pop($key);
191 2
            foreach ($key as $keyPart) {
192 2
                $array = static::getValue($array, $keyPart);
193 2
            }
194 2
            $key = $lastKey;
195 2
        }
196
197 70
        if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array)) ) {
198 51
            return $array[$key];
199
        }
200
201 30
        if (($pos = strrpos($key, '.')) !== false) {
202 10
            $array = static::getValue($array, substr($key, 0, $pos), $default);
203 10
            $key = substr($key, $pos + 1);
204 10
        }
205
206 30
        if (is_object($array)) {
207
            // this is expected to fail if the property does not exist, or __get() is not implemented
208
            // it is not reliably possible to check whether a property is accessible beforehand
209 8
            return $array->$key;
210 23
        } elseif (is_array($array)) {
211 23
            return (isset($array[$key]) || array_key_exists($key, $array)) ? $array[$key] : $default;
212
        } else {
213 3
            return $default;
214
        }
215
    }
216
217
    /**
218
     * Removes an item from an array and returns the value. If the key does not exist in the array, the default value
219
     * will be returned instead.
220
     *
221
     * Usage examples,
222
     *
223
     * ```php
224
     * // $array = ['type' => 'A', 'options' => [1, 2]];
225
     * // working with array
226
     * $type = \yii\helpers\ArrayHelper::remove($array, 'type');
227
     * // $array content
228
     * // $array = ['options' => [1, 2]];
229
     * ```
230
     *
231
     * @param array $array the array to extract value from
232
     * @param string $key key name of the array element
233
     * @param mixed $default the default value to be returned if the specified key does not exist
234
     * @return mixed|null the value of the element if found, default value otherwise
235
     */
236 92
    public static function remove(&$array, $key, $default = null)
237
    {
238 92
        if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) {
239 26
            $value = $array[$key];
240 26
            unset($array[$key]);
241
242 26
            return $value;
243
        }
244
245 90
        return $default;
246
    }
247
248
    /**
249
     * Removes items with matching values from the array and returns the removed items.
250
     *
251
     * Example,
252
     *
253
     * ```php
254
     * $array = ['Bob' => 'Dylan', 'Michael' => 'Jackson', 'Mick' => 'Jagger', 'Janet' => 'Jackson'];
255
     * $removed = \yii\helpers\ArrayHelper::removeValue($array, 'Jackson');
256
     * // result:
257
     * // $array = ['Bob' => 'Dylan', 'Mick' => 'Jagger'];
258
     * // $removed = ['Michael' => 'Jackson', 'Janet' => 'Jackson'];
259
     * ```
260
     *
261
     * @param array $array the array where to look the value from
262
     * @param string $value the value to remove from the array
263
     * @return array the items that were removed from the array
264
     * @since 2.0.11
265
     */
266 2
    public static function removeValue(&$array, $value)
267
    {
268 2
        $result = [];
269 2
        if (is_array($array)) {
270 2
            foreach ($array as $key => $val) {
271 2
                if ($val === $value) {
272 1
                    $result[$key] = $val;
273 1
                    unset($array[$key]);
274 1
                }
275 2
            }
276 2
        }
277 2
        return $result;
278
    }
279
280
    /**
281
     * Indexes and/or groups the array according to a specified key.
282
     * The input should be either multidimensional array or an array of objects.
283
     *
284
     * The $key can be either a key name of the sub-array, a property name of object, or an anonymous
285
     * function that must return the value that will be used as a key.
286
     *
287
     * $groups is an array of keys, that will be used to group the input array into one or more sub-arrays based
288
     * on keys specified.
289
     *
290
     * If the `$key` is specified as `null` or a value of an element corresponding to the key is `null` in addition
291
     * to `$groups` not specified then the element is discarded.
292
     *
293
     * For example:
294
     *
295
     * ```php
296
     * $array = [
297
     *     ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],
298
     *     ['id' => '345', 'data' => 'def', 'device' => 'tablet'],
299
     *     ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],
300
     * ];
301
     * $result = ArrayHelper::index($array, 'id');
302
     * ```
303
     *
304
     * The result will be an associative array, where the key is the value of `id` attribute
305
     *
306
     * ```php
307
     * [
308
     *     '123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],
309
     *     '345' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']
310
     *     // The second element of an original array is overwritten by the last element because of the same id
311
     * ]
312
     * ```
313
     *
314
     * An anonymous function can be used in the grouping array as well.
315
     *
316
     * ```php
317
     * $result = ArrayHelper::index($array, function ($element) {
318
     *     return $element['id'];
319
     * });
320
     * ```
321
     *
322
     * Passing `id` as a third argument will group `$array` by `id`:
323
     *
324
     * ```php
325
     * $result = ArrayHelper::index($array, null, 'id');
326
     * ```
327
     *
328
     * The result will be a multidimensional array grouped by `id` on the first level, by `device` on the second level
329
     * and indexed by `data` on the third level:
330
     *
331
     * ```php
332
     * [
333
     *     '123' => [
334
     *         ['id' => '123', 'data' => 'abc', 'device' => 'laptop']
335
     *     ],
336
     *     '345' => [ // all elements with this index are present in the result array
337
     *         ['id' => '345', 'data' => 'def', 'device' => 'tablet'],
338
     *         ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],
339
     *     ]
340
     * ]
341
     * ```
342
     *
343
     * The anonymous function can be used in the array of grouping keys as well:
344
     *
345
     * ```php
346
     * $result = ArrayHelper::index($array, 'data', [function ($element) {
347
     *     return $element['id'];
348
     * }, 'device']);
349
     * ```
350
     *
351
     * The result will be a multidimensional array grouped by `id` on the first level, by the `device` on the second one
352
     * and indexed by the `data` on the third level:
353
     *
354
     * ```php
355
     * [
356
     *     '123' => [
357
     *         'laptop' => [
358
     *             'abc' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop']
359
     *         ]
360
     *     ],
361
     *     '345' => [
362
     *         'tablet' => [
363
     *             'def' => ['id' => '345', 'data' => 'def', 'device' => 'tablet']
364
     *         ],
365
     *         'smartphone' => [
366
     *             'hgi' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']
367
     *         ]
368
     *     ]
369
     * ]
370
     * ```
371
     *
372
     * @param array $array the array that needs to be indexed or grouped
373
     * @param string|\Closure|null $key the column name or anonymous function which result will be used to index the array
374
     * @param string|string[]|\Closure[]|null $groups the array of keys, that will be used to group the input array
375
     * by one or more keys. If the $key attribute or its value for the particular element is null and $groups is not
376
     * defined, the array element will be discarded. Otherwise, if $groups is specified, array element will be added
377
     * to the result array without any key. This parameter is available since version 2.0.8.
378
     * @return array the indexed and/or grouped array
379
     */
380 3
    public static function index($array, $key, $groups = [])
381
    {
382 3
        $result = [];
383 3
        $groups = (array)$groups;
384
385 3
        foreach ($array as $element) {
386 3
            $lastArray = &$result;
387
388 3
            foreach ($groups as $group) {
389 1
                $value = static::getValue($element, $group);
390 1
                if (!array_key_exists($value, $lastArray)) {
391 1
                    $lastArray[$value] = [];
392 1
                }
393 1
                $lastArray = &$lastArray[$value];
394 3
            }
395
396 3
            if ($key === null) {
397 2
                if (!empty($groups)) {
398 1
                    $lastArray[] = $element;
399 1
                }
400 2
            } else {
401 3
                $value = static::getValue($element, $key);
402 3
                if ($value !== null) {
403 3
                    if (is_float($value)) {
404 1
                        $value = (string) $value;
405 1
                    }
406 3
                    $lastArray[$value] = $element;
407 3
                }
408
            }
409 3
            unset($lastArray);
410 3
        }
411
412 3
        return $result;
413
    }
414
415
    /**
416
     * Returns the values of a specified column in an array.
417
     * The input array should be multidimensional or an array of objects.
418
     *
419
     * For example,
420
     *
421
     * ```php
422
     * $array = [
423
     *     ['id' => '123', 'data' => 'abc'],
424
     *     ['id' => '345', 'data' => 'def'],
425
     * ];
426
     * $result = ArrayHelper::getColumn($array, 'id');
427
     * // the result is: ['123', '345']
428
     *
429
     * // using anonymous function
430
     * $result = ArrayHelper::getColumn($array, function ($element) {
431
     *     return $element['id'];
432
     * });
433
     * ```
434
     *
435
     * @param array $array
436
     * @param string|\Closure $name
437
     * @param boolean $keepKeys whether to maintain the array keys. If false, the resulting array
438
     * will be re-indexed with integers.
439
     * @return array the list of column values
440
     */
441 7
    public static function getColumn($array, $name, $keepKeys = true)
442
    {
443 7
        $result = [];
444 7
        if ($keepKeys) {
445 7
            foreach ($array as $k => $element) {
446 7
                $result[$k] = static::getValue($element, $name);
447 7
            }
448 7
        } else {
449 1
            foreach ($array as $element) {
450 1
                $result[] = static::getValue($element, $name);
451 1
            }
452
        }
453
454 7
        return $result;
455
    }
456
457
    /**
458
     * Builds a map (key-value pairs) from a multidimensional array or an array of objects.
459
     * The `$from` and `$to` parameters specify the key names or property names to set up the map.
460
     * Optionally, one can further group the map according to a grouping field `$group`.
461
     *
462
     * For example,
463
     *
464
     * ```php
465
     * $array = [
466
     *     ['id' => '123', 'name' => 'aaa', 'class' => 'x'],
467
     *     ['id' => '124', 'name' => 'bbb', 'class' => 'x'],
468
     *     ['id' => '345', 'name' => 'ccc', 'class' => 'y'],
469
     * ];
470
     *
471
     * $result = ArrayHelper::map($array, 'id', 'name');
472
     * // the result is:
473
     * // [
474
     * //     '123' => 'aaa',
475
     * //     '124' => 'bbb',
476
     * //     '345' => 'ccc',
477
     * // ]
478
     *
479
     * $result = ArrayHelper::map($array, 'id', 'name', 'class');
480
     * // the result is:
481
     * // [
482
     * //     'x' => [
483
     * //         '123' => 'aaa',
484
     * //         '124' => 'bbb',
485
     * //     ],
486
     * //     'y' => [
487
     * //         '345' => 'ccc',
488
     * //     ],
489
     * // ]
490
     * ```
491
     *
492
     * @param array $array
493
     * @param string|\Closure $from
494
     * @param string|\Closure $to
495
     * @param string|\Closure $group
496
     * @return array
497
     */
498 22
    public static function map($array, $from, $to, $group = null)
499
    {
500 22
        $result = [];
501 22
        foreach ($array as $element) {
502 21
            $key = static::getValue($element, $from);
503 21
            $value = static::getValue($element, $to);
504 21
            if ($group !== null) {
505 1
                $result[static::getValue($element, $group)][$key] = $value;
506 1
            } else {
507 21
                $result[$key] = $value;
508
            }
509 22
        }
510
511 22
        return $result;
512
    }
513
514
    /**
515
     * Checks if the given array contains the specified key.
516
     * This method enhances the `array_key_exists()` function by supporting case-insensitive
517
     * key comparison.
518
     * @param string $key the key to check
519
     * @param array $array the array with keys to check
520
     * @param boolean $caseSensitive whether the key comparison should be case-sensitive
521
     * @return boolean whether the array contains the specified key
522
     */
523 8
    public static function keyExists($key, $array, $caseSensitive = true)
524
    {
525 8
        if ($caseSensitive) {
526
            // Function `isset` checks key faster but skips `null`, `array_key_exists` handles this case
527
            // http://php.net/manual/en/function.array-key-exists.php#107786
528 8
            return isset($array[$key]) || array_key_exists($key, $array);
529
        } else {
530 1
            foreach (array_keys($array) as $k) {
531 1
                if (strcasecmp($key, $k) === 0) {
532 1
                    return true;
533
                }
534 1
            }
535
536 1
            return false;
537
        }
538
    }
539
540
    /**
541
     * Sorts an array of objects or arrays (with the same structure) by one or several keys.
542
     * @param array $array the array to be sorted. The array will be modified after calling this method.
543
     * @param string|\Closure|array $key the key(s) to be sorted by. This refers to a key name of the sub-array
544
     * elements, a property name of the objects, or an anonymous function returning the values for comparison
545
     * purpose. The anonymous function signature should be: `function($item)`.
546
     * To sort by multiple keys, provide an array of keys here.
547
     * @param integer|array $direction the sorting direction. It can be either `SORT_ASC` or `SORT_DESC`.
548
     * When sorting by multiple keys with different sorting directions, use an array of sorting directions.
549
     * @param integer|array $sortFlag the PHP sort flag. Valid values include
550
     * `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING`, `SORT_LOCALE_STRING`, `SORT_NATURAL` and `SORT_FLAG_CASE`.
551
     * Please refer to [PHP manual](http://php.net/manual/en/function.sort.php)
552
     * for more details. When sorting by multiple keys with different sort flags, use an array of sort flags.
553
     * @throws InvalidParamException if the $direction or $sortFlag parameters do not have
554
     * correct number of elements as that of $key.
555
     */
556 6
    public static function multisort(&$array, $key, $direction = SORT_ASC, $sortFlag = SORT_REGULAR)
557
    {
558 6
        $keys = is_array($key) ? $key : [$key];
559 6
        if (empty($keys) || empty($array)) {
560
            return;
561
        }
562 6
        $n = count($keys);
563 6
        if (is_scalar($direction)) {
564 2
            $direction = array_fill(0, $n, $direction);
565 6
        } elseif (count($direction) !== $n) {
566
            throw new InvalidParamException('The length of $direction parameter must be the same as that of $keys.');
567
        }
568 6
        if (is_scalar($sortFlag)) {
569 6
            $sortFlag = array_fill(0, $n, $sortFlag);
570 6
        } elseif (count($sortFlag) !== $n) {
571
            throw new InvalidParamException('The length of $sortFlag parameter must be the same as that of $keys.');
572
        }
573 6
        $args = [];
574 6
        foreach ($keys as $i => $key) {
575 6
            $flag = $sortFlag[$i];
576 6
            $args[] = static::getColumn($array, $key);
577 6
            $args[] = $direction[$i];
578 6
            $args[] = $flag;
579 6
        }
580
581
        // This fix is used for cases when main sorting specified by columns has equal values
582
        // Without it it will lead to Fatal Error: Nesting level too deep - recursive dependency?
583 6
        $args[] = range(1, count($array));
584 6
        $args[] = SORT_ASC;
585 6
        $args[] = SORT_NUMERIC;
586
587 6
        $args[] = &$array;
588 6
        call_user_func_array('array_multisort', $args);
589 6
    }
590
591
    /**
592
     * Encodes special characters in an array of strings into HTML entities.
593
     * Only array values will be encoded by default.
594
     * If a value is an array, this method will also encode it recursively.
595
     * Only string values will be encoded.
596
     * @param array $data data to be encoded
597
     * @param boolean $valuesOnly whether to encode array values only. If false,
598
     * both the array keys and array values will be encoded.
599
     * @param string $charset the charset that the data is using. If not set,
600
     * [[\yii\base\Application::charset]] will be used.
601
     * @return array the encoded data
602
     * @see http://www.php.net/manual/en/function.htmlspecialchars.php
603
     */
604 1
    public static function htmlEncode($data, $valuesOnly = true, $charset = null)
605
    {
606 1
        if ($charset === null) {
607 1
            $charset = Yii::$app ? Yii::$app->charset : 'UTF-8';
608 1
        }
609 1
        $d = [];
610 1
        foreach ($data as $key => $value) {
611 1
            if (!$valuesOnly && is_string($key)) {
612 1
                $key = htmlspecialchars($key, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
613 1
            }
614 1
            if (is_string($value)) {
615 1
                $d[$key] = htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
616 1
            } elseif (is_array($value)) {
617 1
                $d[$key] = static::htmlEncode($value, $valuesOnly, $charset);
618 1
            } else {
619 1
                $d[$key] = $value;
620
            }
621 1
        }
622
623 1
        return $d;
624
    }
625
626
    /**
627
     * Decodes HTML entities into the corresponding characters in an array of strings.
628
     * Only array values will be decoded by default.
629
     * If a value is an array, this method will also decode it recursively.
630
     * Only string values will be decoded.
631
     * @param array $data data to be decoded
632
     * @param boolean $valuesOnly whether to decode array values only. If false,
633
     * both the array keys and array values will be decoded.
634
     * @return array the decoded data
635
     * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
636
     */
637 1
    public static function htmlDecode($data, $valuesOnly = true)
638
    {
639 1
        $d = [];
640 1
        foreach ($data as $key => $value) {
641 1
            if (!$valuesOnly && is_string($key)) {
642 1
                $key = htmlspecialchars_decode($key, ENT_QUOTES);
643 1
            }
644 1
            if (is_string($value)) {
645 1
                $d[$key] = htmlspecialchars_decode($value, ENT_QUOTES);
646 1
            } elseif (is_array($value)) {
647 1
                $d[$key] = static::htmlDecode($value);
648 1
            } else {
649 1
                $d[$key] = $value;
650
            }
651 1
        }
652
653 1
        return $d;
654
    }
655
656
    /**
657
     * Returns a value indicating whether the given array is an associative array.
658
     *
659
     * An array is associative if all its keys are strings. If `$allStrings` is false,
660
     * then an array will be treated as associative if at least one of its keys is a string.
661
     *
662
     * Note that an empty array will NOT be considered associative.
663
     *
664
     * @param array $array the array being checked
665
     * @param boolean $allStrings whether the array keys must be all strings in order for
666
     * the array to be treated as associative.
667
     * @return boolean whether the array is associative
668
     */
669 168
    public static function isAssociative($array, $allStrings = true)
670
    {
671 168
        if (!is_array($array) || empty($array)) {
672 138
            return false;
673
        }
674
675 51
        if ($allStrings) {
676 51
            foreach ($array as $key => $value) {
677 51
                if (!is_string($key)) {
678 7
                    return false;
679
                }
680 46
            }
681 46
            return true;
682
        } else {
683 1
            foreach ($array as $key => $value) {
684 1
                if (is_string($key)) {
685 1
                    return true;
686
                }
687
            }
688
            return false;
689
        }
690
    }
691
692
    /**
693
     * Returns a value indicating whether the given array is an indexed array.
694
     *
695
     * An array is indexed if all its keys are integers. If `$consecutive` is true,
696
     * then the array keys must be a consecutive sequence starting from 0.
697
     *
698
     * Note that an empty array will be considered indexed.
699
     *
700
     * @param array $array the array being checked
701
     * @param boolean $consecutive whether the array keys must be a consecutive sequence
702
     * in order for the array to be treated as indexed.
703
     * @return boolean whether the array is associative
704
     */
705 1
    public static function isIndexed($array, $consecutive = false)
706
    {
707 1
        if (!is_array($array)) {
708 1
            return false;
709
        }
710
711 1
        if (empty($array)) {
712 1
            return true;
713
        }
714
715 1
        if ($consecutive) {
716 1
            return array_keys($array) === range(0, count($array) - 1);
717
        } else {
718 1
            foreach ($array as $key => $value) {
719 1
                if (!is_int($key)) {
720
                    return false;
721
                }
722 1
            }
723 1
            return true;
724
        }
725
    }
726
727
    /**
728
     * Check whether an array or [[\Traversable]] contains an element.
729
     *
730
     * This method does the same as the PHP function [in_array()](http://php.net/manual/en/function.in-array.php)
731
     * but additionally works for objects that implement the [[\Traversable]] interface.
732
     * @param mixed $needle The value to look for.
733
     * @param array|\Traversable $haystack The set of values to search.
734
     * @param boolean $strict Whether to enable strict (`===`) comparison.
735
     * @return boolean `true` if `$needle` was found in `$haystack`, `false` otherwise.
736
     * @throws InvalidParamException if `$haystack` is neither traversable nor an array.
737
     * @see http://php.net/manual/en/function.in-array.php
738
     * @since 2.0.7
739
     */
740 16
    public static function isIn($needle, $haystack, $strict = false)
741
    {
742 16
        if ($haystack instanceof \Traversable) {
743 7
            foreach ($haystack as $value) {
744 7
                if ($needle == $value && (!$strict || $needle === $value)) {
745 7
                    return true;
746
                }
747 6
            }
748 16
        } elseif (is_array($haystack)) {
749 15
            return in_array($needle, $haystack, $strict);
750
        } else {
751 1
            throw new InvalidParamException('Argument $haystack must be an array or implement Traversable');
752
        }
753
754 5
        return false;
755
    }
756
757
    /**
758
     * Checks whether a variable is an array or [[\Traversable]].
759
     *
760
     * This method does the same as the PHP function [is_array()](http://php.net/manual/en/function.is-array.php)
761
     * but additionally works on objects that implement the [[\Traversable]] interface.
762
     * @param mixed $var The variable being evaluated.
763
     * @return boolean whether $var is array-like
764
     * @see http://php.net/manual/en/function.is_array.php
765
     * @since 2.0.8
766
     */
767 387
    public static function isTraversable($var)
768
    {
769 387
        return is_array($var) || $var instanceof \Traversable;
770
    }
771
772
    /**
773
     * Checks whether an array or [[\Traversable]] is a subset of another array or [[\Traversable]].
774
     *
775
     * This method will return `true`, if all elements of `$needles` are contained in
776
     * `$haystack`. If at least one element is missing, `false` will be returned.
777
     * @param array|\Traversable $needles The values that must **all** be in `$haystack`.
778
     * @param array|\Traversable $haystack The set of value to search.
779
     * @param boolean $strict Whether to enable strict (`===`) comparison.
780
     * @throws InvalidParamException if `$haystack` or `$needles` is neither traversable nor an array.
781
     * @return boolean `true` if `$needles` is a subset of `$haystack`, `false` otherwise.
782
     * @since 2.0.7
783
     */
784 6
    public static function isSubset($needles, $haystack, $strict = false)
785
    {
786 6
        if (is_array($needles) || $needles instanceof \Traversable) {
787 5
            foreach ($needles as $needle) {
788 4
                if (!static::isIn($needle, $haystack, $strict)) {
789 3
                    return false;
790
                }
791 4
            }
792 4
            return true;
793
        } else {
794 1
            throw new InvalidParamException('Argument $needles must be an array or implement Traversable');
795
        }
796
    }
797
798
    /**
799
     * Filters array according to rules specified.
800
     *
801
     * For example:
802
     *
803
     * ```php
804
     * $array = [
805
     *     'A' => [1, 2],
806
     *     'B' => [
807
     *         'C' => 1,
808
     *         'D' => 2,
809
     *     ],
810
     *     'E' => 1,
811
     * ];
812
     *
813
     * $result = \yii\helpers\ArrayHelper::filter($array, ['A']);
814
     * // $result will be:
815
     * // [
816
     * //     'A' => [1, 2],
817
     * // ]
818
     *
819
     * $result = \yii\helpers\ArrayHelper::filter($array, ['A', 'B.C']);
820
     * // $result will be:
821
     * // [
822
     * //     'A' => [1, 2],
823
     * //     'B' => ['C' => 1],
824
     * // ]
825
     *
826
     * $result = \yii\helpers\ArrayHelper::filter($array, ['B', '!B.C']);
827
     * // $result will be:
828
     * // [
829
     * //     'B' => ['D' => 2],
830
     * // ]
831
     * ```
832
     *
833
     * @param array $array Source array
834
     * @param array $filters Rules that define array keys which should be left or removed from results.
835
     * Each rule is:
836
     * - `var` - `$array['var']` will be left in result.
837
     * - `var.key` = only `$array['var']['key'] will be left in result.
838
     * - `!var.key` = `$array['var']['key'] will be removed from result.
839
     * @return array Filtered array
840
     * @since 2.0.9
841
     */
842 24
    public static function filter($array, $filters)
843
    {
844 24
        $result = [];
845 24
        $forbiddenVars = [];
846
847 24
        foreach ($filters as $var) {
848 5
            $keys = explode('.', $var);
849 5
            $globalKey = $keys[0];
850 5
            $localKey = isset($keys[1]) ? $keys[1] : null;
851
852 5
            if ($globalKey[0] === '!') {
853 2
                $forbiddenVars[] = [
854 2
                    substr($globalKey, 1),
855 2
                    $localKey,
856
                ];
857 2
                continue;
858
            }
859
860 5
            if (empty($array[$globalKey])) {
861 5
                continue;
862
            }
863 5
            if ($localKey === null) {
864 5
                $result[$globalKey] = $array[$globalKey];
865 5
                continue;
866
            }
867 2
            if (!isset($array[$globalKey][$localKey])) {
868 1
                continue;
869
            }
870 2
            if (!array_key_exists($globalKey, $result)) {
871 2
                $result[$globalKey] = [];
872 2
            }
873 2
            $result[$globalKey][$localKey] = $array[$globalKey][$localKey];
874 24
        }
875
876 24
        foreach ($forbiddenVars as $var) {
877 2
            list($globalKey, $localKey) = $var;
878 2
            if (array_key_exists($globalKey, $result)) {
879 2
                unset($result[$globalKey][$localKey]);
880 2
            }
881 24
        }
882
883 24
        return $result;
884
    }
885
}
886