Collection::overwrite()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BinaryCube\CarrotMQ\Support;
6
7
use Countable;
8
use ArrayAccess;
9
use Traversable;
10
use ArrayIterator;
11
use IteratorAggregate;
12
13
use function count;
14
use function is_int;
15
use function is_array;
16
use function is_string;
17
use function array_keys;
18
use function array_shift;
19
use function array_values;
20
use function array_unshift;
21
use function func_get_args;
22
use function array_key_exists;
23
use function iterator_to_array;
24
use function forward_static_call_array;
25
26
/**
27
 * Class Collection
28
 */
29
class Collection implements ArrayAccess, Countable, IteratorAggregate
30
{
31
32
    /**
33
     * @var array
34
     */
35
    protected $items = [];
36
37
    /**
38
     * @param array $items
39
     *
40
     * @return static
41
     */
42
    public static function make(array $items = []): self
43
    {
44
        return (new static($items));
45
    }
46
47
    /**
48
     * Constructor.
49
     *
50
     * @param array $items
51
     */
52
    public function __construct(array $items = [])
53
    {
54
        $this->items = $this->getArrayableItems($items);
55
    }
56
57
    /**
58
     * Results array of items from Collection.
59
     *
60
     * @param mixed $items
61
     *
62
     * @return array
63
     */
64
    protected function getArrayableItems($items)
65
    {
66
        $arr = $items;
67
68
        if (is_array($items)) {
69
            return $items;
70
        } elseif ($items instanceof Collection) {
71
            $arr = $items->all();
72
        } elseif ($items instanceof Traversable) {
73
            $arr = iterator_to_array($items);
74
        }
75
76
        return static::mergeWith([], (array) $arr);
77
    }
78
79
    /**
80
     * @return array
81
     */
82
    public function all(): array
83
    {
84
        return $this->items;
85
    }
86
87
    /**
88
     * @return static
89
     */
90
    public function keys()
91
    {
92
        return new static(array_keys($this->items));
93
    }
94
95
    /**
96
     * Reset the keys on the underlying array.
97
     *
98
     * @return static
99
     */
100
    public function values()
101
    {
102
        return new static(array_values($this->items));
103
    }
104
105
    /**
106
     * @param mixed $values
107
     *
108
     * @return $this
109
     */
110
    public function add($values): self
111
    {
112
        $assoc = self::isAssociative($this->items);
113
114
        foreach ($values as $key => $value) {
115
            $this->offsetSet($assoc ? $key : null, $value);
116
        }
117
118
        return $this;
119
    }
120
121
    /**
122
     * @param string|null $key
123
     * @param mixed       $value
124
     *
125
     * @return $this
126
     */
127
    public function put(?string $key, $value): self
128
    {
129
        $this->offsetSet($key, $value);
130
131
        return $this;
132
    }
133
134
    /**
135
     * @param mixed $key
136
     * @param mixed $default
137
     *
138
     * @return mixed
139
     */
140
    public function get($key, $default = null)
141
    {
142
        if ($this->offsetExists($key)) {
143
            return $this->offsetGet($key);
144
        }
145
146
        return $default;
147
    }
148
149
    /**
150
     * @param string     $id
151
     * @param null|mixed $default
152
     *
153
     * @return mixed|null
154
     */
155
    public function getIfSet(string $id, $default = null)
156
    {
157
        if ($this->has($id)) {
158
            return $this->items[$id];
159
        }
160
161
        return $default;
162
    }
163
164
    /**
165
     * @param array $items
166
     *
167
     * @return $this
168
     */
169
    public function overwrite(array $items): self
170
    {
171
        $this->items = $this->getArrayableItems($items);
172
173
        return $this;
174
    }
175
176
    /**
177
     * Delete the given key or keys.
178
     *
179
     * @param integer|string|array $keys
180
     *
181
     * @return $this
182
     */
183
    public function forget($keys): self
184
    {
185
        $this->offsetUnset($keys);
186
187
        return $this;
188
    }
189
190
    /**
191
     * If each array has an element with the same string key value, the latter
192
     * will overwrite the former (different from array_merge_recursive).
193
     * Recursive merging will be conducted if both arrays have an element of array
194
     * type and are having the same key.
195
     * For integer-keyed elements, the elements from the latter array will
196
     * be appended to the former array.
197
     *
198
     * @param array $a array to be merged from. You can specify additional
199
     *                 arrays via second argument, third argument, fourth argument etc.
200
     *
201
     * @return $this
202
     */
203
    public function merge(array $a): self
204
    {
205
        $args = $this->getArrayableItems(func_get_args());
206
207
        array_unshift($args, $this->items);
208
209
        $this->items = forward_static_call_array([static::class, 'mergeWith'], $args);
210
211
        return $this;
212
    }
213
214
    /**
215
     * Determine if an item exists in the collection by key.
216
     *
217
     * @param string|array $key
218
     *
219
     * @return bool
220
     */
221
    public function has($key): bool
222
    {
223
        $keys = (array) $key;
224
225
        foreach ($keys as $value) {
226
            if (! $this->offsetExists($value)) {
227
                return false;
228
            }
229
        }
230
231
        return true;
232
    }
233
234
    /**
235
     * @return int
236
     */
237
    public function count(): int
238
    {
239
        return count($this->items);
240
    }
241
242
    /**
243
     * Determine if the collection is empty or not.
244
     *
245
     * @return bool
246
     */
247
    public function isEmpty(): bool
248
    {
249
        return empty($this->items);
250
    }
251
252
    /**
253
     * Determine if the collection is not empty.
254
     *
255
     * @return bool
256
     */
257
    public function isNotEmpty(): bool
258
    {
259
        return ! $this->isEmpty();
260
    }
261
262
    /**
263
     * @return $this
264
     */
265
    public function clear(): self
266
    {
267
        $this->items = [];
268
269
        return $this;
270
    }
271
272
    /**
273
     * Determine if an item exists at an offset.
274
     *
275
     * @param mixed $key
276
     *
277
     * @return bool
278
     */
279
    public function offsetExists($key): bool
280
    {
281
        return (
282
            isset($this->items[$key])
283
            || array_key_exists($key, $this->items)
284
        );
285
    }
286
287
    /**
288
     * @param mixed $key
289
     *
290
     * @return mixed|void
291
     */
292
    public function offsetGet($key)
293
    {
294
        return $this->items[$key];
295
    }
296
297
    /**
298
     * Set the item at a given offset.
299
     *
300
     * @param mixed $key
301
     * @param mixed $value
302
     *
303
     * @return void
304
     */
305
    public function offsetSet($key, $value)
306
    {
307
        if (! isset($key)) {
308
            $this->items[] = $value;
309
            return;
310
        }
311
312
        $this->items[$key] = $value;
313
    }
314
315
    /**
316
     * Delete the given key or keys.
317
     *
318
     * @param $keys
319
     *
320
     * @return void
321
     */
322
    public function offsetUnset($keys)
323
    {
324
        $keys = (array) $keys;
325
326
        foreach ($keys as $key) {
327
            if ($this->offsetExists($key)) {
328
                unset($this->items[$key]);
329
            }
330
        }
331
    }
332
333
    /**
334
     * @return ArrayIterator
335
     */
336
    public function getIterator(): ArrayIterator
337
    {
338
        return new ArrayIterator($this->all());
339
    }
340
341
    /**
342
     * Returns a value indicating whether the given array is an associative array.
343
     *
344
     * An array is associative if all its keys are strings. If `$allStrings` is false,
345
     * then an array will be treated as associative if at least one of its keys is a string.
346
     *
347
     * Note that an empty array will NOT be considered associative.
348
     *
349
     * @param array $array      the array being checked
350
     * @param bool  $allStrings whether the array keys must be all strings in order for
351
     *                          the array to be treated as associative.
352
     *
353
     * @return bool whether the array is associative
354
     */
355
    protected static function isAssociative(array $array, bool $allStrings = true)
356
    {
357
        if (! is_array($array) || empty($array)) {
0 ignored issues
show
introduced by
The condition is_array($array) is always true.
Loading history...
358
            return false;
359
        }
360
361
        if ($allStrings) {
362
            foreach ($array as $key => $value) {
363
                if (! is_string($key)) {
364
                    return false;
365
                }
366
            }
367
368
            return true;
369
        }
370
371
        foreach ($array as $key => $value) {
372
            if (is_string($key)) {
373
                return true;
374
            }
375
        }
376
377
        return false;
378
    }
379
380
    /**
381
     * @param array $a
382
     * @param array $b
383
     *
384
     * @return array|mixed
385
     */
386
    protected static function mergeWith(array $a, array $b)
387
    {
388
        $args = func_get_args();
389
        $res  = array_shift($args);
390
391
        while (! empty($args)) {
392
            foreach (array_shift($args) as $k => $v) {
393
                if (is_int($k)) {
394
                    if (array_key_exists($k, $res)) {
395
                        $res[] = $v;
396
                    } else {
397
                        $res[$k] = $v;
398
                    }
399
                } elseif (is_array($v) && isset($res[$k]) && is_array($res[$k])) {
400
                    $res[$k] = static::mergeWith($res[$k], $v);
401
                } else {
402
                    $res[$k] = $v;
403
                }
404
            }
405
        }
406
407
        return $res;
408
    }
409
410
}
411