Passed
Push — master ( 93b1f5...93d042 )
by Eric
14:02 queued 01:23
created

Arrays::keyExists()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
ccs 4
cts 4
cp 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Utility - Collection of various PHP utility functions.
7
 *
8
 * @author    Eric Sizemore <[email protected]>
9
 * @version   2.0.0
10
 * @copyright (C) 2017 - 2024 Eric Sizemore
11
 * @license   The MIT License (MIT)
12
 *
13
 * Copyright (C) 2017 - 2024 Eric Sizemore <https://www.secondversion.com>.
14
 *
15
 * Permission is hereby granted, free of charge, to any person obtaining a copy
16
 * of this software and associated documentation files (the "Software"), to
17
 * deal in the Software without restriction, including without limitation the
18
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
19
 * sell copies of the Software, and to permit persons to whom the Software is
20
 * furnished to do so, subject to the following conditions:
21
 *
22
 * The above copyright notice and this permission notice shall be included in
23
 * all copies or substantial portions of the Software.
24
 *
25
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31
 * THE SOFTWARE.
32
 */
33
34
namespace Esi\Utility;
35
36
// Exceptions
37
use RuntimeException;
38
39
// Classes
40
use ArrayAccess;
41
42
// Functions
43
use function array_key_exists;
44
use function array_map;
45
use function array_merge;
46
use function array_sum;
47
use function call_user_func;
48
use function count;
49
use function get_object_vars;
50
use function is_array;
51
use function is_null;
52
use function is_object;
53
use function sprintf;
54
use function trigger_error;
55
56
use const E_USER_DEPRECATED;
57
58
/**
59
 * Array utilities.
60
 * @see \Esi\Utility\Tests\ArraysTest
61
 */
62
final class Arrays
63
{
64
    /**
65
     * isAssociative()
66
     *
67
     * Determines if the given array is an associative array.
68
     *
69
     * @param  array<mixed>  $array  Array to check
70
     */
71 3
    public static function isAssociative(array $array): bool
72
    {
73 3
        return !array_is_list($array);
74
    }
75
76
    /**
77
     * get()
78
     *
79
     * Retrieve a value from an array.
80
     *
81
     * @param  array<mixed>|ArrayAccess<mixed, mixed>  $array    Array to retrieve value from.
82
     * @param  string|int    $key      Key to retrieve
83
     * @param  mixed         $default  A default value to return if $key does not exist
84
     *
85
     * @throws RuntimeException  If $array is not accessible
86
     */
87 2
    public static function get(array | ArrayAccess $array, string | int $key, mixed $default = null): mixed
88
    {
89 2
        if (Arrays::keyExists($array, $key)) {
90 2
            return $array[$key];
91
        }
92 1
        return $default;
93
    }
94
95
    /**
96
     * set()
97
     *
98
     * Add a value to an array.
99
     *
100
     * @param  array<mixed>|ArrayAccess<mixed, mixed>     &$array  Array to add value to.
101
     * @param  string|int|null  $key     Key to add
102
     * @param  mixed            $value   Value to add
103
     *
104
     * @throws RuntimeException  If $array is not accessible
105
     */
106 1
    public static function set(array | ArrayAccess &$array, string | int | null $key, mixed $value): mixed
107
    {
108 1
        if (is_null($key)) {
109 1
            return $array = $value;
110
        }
111 1
        return $array[$key] = $value;
112
    }
113
114
    /**
115
     * Checks if a key exists in an array.
116
     *
117
     * @deprecated
118
     * @param  array<mixed>|ArrayAccess<mixed, mixed>  $array  Array to check
119
     * @param  string|int                $key    Key to check
120
     */
121 1
    public static function exists(array | ArrayAccess $array, string | int $key): bool
122
    {
123 1
        trigger_error(sprintf(
124 1
            '%s is deprecated and will be removed in v2.1.0, use %s instead.',
125 1
            __METHOD__,
126 1
            self::class . '::keyExists'
127 1
        ), E_USER_DEPRECATED);
128
129 1
        return Arrays::keyExists($array, $key);
130
    }
131
132
    /**
133
     * Checks if a key exists in an array.
134
     *
135
     * @param  array<mixed>|ArrayAccess<mixed, mixed>  $array  Array to check
136
     * @param  string|int                $key    Key to check
137
     */
138 4
    public static function keyExists(array | ArrayAccess $array, string | int $key): bool
139
    {
140 4
        if ($array instanceof ArrayAccess) {
1 ignored issue
show
introduced by
$array is never a sub-type of ArrayAccess.
Loading history...
141 2
            return $array->offsetExists($key);
142
        }
143 4
        return array_key_exists($key, $array);
144
    }
145
146
    /**
147
     * Checks if a value exists in an array.
148
     *
149
     * @param  array<mixed>  $array  Array to check
150
     * @param  string|int    $value  Value to check
151
     */
152 1
    public static function valueExists(array $array, string | int $value): bool
153
    {
154 1
        return in_array($value, $array, true);
155
    }
156
157
    /**
158
     * flatten()
159
     *
160
     * Flattens a multidimensional array.
161
     *
162
     * Keys are preserved based on $separator.
163
     *
164
     * @param   array<mixed>   $array      Array to flatten.
165
     * @param   string         $separator  The new keys are a list of original keys separated by $separator.
166
     *
167
     * @since 1.2.0
168
     * @param   string         $prepend    A string to prepend to resulting array keys.
169
     *
170
     * @return  array<mixed>               The flattened array.
171
     */
172 1
    public static function flatten(array $array, string $separator = '.', string $prepend = ''): array
173
    {
174 1
        $result = [];
175
176 1
        foreach ($array as $key => $value) {
177 1
            if (is_array($value) && $value !== []) {
178 1
                $result = array_merge($result, Arrays::flatten($value, $separator, $prepend . $key . $separator));
179
            } else {
180 1
                $result[$prepend . $key] = $value;
181
            }
182
        }
183 1
        return $result;
184
    }
185
186
    /**
187
     * mapDeep()
188
     *
189
     * Recursively applies a callback to all non-iterable elements of an array or an object.
190
     *
191
     * @since 1.2.0 - updated with inspiration from the WordPress map_deep() function.
192
     *      @see https://developer.wordpress.org/reference/functions/map_deep/
193
     *
194
     * @param  mixed     $array     The array to apply $callback to.
195
     * @param  callable  $callback  The callback function to apply.
196
     */
197 1
    public static function mapDeep(mixed $array, callable $callback): mixed
198
    {
199 1
        if (is_array($array)) {
200 1
            foreach ($array as $key => $value) {
201 1
                $array[$key] = Arrays::mapDeep($value, $callback);
202
            }
203 1
        } elseif (is_object($array)) {
204 1
            foreach (get_object_vars($array) as $key => $value) {
205
                // @phpstan-ignore-next-line
206 1
                $array->$key = Arrays::mapDeep($value, $callback);
207
            }
208
        } else {
209 1
            $array = call_user_func($callback, $array);
210
        }
211 1
        return $array;
212
    }
213
214
    /**
215
     * interlace()
216
     *
217
     * Interlaces one or more arrays' values (not preserving keys).
218
     *
219
     * Example:
220
     *
221
     *      var_dump(Utility\Arrays::interlace(
222
     *          [1, 2, 3],
223
     *          ['a', 'b', 'c']
224
     *      ));
225
     *
226
     * Result:
227
     *      Array (
228
     *          [0] => 1
229
     *          [1] => a
230
     *          [2] => 2
231
     *          [3] => b
232
     *          [4] => 3
233
     *          [5] => c
234
     *      )
235
     *
236
     * @since 1.2.0
237
     *
238
     * @param   array<mixed>        ...$args
239
     * @return  array<mixed>|false
240
     */
241 1
    public static function interlace(array ...$args): array | false
242
    {
243 1
        $numArgs = count($args);
244
245 1
        if ($numArgs === 0) {
246 1
            return false;
247
        }
248
249 1
        if ($numArgs === 1) {
250 1
            return $args[0];
251
        }
252
253 1
        $newArray      = [];
254 1
        $totalElements = array_sum(array_map('count', $args));
255
256 1
        for ($i = 0; $i < $totalElements; $i++) {
257 1
            foreach ($args as $arg) {
258 1
                if (isset($arg[$i])) {
259 1
                    $newArray[] = $arg[$i];
260
                }
261
            }
262
        }
263 1
        return $newArray;
264
    }
265
266
    /**
267
     *
268
     * Returns an associative array, grouped by $key, where the keys are the distinct values of $key,
269
     * and the values are arrays of items that share the same $key.
270
     *
271
     * *Important to note:* if a $key is provided that does not exist, the result will be an empty array.
272
     *
273
     * @since 2.0.0
274
     *
275
     * @param   array<mixed, array<mixed>>  $array  Input array.
276
     * @param   string                      $key    Key to use for grouping.
277
     * @return  array<mixed, array<mixed>>
278
     */
279 2
    public static function groupBy(array $array, string $key): array
280
    {
281 2
        $result = [];
282
283 2
        foreach ($array as $item) {
284 2
            if (!self::isAssociative($item)) {
285
                //@codeCoverageIgnoreStart
286
                continue;
287
                //@codeCoverageIgnoreEnd
288
            }
289
290 2
            if (!isset($item[$key])) {
291 1
                continue;
292
            }
293
294 1
            $groupKey = $item[$key];
295
296 1
            if (!isset($result[$groupKey])) {
297 1
                $result[$groupKey] = [];
298
            }
299
300 1
            $result[$groupKey][] = $item;
301
        }
302 2
        return $result;
303
    }
304
}
305