Passed
Push — master ( 0ce24c...6c61f0 )
by Eric
01:53
created

Arrays::set()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 3
dl 0
loc 9
ccs 6
cts 6
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of Esi\Utility.
7
 *
8
 * (c) 2017 - 2024 Eric Sizemore <[email protected]>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE.md file that was distributed with this source code.
12
 */
13
14
namespace Esi\Utility;
15
16
use ArrayAccess;
17
use RuntimeException;
18
use SplObjectStorage;
19
20
use function array_map;
21
use function array_merge;
22
use function array_sum;
23
24
/**
25
 * Array utilities.
26
 *
27
 * @see Tests\ArraysTest
28
 *
29
 * @template TObject of SplObjectStorage
30
 * @template TData
31
 *
32
 * @uses SplObjectStorage<TObject, TData>
33
 */
34
abstract class Arrays
35
{
36
    /**
37
     * flatten().
38
     *
39
     * Flattens a multidimensional array.
40
     *
41
     * Keys are preserved based on $separator.
42
     *
43
     * @param array<TKey, TValue> $array     Array to flatten.
44
     * @param string              $separator The new keys are a list of original keys separated by $separator.
45
     * @param string              $prepend   A string to prepend to resulting array keys.
46
     *
47
     * @since 1.2.0
48
     *
49
     * @return array<TKey, TValue> The flattened array.
50
     *
51
     * @template TKey
52
     * @template TValue
53
     */
54 1
    public static function flatten(array $array, string $separator = '.', string $prepend = ''): array
55
    {
56 1
        $result = [];
57
58 1
        foreach ($array as $key => $value) {
59 1
            if (\is_array($value) && $value !== []) {
60 1
                $result = array_merge($result, Arrays::flatten($value, $separator, $prepend . $key . $separator));
61
            } else {
62 1
                $result[$prepend . $key] = $value;
63
            }
64
        }
65
66
        /**
67
         * @var array<TKey, TValue> $result
68
         */
69 1
        return $result;
70
    }
71
72
    /**
73
     * get().
74
     *
75
     * Retrieve a value from an array.
76
     *
77
     * @param array<TKey, TValue>|ArrayAccess<TKey, TValue> $array   Array to retrieve value from.
78
     * @param TKey                                          $key     Key to retrieve
0 ignored issues
show
Bug introduced by
The type Esi\Utility\TKey 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...
79
     * @param TDefault                                      $default A default value to return if $key does not exist
0 ignored issues
show
Bug introduced by
The type Esi\Utility\TDefault 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...
80
     *
81
     * @throws RuntimeException If $array is not accessible
82
     *
83
     * @template TKey of (int|string)
84
     * @template TValue
85
     * @template TDefault
86
     *
87
     * @uses ArrayAccess<TKey, TValue>
88
     */
89 7
    public static function get(array|ArrayAccess $array, int|string $key, mixed $default = null): mixed
90
    {
91 7
        if (Arrays::keyExists($array, $key)) {
92 7
            return $array[$key];
93
        }
94
95 5
        return $default;
96
    }
97
98
    /**
99
     * Returns an associative array, grouped by $key, where the keys are the distinct values of $key,
100
     * and the values are arrays of items that share the same $key.
101
     *
102
     * *Important to note:* if a $key is provided that does not exist, the result will be an empty array.
103
     *
104
     * @since 2.0.0
105
     *
106
     * @param array<TKey, array<TKey, mixed>> $array Input array.
107
     * @param string                          $key   Key to use for grouping.
108
     *
109
     * @return array<mixed, non-empty-list<array<TKey, mixed>>>|array{}
1 ignored issue
show
Documentation Bug introduced by
The doc comment array<mixed, non-empty-l...<TKey, mixed>>>|array{} at position 4 could not be parsed: Unknown type name 'non-empty-list' at position 4 in array<mixed, non-empty-list<array<TKey, mixed>>>|array{}.
Loading history...
110
     *
111
     * @template TKey
112
     */
113 2
    public static function groupBy(array $array, string $key): array
114
    {
115 2
        $result = [];
116
117 2
        foreach ($array as $item) {
118 2
            if (!self::isAssociative($item)) {
119
                //@codeCoverageIgnoreStart
120
                continue;
121
                //@codeCoverageIgnoreEnd
122
            }
123
124 2
            if (!isset($item[$key])) {
125 1
                continue;
126
            }
127
128 1
            $groupKey = $item[$key];
129
130 1
            $result[$groupKey] ??= [];
131 1
            $result[$groupKey][] = $item;
132
        }
133
134 2
        return $result;
135
    }
136
137
    /**
138
     * interlace().
139
     *
140
     * Interlaces one or more arrays' values (not preserving keys).
141
     *
142
     * Example:
143
     *
144
     *      var_dump(Utility\Arrays::interlace(
145
     *          [1, 2, 3],
146
     *          ['a', 'b', 'c']
147
     *      ));
148
     *
149
     * Result:
150
     *      Array (
151
     *          [0] => 1
152
     *          [1] => a
153
     *          [2] => 2
154
     *          [3] => b
155
     *          [4] => 3
156
     *          [5] => c
157
     *      )
158
     *
159
     * @since 1.2.0
160
     *
161
     * @param array<TKey, TValue> ...$args
162
     *
163
     * @return array<TKey, TValue>|false|list<TValue>
0 ignored issues
show
Bug introduced by
The type Esi\Utility\list 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...
164
     *
165
     * @template TKey
166
     * @template TValue
167
     */
168 1
    public static function interlace(array ...$args): array|false
169
    {
170 1
        $numArgs = \count($args);
171
172 1
        if ($numArgs === 0) {
173 1
            return false;
174
        }
175
176 1
        if ($numArgs === 1) {
177 1
            return $args[0];
178
        }
179
180 1
        $newArray      = [];
181 1
        $totalElements = array_sum(array_map('count', $args));
182
183 1
        for ($i = 0; $i < $totalElements; ++$i) {
184 1
            foreach ($args as $arg) {
185 1
                if (isset($arg[$i])) {
186 1
                    $newArray[] = $arg[$i];
187
                }
188
            }
189
        }
190
191 1
        return $newArray;
192
    }
193
194
    /**
195
     * isAssociative().
196
     *
197
     * Determines if the given array is an associative array.
198
     *
199
     * @param array<mixed> $array Array to check
200
     */
201 3
    public static function isAssociative(array $array): bool
202
    {
203 3
        return !array_is_list($array);
204
    }
205
206
    /**
207
     * Checks if a key exists in an array.
208
     *
209
     * @param array<TKey, TValue>|ArrayAccess<TKey, TValue> $array Array to check
210
     * @param TKey                                          $key   Key to check
211
     *
212
     * @return bool
213
     *
214
     * @template TKey of (int|string)
215
     * @template TValue
216
     *
217
     * @uses ArrayAccess<TKey, TValue>
218
     */
219 8
    public static function keyExists(array|ArrayAccess $array, int|string $key): bool
220
    {
221 8
        if ($array instanceof ArrayAccess) {
222 2
            return $array->offsetExists($key);
223
        }
224
225 8
        return \array_key_exists($key, $array);
226
    }
227
228
    /**
229
     * mapDeep().
230
     *
231
     * Recursively applies a callback to all non-iterable elements of an array or an object.
232
     *
233
     * @since 1.2.0 - updated with inspiration from the WordPress map_deep() function.
234
     * @see https://developer.wordpress.org/reference/functions/map_deep/
235
     *
236
     * @param array<mixed>|mixed|object $array    The array to apply $callback to.
237
     * @param callable                  $callback The callback function to apply.
238
     */
239 8
    public static function mapDeep(mixed $array, callable $callback): mixed
240
    {
241
        /**
242
         * @var ?SplObjectStorage<TObject, TData> $visited
243
         */
244 8
        static $visited;
245
246 8
        $visited ??= new SplObjectStorage();
247
248 8
        if (\is_object($array)) {
249 4
            if ($visited->contains($array)) {
250 1
                return $array; // Avoid circular references.
251
            }
252 4
            $visited->attach($array);
253
        }
254
255 8
        if (\is_array($array)) {
256 5
            foreach ($array as $key => $value) {
257 5
                $array[$key] = Arrays::mapDeep($value, $callback);
258
            }
259 8
        } elseif (\is_object($array)) {
260 4
            foreach (get_object_vars($array) as $key => $value) {
261 4
                $array->{$key} = Arrays::mapDeep($value, $callback);
262
            }
263
        } else {
264 7
            $array = $callback($array);
265
        }
266
267 8
        if (\is_object($array)) {
268 4
            $visited->detach($array);
269
        }
270
271 8
        return $array;
272
    }
273
274
    /**
275
     * set().
276
     *
277
     * Add a value to an array.
278
     *
279
     * @param array<TKey, TValue>|ArrayAccess<TKey, TValue> $array Array to add value to.
280
     * @param TKey                                          $key   Key to add
281
     * @param TValue                                        $value Value to add
0 ignored issues
show
Bug introduced by
The type Esi\Utility\TValue 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...
282
     *
283
     * @param-out TValue|non-empty-array<TKey, TValue>|ArrayAccess<TKey, TValue> $array
284
     *
285
     * @throws RuntimeException If $array is not accessible
286
     *
287
     * @template TKey of (int|string)
288
     * @template TValue
289
     *
290
     * @uses ArrayAccess<TKey, TValue>
291
     */
292 6
    public static function set(array|ArrayAccess &$array, null|int|string $key, mixed $value): void
293
    {
294 6
        if ($key === null) {
295 1
            $array = $value;
296
        } else {
297 6
            if ($array instanceof ArrayAccess) {
0 ignored issues
show
introduced by
$array is never a sub-type of ArrayAccess.
Loading history...
298 1
                $array->offsetSet($key, $value);
299
            } else {
300 6
                $array[$key] = $value;
301
            }
302
        }
303
    }
304
305
    /**
306
     * Checks if a value exists in an array.
307
     *
308
     * @param array<TKey, TValue> $array Array to check
309
     * @param TKey                $value Value to check
310
     *
311
     * @template TKey of (int|string)
312
     * @template TValue
313
     */
314 26
    public static function valueExists(array $array, int|string $value): bool
315
    {
316 26
        return \in_array($value, $array, true);
317
    }
318
}
319