Completed
Push — master ( 8122cf...d163c5 )
by Lars
02:22 queued 46s
created

Arrayy   F

Complexity

Total Complexity 532

Size/Duplication

Total Lines 4897
Duplicated Lines 6.9 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 90.14%

Importance

Changes 0
Metric Value
dl 338
loc 4897
ccs 1417
cts 1572
cp 0.9014
rs 0.8
c 0
b 0
f 0
wmc 532
lcom 1
cbo 6

200 Methods

Rating   Name   Duplication   Size   Complexity  
A containsKey() 0 4 1
B __construct() 0 43 8
A __invoke() 0 10 2
A __isset() 0 4 1
A __set() 0 4 1
A __toString() 0 4 1
A __unset() 0 4 1
A __get() 0 10 2
A add() 0 4 1
A append() 0 20 4
A asort() 0 8 1
A count() 0 4 1
A exchangeArray() 0 6 1
A getArrayCopy() 0 6 1
A getIterator() 0 14 3
A getIteratorClass() 0 4 1
A ksort() 0 8 1
A natcasesort() 0 8 1
A natsort() 0 8 1
C offsetExists() 0 54 11
A offsetGet() 0 4 2
A offsetSet() 0 10 2
B offsetUnset() 0 37 7
A serialize() 0 6 1
A setIteratorClass() 0 19 4
A uasort() 14 14 2
A uksort() 0 4 1
A unserialize() 0 6 1
B appendArrayValues() 0 26 7
A appendToEachKey() 19 19 4
A appendToEachValue() 19 19 5
A arsort() 0 8 1
A at() 0 14 2
A average() 0 14 3
A changeKeyCase() 0 27 5
A changeSeparator() 0 6 1
A chunk() 0 8 1
A clean() 0 8 1
A clear() 0 7 1
B contains() 0 21 6
B containsCaseInsensitive() 0 25 8
A containsKeys() 0 28 2
A containsKeysRecursive() 0 4 1
A containsValue() 0 4 1
A containsValueRecursive() 0 4 1
A containsValues() 0 6 1
A countValues() 0 4 1
A create() 0 8 1
A createByReference() 0 8 1
A createFromGeneratorFunction() 0 6 1
A createFromGeneratorImmutable() 0 4 1
A createFromJson() 0 4 1
A createFromObject() 0 17 3
A createFromObjectVars() 0 4 1
A createFromString() 0 29 5
A createFromTraversableImmutable() 0 4 1
A createWithRange() 0 4 1
A customSortKeys() 14 14 2
A customSortValues() 14 14 2
A delete() 0 8 2
A diff() 0 8 1
B diffRecursive() 0 35 7
A diffReverse() 0 8 1
A divide() 0 11 1
A each() 11 15 2
A exists() 0 15 3
A fillWithDefaults() 0 23 3
A filter() 0 12 2
B filterBy() 0 69 3
A find() 0 10 3
A findBy() 0 4 1
A first() 0 11 2
A firstsImmutable() 0 17 2
A firstsMutable() 0 13 2
A flip() 0 8 1
D get() 0 92 21
A getAll() 0 4 1
A getArray() 0 11 2
A getColumn() 0 8 1
A getGenerator() 0 8 2
A getKeys() 0 4 1
A getObject() 0 4 1
A getRandom() 0 4 1
A getRandomKey() 0 4 1
A getRandomKeys() 0 4 1
A getRandomValue() 0 4 1
A getRandomValues() 0 4 1
B group() 0 41 7
A has() 0 11 2
A implode() 0 4 1
A implodeKeys() 0 4 1
A indexBy() 0 17 3
A indexOf() 0 4 1
A initial() 0 4 1
A intersection() 0 22 3
A intersects() 0 4 1
A invoke() 0 24 3
A isAssoc() 0 14 4
A isEmpty() 0 18 5
A isEqual() 0 4 1
A isMultiArray() 0 8 1
A isNumeric() 0 14 4
A isSequential() 0 17 2
A jsonSerialize() 0 4 1
A keyExists() 0 4 1
B keys() 0 50 9
A krsort() 0 8 1
A last() 0 12 2
A lastsImmutable() 0 31 4
A lastsMutable() 0 29 4
A length() 0 4 1
A map() 0 27 5
A matches() 16 16 4
A matchesAny() 16 16 4
A max() 8 8 2
A mergeAppendKeepIndex() 10 14 2
A mergeAppendNewIndex() 10 14 2
A mergePrependKeepIndex() 10 14 2
A mergePrependNewIndex() 10 14 2
A meta() 0 4 1
A min() 8 8 2
B moveElement() 0 33 6
A only() 0 10 1
A pad() 0 8 1
A pop() 0 6 1
A prepend() 0 13 2
A prependToEachKey() 17 26 4
A prependToEachValue() 21 28 5
A pull() 0 22 4
A push() 11 11 2
A randomImmutable() 9 33 3
A randomKey() 0 10 2
A randomKeys() 0 24 3
A randomMutable() 10 25 3
A randomValue() 0 10 2
A randomValues() 0 4 1
A randomWeighted() 0 15 4
A reduce() 0 30 4
A reduce_dimension() 0 18 4
A reindex() 0 8 1
A reject() 12 17 3
A remove() 0 23 3
A removeFirst() 8 12 1
A removeLast() 8 12 1
A removeValue() 0 26 5
A repeat() 0 12 2
A replace() 0 7 1
A replaceAllKeys() 0 8 1
A replaceAllValues() 0 8 1
A replaceKeys() 0 11 1
A replaceOneValue() 0 15 2
A replaceValues() 0 10 1
A rest() 10 10 1
A reverse() 0 8 1
A rsort() 0 8 1
A searchIndex() 0 10 3
A searchValue() 0 30 4
A set() 0 8 1
A setAndGet() 0 11 2
A shift() 0 6 1
B shuffle() 0 43 8
A size() 0 4 1
A sizeIs() 14 14 3
A sizeIsBetween() 0 20 5
A sizeIsGreaterThan() 14 14 3
A sizeIsLessThan() 14 14 3
A sizeRecursive() 0 4 1
A slice() 0 13 1
A sort() 0 11 1
A sortKeys() 0 8 1
A sortValueKeepIndex() 0 4 1
A sortValueNewIndex() 0 4 1
A sorter() 0 37 3
A split() 0 19 2
A stripEmpty() 0 12 2
A swap() 0 12 1
A toArray() 0 4 1
A toJson() 0 9 2
A toString() 0 4 1
A unique() 0 18 2
A uniqueKeepIndex() 0 28 3
A uniqueNewIndex() 0 4 1
A unshift() 11 11 2
A values() 0 13 2
A walk() 0 12 2
A arrayToObject() 0 19 4
B array_keys_recursive() 0 34 7
A callAtPath() 0 29 5
A fallbackForArray() 0 12 2
A getDirection() 0 22 5
B implode_recursive() 0 30 9
B in_array_recursive() 0 25 6
D internalGetArray() 0 52 18
B internalRemove() 0 33 7
B internalSet() 0 47 10
A objectToArray() 0 12 3
A sorterKeys() 0 18 5
B sorting() 0 30 8
A generatorToArray() 0 11 2
A getPropertiesFromPhpDoc() 0 25 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Arrayy often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Arrayy, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
/** @noinspection ClassReImplementsParentInterfaceInspection */
8
9
/**
10
 * Methods to manage arrays.
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 */
15
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
16
{
17
    /**
18
     * @var array
19
     */
20
    protected $array = [];
21
22
    /**
23
     * @var ArrayyRewindableGenerator|null
24
     */
25
    protected $generator;
26
27
    /**
28
     * @var string
29
     */
30
    protected $iteratorClass = ArrayyIterator::class;
31
32
    /**
33
     * @var string
34
     */
35
    protected $pathSeparator = '.';
36
37
    /**
38
     * @var bool
39
     */
40
    protected $checkPropertyTypes = false;
41
42
    /**
43
     * @var bool
44
     */
45
    protected $checkForMissingPropertiesInConstructor = false;
46
47
    /**
48
     * @var bool
49
     */
50
    protected $checkPropertiesMismatchInConstructor = false;
51
52
    /**
53
     * @var Property[]
54
     */
55
    protected $properties = [];
56
57
    /**
58
     * Initializes
59
     *
60
     * @param mixed  $data                                   <p>
61
     *                                                       Should be an array or a generator, otherwise it will try
62
     *                                                       to convert it into an array.
63
     *                                                       </p>
64
     * @param string $iteratorClass                          optional <p>
65
     *                                                       You can overwrite the ArrayyIterator, but mostly you don't
66
     *                                                       need this option.
67
     *                                                       </p>
68
     * @param bool   $checkForMissingPropertiesInConstructor optional <p>
69
     *                                                       You need to extend the "Arrayy"-class and you need to set
70
     *                                                       the $checkPropertiesMismatchInConstructor class property
71
     *                                                       to
72
     *                                                       true, otherwise this option didn't not work anyway.
73
     *                                                       </p>
74
     */
75 947
    public function __construct(
76
        $data = [],
77
        string $iteratorClass = ArrayyIterator::class,
78
        bool $checkForMissingPropertiesInConstructor = true
79
    ) {
80 947
        $data = $this->fallbackForArray($data);
81
82
        // used only for serialize + unserialize, all other methods are overwritten
83 945
        parent::__construct([], 0, $iteratorClass);
84
85 945
        $checkForMissingPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
86
                                                  &&
87 945
                                                  $checkForMissingPropertiesInConstructor === true;
88
89
        if (
90 945
            $this->checkPropertyTypes === true
91
            ||
92 945
            $checkForMissingPropertiesInConstructor === true
93
        ) {
94 15
            $this->properties = $this->getPropertiesFromPhpDoc();
95
        }
96
97
        if (
98 945
            $this->checkPropertiesMismatchInConstructor === true
99
            &&
100 945
            \count($data) !== 0
101
            &&
102 945
            \count(\array_diff_key($this->properties, $data)) > 0
103
        ) {
104 1
            throw new \InvalidArgumentException('Property mismatch - input: ' . \print_r(\array_keys($data), true) . ' | expected: ' . \print_r(\array_keys($this->properties), true));
105
        }
106
107
        /** @noinspection AlterInForeachInspection */
108 944
        foreach ($data as $key => &$value) {
109 824
            $this->internalSet(
110 824
                $key,
111 824
                $value,
112 824
                $checkForMissingPropertiesInConstructor
113
            );
114
        }
115
116 940
        $this->setIteratorClass($iteratorClass);
117 940
    }
118
119
    /**
120
     * Call object as function.
121
     *
122
     * @param mixed $key
123
     *
124
     * @return mixed
125
     */
126 1
    public function __invoke($key = null)
127
    {
128 1
        if ($key !== null) {
129 1
            $this->generatorToArray();
130
131 1
            return $this->array[$key] ?? false;
132
        }
133
134
        return $this->getArray();
135
    }
136
137
    /**
138
     * Whether or not an element exists by key.
139
     *
140
     * @param mixed $key
141
     *
142
     * @return bool
143
     *              <p>True is the key/index exists, otherwise false.</p>
144
     */
145
    public function __isset($key): bool
146
    {
147
        return $this->offsetExists($key);
148
    }
149
150
    /**
151
     * Assigns a value to the specified element.
152
     *
153
     * @param mixed $key
154
     * @param mixed $value
155
     */
156 2
    public function __set($key, $value)
157
    {
158 2
        $this->internalSet($key, $value);
159 2
    }
160
161
    /**
162
     * magic to string
163
     *
164
     * @return string
165
     */
166 16
    public function __toString(): string
167
    {
168 16
        return $this->toString();
169
    }
170
171
    /**
172
     * Unset element by key.
173
     *
174
     * @param mixed $key
175
     */
176
    public function __unset($key)
177
    {
178
        $this->internalRemove($key);
179
    }
180
181
    /**
182
     * Get a value by key.
183
     *
184
     * @param mixed $key
185
     *
186
     * @return mixed
187
     *               <p>Get a Value from the current array.</p>
188
     */
189 4
    public function &__get($key)
190
    {
191 4
        $return = $this->get($key);
192
193 4
        if (\is_array($return)) {
194
            return static::create($return, $this->iteratorClass, false);
195
        }
196
197 4
        return $return;
198
    }
199
200
    /**
201
     * alias: for "Arrayy->append()"
202
     *
203
     * @param mixed $value
204
     *
205
     * @return static
206
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
207
     *
208
     * @see Arrayy::append()
209
     */
210 3
    public function add($value): self
211
    {
212 3
        return $this->append($value);
213
    }
214
215
    /**
216
     * Append a (key) + value to the current array.
217
     *
218
     * @param mixed $value
219
     * @param mixed $key
220
     *
221
     * @return static
222
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
223
     */
224 12
    public function append($value, $key = null): self
225
    {
226 12
        $this->generatorToArray();
227
228 12
        if ($key !== null) {
229
            if (
230
                isset($this->array[$key])
231
                &&
232
                \is_array($this->array[$key])
233
            ) {
234
                $this->array[$key][] = $value;
235
            } else {
236
                $this->array[$key] = $value;
237
            }
238
        } else {
239 12
            $this->array[] = $value;
240
        }
241
242 12
        return $this;
243
    }
244
245
    /**
246
     * Sort the entries by value.
247
     *
248
     * @param int $sort_flags [optional] <p>
249
     *                        You may modify the behavior of the sort using the optional
250
     *                        parameter sort_flags, for details
251
     *                        see sort.
252
     *                        </p>
253
     *
254
     * @return static
255
     *                <p>(Mutable) Return this Arrayy object.</p>
256
     */
257 4
    public function asort(int $sort_flags = 0): self
258
    {
259 4
        $this->generatorToArray();
260
261 4
        \asort($this->array, $sort_flags);
262
263 4
        return $this;
264
    }
265
266
    /**
267
     * Counts all elements in an array, or something in an object.
268
     *
269
     * <p>
270
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
271
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
272
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
273
     * implemented and used in PHP.
274
     * </p>
275
     *
276
     * @see http://php.net/manual/en/function.count.php
277
     *
278
     * @param int $mode [optional] If the optional mode parameter is set to
279
     *                  COUNT_RECURSIVE (or 1), count
280
     *                  will recursively count the array. This is particularly useful for
281
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
282
     *
283
     * @return int
284
     *             <p>
285
     *             The number of elements in var, which is
286
     *             typically an array, since anything else will have one
287
     *             element.
288
     *             </p>
289
     *             <p>
290
     *             If var is not an array or an object with
291
     *             implemented Countable interface,
292
     *             1 will be returned.
293
     *             There is one exception, if var is &null;,
294
     *             0 will be returned.
295
     *             </p>
296
     *             <p>
297
     *             Caution: count may return 0 for a variable that isn't set,
298
     *             but it may also return 0 for a variable that has been initialized with an
299
     *             empty array. Use isset to test if a variable is set.
300
     *             </p>
301
     */
302 46
    public function count(int $mode = \COUNT_NORMAL): int
303
    {
304 46
        return \count($this->getArray(), $mode);
305
    }
306
307
    /**
308
     * Exchange the array for another one.
309
     *
310
     * @param array|static $data
311
     *
312
     * @return array
313
     */
314 1
    public function exchangeArray($data): array
315
    {
316 1
        $this->array = $this->fallbackForArray($data);
317
318 1
        return $this->array;
319
    }
320
321
    /**
322
     * Creates a copy of the ArrayyObject.
323
     *
324
     * @return array
325
     */
326 1
    public function getArrayCopy(): array
327
    {
328 1
        $this->generatorToArray();
329
330 1
        return $this->array;
331
    }
332
333
    /**
334
     * Returns a new iterator, thus implementing the \Iterator interface.
335
     *
336
     * @return \Iterator
337
     *                   <p>An iterator for the values in the array.</p>
338
     */
339 24
    public function getIterator(): \Iterator
340
    {
341 24
        if ($this->generator instanceof ArrayyRewindableGenerator) {
342 1
            return $this->generator;
343
        }
344
345 23
        $iterator = $this->getIteratorClass();
346
347 23
        if ($iterator === ArrayyIterator::class) {
348 23
            return new $iterator($this->getArray(), 0, static::class);
349
        }
350
351
        return new $iterator($this->getArray());
352
    }
353
354
    /**
355
     * Gets the iterator classname for the ArrayObject.
356
     *
357
     * @return string
358
     */
359 23
    public function getIteratorClass(): string
360
    {
361 23
        return $this->iteratorClass;
362
    }
363
364
    /**
365
     * Sort the entries by key
366
     *
367
     * @param int $sort_flags [optional] <p>
368
     *                        You may modify the behavior of the sort using the optional
369
     *                        parameter sort_flags, for details
370
     *                        see sort.
371
     *                        </p>
372
     *
373
     * @return static
374
     *                <p>(Mutable) Return this Arrayy object.</p>
375
     */
376 4
    public function ksort(int $sort_flags = 0): self
377
    {
378 4
        $this->generatorToArray();
379
380 4
        \ksort($this->array, $sort_flags);
381
382 4
        return $this;
383
    }
384
385
    /**
386
     * Sort an array using a case insensitive "natural order" algorithm
387
     *
388
     * @return static
389
     *                <p>(Mutable) Return this Arrayy object.</p>
390
     */
391
    public function natcasesort(): self
392
    {
393
        $this->generatorToArray();
394
395
        \natcasesort($this->array);
396
397
        return $this;
398
    }
399
400
    /**
401
     * Sort entries using a "natural order" algorithm
402
     *
403
     * @return static
404
     *                <p>(Mutable) Return this Arrayy object.</p>
405
     */
406 1
    public function natsort(): self
407
    {
408 1
        $this->generatorToArray();
409
410 1
        \natsort($this->array);
411
412 1
        return $this;
413
    }
414
415
    /**
416
     * Whether or not an offset exists.
417
     *
418
     * @param bool|int|string $offset
419
     *
420
     * @return bool
421
     */
422 50
    public function offsetExists($offset): bool
423
    {
424 50
        $this->generatorToArray();
425
426 50
        if ($this->array === []) {
427 5
            return false;
428
        }
429
430
        // php cast "bool"-index into "int"-index
431 45
        if (\is_bool($offset)) {
432 1
            $offset = (int) $offset;
433
        }
434
435 45
        $tmpReturn = $this->keyExists($offset);
436
437
        if (
438 45
            $tmpReturn === true
439
            ||
440
            (
441 19
                $tmpReturn === false
442
                &&
443 45
                \strpos((string) $offset, $this->pathSeparator) === false
444
            )
445
        ) {
446 43
            return $tmpReturn;
447
        }
448
449 3
        $offsetExists = false;
450
451
        if (
452 3
            $this->pathSeparator
453
            &&
454 3
            \is_string($offset)
455
            &&
456 3
            \strpos($offset, $this->pathSeparator) !== false
457
        ) {
458 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
459 3
            if ($explodedPath !== false) {
460 3
                $lastOffset = \array_pop($explodedPath);
461 3
                if ($lastOffset !== null) {
462 3
                    $containerPath = \implode($this->pathSeparator, $explodedPath);
463
464 3
                    $this->callAtPath(
465 3
                        $containerPath,
466 3
                        static function ($container) use ($lastOffset, &$offsetExists) {
467 3
                            $offsetExists = \array_key_exists($lastOffset, $container);
468 3
                        }
469
                    );
470
                }
471
            }
472
        }
473
474 3
        return $offsetExists;
475
    }
476
477
    /**
478
     * Returns the value at specified offset.
479
     *
480
     * @param int|string $offset
481
     *
482
     * @return mixed
483
     *               <p>Will return null if the offset did not exists.</p>
484
     */
485 30
    public function offsetGet($offset)
486
    {
487 30
        return $this->offsetExists($offset) ? $this->get($offset) : null;
488
    }
489
490
    /**
491
     * Assigns a value to the specified offset.
492
     *
493
     * @param int|string|null $offset
494
     * @param mixed           $value
495
     */
496 20
    public function offsetSet($offset, $value)
497
    {
498 20
        $this->generatorToArray();
499
500 20
        if ($offset === null) {
501 4
            $this->array[] = $value;
502
        } else {
503 16
            $this->internalSet($offset, $value);
504
        }
505 20
    }
506
507
    /**
508
     * Unset an offset.
509
     *
510
     * @param int|string $offset
511
     */
512 12
    public function offsetUnset($offset)
513
    {
514 12
        $this->generatorToArray();
515
516 12
        if ($this->array === []) {
517 3
            return;
518
        }
519
520 10
        if ($this->keyExists($offset)) {
521 7
            unset($this->array[$offset]);
522
523 7
            return;
524
        }
525
526
        if (
527 5
            $this->pathSeparator
528
            &&
529 5
            \is_string($offset)
530
            &&
531 5
            \strpos($offset, $this->pathSeparator) !== false
532
        ) {
533 2
            $path = \explode($this->pathSeparator, (string) $offset);
534
535 2
            if ($path !== false) {
536 2
                $pathToUnset = \array_pop($path);
537
538 2
                $this->callAtPath(
539 2
                    \implode($this->pathSeparator, $path),
540 2
                    static function (&$offset) use ($pathToUnset) {
541 2
                        unset($offset[$pathToUnset]);
542 2
                    }
543
                );
544
            }
545
        }
546
547 5
        unset($this->array[$offset]);
548 5
    }
549
550
    /**
551
     * Serialize the current "Arrayy"-object.
552
     *
553
     * @return string
554
     */
555 2
    public function serialize(): string
556
    {
557 2
        $this->generatorToArray();
558
559 2
        return parent::serialize();
560
    }
561
562
    /**
563
     * Sets the iterator classname for the current "Arrayy"-object.
564
     *
565
     * @param string $class
566
     *
567
     * @throws \InvalidArgumentException
568
     */
569 940
    public function setIteratorClass($class)
570
    {
571 940
        if (\class_exists($class)) {
572 940
            $this->iteratorClass = $class;
573
574 940
            return;
575
        }
576
577
        if (\strpos($class, '\\') === 0) {
578
            $class = '\\' . $class;
579
            if (\class_exists($class)) {
580
                $this->iteratorClass = $class;
581
582
                return;
583
            }
584
        }
585
586
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
587
    }
588
589
    /**
590
     * Sort the entries with a user-defined comparison function and maintain key association.
591
     *
592
     * @param callable $function
593
     *
594
     * @throws \InvalidArgumentException
595
     *
596
     * @return static
597
     *                <p>(Mutable) Return this Arrayy object.</p>
598
     */
599 View Code Duplication
    public function uasort($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
600
    {
601
        if (!\is_callable($function)) {
602
            throw new \InvalidArgumentException(
603
                'Passed function must be callable'
604
            );
605
        }
606
607
        $this->generatorToArray();
608
609
        \uasort($this->array, $function);
610
611
        return $this;
612
    }
613
614
    /**
615
     * Sort the entries by keys using a user-defined comparison function.
616
     *
617
     * @param callable $function
618
     *
619
     * @throws \InvalidArgumentException
620
     *
621
     * @return static
622
     *                <p>(Mutable) Return this Arrayy object.</p>
623
     */
624 5
    public function uksort($function): self
625
    {
626 5
        return $this->customSortKeys($function);
627
    }
628
629
    /**
630
     * Unserialize an string and return the instance of the "Arrayy"-class.
631
     *
632
     * @param string $string
633
     *
634
     * @return static
635
     */
636 2
    public function unserialize($string): self
637
    {
638 2
        parent::unserialize($string);
639
640 2
        return $this;
641
    }
642
643
    /**
644
     * Append a (key) + values to the current array.
645
     *
646
     * @param array $values
647
     * @param mixed $key
648
     *
649
     * @return static
650
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
651
     */
652 1
    public function appendArrayValues(array $values, $key = null): self
653
    {
654 1
        $this->generatorToArray();
655
656 1
        if ($key !== null) {
657
            if (
658 1
                isset($this->array[$key])
659
                &&
660 1
                \is_array($this->array[$key])
661
            ) {
662 1
                foreach ($values as $value) {
663 1
                    $this->array[$key][] = $value;
664
                }
665
            } else {
666
                foreach ($values as $value) {
667 1
                    $this->array[$key] = $value;
668
                }
669
            }
670
        } else {
671
            foreach ($values as $value) {
672
                $this->array[] = $value;
673
            }
674
        }
675
676 1
        return $this;
677
    }
678
679
    /**
680
     * Add a suffix to each key.
681
     *
682
     * @param mixed $prefix
683
     *
684
     * @return static
685
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
686
     */
687 10 View Code Duplication
    public function appendToEachKey($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
688
    {
689
        // init
690 10
        $result = [];
691
692 10
        foreach ($this->getGenerator() as $key => $item) {
693 9
            if ($item instanceof self) {
694
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
695 9
            } elseif (\is_array($item)) {
696
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
697
                    ->appendToEachKey($prefix)
698
                    ->toArray();
699
            } else {
700 9
                $result[$prefix . $key] = $item;
701
            }
702
        }
703
704 10
        return self::create($result, $this->iteratorClass, false);
705
    }
706
707
    /**
708
     * Add a prefix to each value.
709
     *
710
     * @param mixed $prefix
711
     *
712
     * @return static
713
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
714
     */
715 10 View Code Duplication
    public function appendToEachValue($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
716
    {
717
        // init
718 10
        $result = [];
719
720 10
        foreach ($this->getGenerator() as $key => $item) {
721 9
            if ($item instanceof self) {
722
                $result[$key] = $item->appendToEachValue($prefix);
723 9
            } elseif (\is_array($item)) {
724
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
725 9
            } elseif (\is_object($item)) {
726 1
                $result[$key] = $item;
727
            } else {
728 9
                $result[$key] = $prefix . $item;
729
            }
730
        }
731
732 10
        return self::create($result, $this->iteratorClass, false);
733
    }
734
735
    /**
736
     * Sort an array in reverse order and maintain index association.
737
     *
738
     * @return static
739
     *                <p>(Mutable) Return this Arrayy object.</p>
740
     */
741 4
    public function arsort(): self
742
    {
743 4
        $this->generatorToArray();
744
745 4
        \arsort($this->array);
746
747 4
        return $this;
748
    }
749
750
    /**
751
     * Iterate over the current array and execute a callback for each loop.
752
     *
753
     * @param \Closure $closure
754
     *
755
     * @return static
756
     *                <p>(Immutable)</p>
757
     */
758 2
    public function at(\Closure $closure): self
759
    {
760 2
        $arrayy = clone $this;
761
762 2
        foreach ($arrayy->getGenerator() as $key => $value) {
763 2
            $closure($value, $key);
764
        }
765
766 2
        return static::create(
767 2
            $arrayy->toArray(),
768 2
            $this->iteratorClass,
769 2
            false
770
        );
771
    }
772
773
    /**
774
     * Returns the average value of the current array.
775
     *
776
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
777
     *
778
     * @return float|int
779
     *                   <p>The average value.</p>
780
     */
781 10
    public function average($decimals = 0)
782
    {
783 10
        $count = \count($this->getArray(), \COUNT_NORMAL);
784
785 10
        if (!$count) {
786 2
            return 0;
787
        }
788
789 8
        if (!\is_int($decimals)) {
790 3
            $decimals = 0;
791
        }
792
793 8
        return \round(\array_sum($this->getArray()) / $count, $decimals);
794
    }
795
796
    /**
797
     * Changes all keys in an array.
798
     *
799
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
800
     *                  or <strong>CASE_LOWER</strong> (default)</p>
801
     *
802
     * @return static
803
     *                <p>(Immutable)</p>
804
     */
805 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
806
    {
807
        if (
808 1
            $case !== \CASE_LOWER
809
            &&
810 1
            $case !== \CASE_UPPER
811
        ) {
812
            $case = \CASE_LOWER;
813
        }
814
815 1
        $return = [];
816 1
        foreach ($this->getGenerator() as $key => $value) {
817 1
            if ($case === \CASE_LOWER) {
818 1
                $key = \mb_strtolower((string) $key);
819
            } else {
820 1
                $key = \mb_strtoupper((string) $key);
821
            }
822
823 1
            $return[$key] = $value;
824
        }
825
826 1
        return static::create(
827 1
            $return,
828 1
            $this->iteratorClass,
829 1
            false
830
        );
831
    }
832
833
    /**
834
     * Change the path separator of the array wrapper.
835
     *
836
     * By default, the separator is: "."
837
     *
838
     * @param string $separator <p>Separator to set.</p>
839
     *
840
     * @return static
841
     *                <p>Mutable</p>
842
     */
843 10
    public function changeSeparator($separator): self
844
    {
845 10
        $this->pathSeparator = $separator;
846
847 10
        return $this;
848
    }
849
850
    /**
851
     * Create a chunked version of the current array.
852
     *
853
     * @param int  $size         <p>Size of each chunk.</p>
854
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
855
     *
856
     * @return static
857
     *                <p>(Immutable) A new array of chunks from the original array.</p>
858
     */
859 4
    public function chunk($size, $preserveKeys = false): self
860
    {
861 4
        return static::create(
862 4
            \array_chunk($this->getArray(), $size, $preserveKeys),
863 4
            $this->iteratorClass,
864 4
            false
865
        );
866
    }
867
868
    /**
869
     * Clean all falsy values from the current array.
870
     *
871
     * @return static
872
     *                <p>(Immutable)</p>
873
     */
874 8
    public function clean(): self
875
    {
876 8
        return $this->filter(
877 8
            static function ($value) {
878 7
                return (bool) $value;
879 8
            }
880
        );
881
    }
882
883
    /**
884
     * WARNING!!! -> Clear the current array.
885
     *
886
     * @return static
887
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
888
     */
889 4
    public function clear(): self
890
    {
891 4
        $this->array = [];
892 4
        $this->generator = null;
893
894 4
        return $this;
895
    }
896
897
    /**
898
     * Check if an item is in the current array.
899
     *
900
     * @param float|int|string $value
901
     * @param bool             $recursive
902
     * @param bool             $strict
903
     *
904
     * @return bool
905
     */
906 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
907
    {
908 23
        if ($recursive === true) {
909 19
            return $this->in_array_recursive($value, $this->getArray(), $strict);
910
        }
911
912 13
        foreach ($this->getGenerator() as $valueFromArray) {
913 10
            if ($strict) {
914 10
                if ($value === $valueFromArray) {
915 10
                    return true;
916
                }
917
            } else {
918
                /** @noinspection NestedPositiveIfStatementsInspection */
919
                if ($value == $valueFromArray) {
920 6
                    return true;
921
                }
922
            }
923
        }
924
925 6
        return false;
926
    }
927
928
    /**
929
     * Check if an (case-insensitive) string is in the current array.
930
     *
931
     * @param string $value
932
     * @param bool   $recursive
933
     *
934
     * @return bool
935
     */
936 26
    public function containsCaseInsensitive($value, $recursive = false): bool
937
    {
938 26
        if ($recursive === true) {
939 26
            foreach ($this->getGenerator() as $key => $valueTmp) {
940 22
                if (\is_array($valueTmp)) {
941 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
942 5
                    if ($return === true) {
943 5
                        return $return;
944
                    }
945 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
946 22
                    return true;
947
                }
948
            }
949
950 10
            return false;
951
        }
952
953 13
        foreach ($this->getGenerator() as $key => $valueTmp) {
954 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
955 11
                return true;
956
            }
957
        }
958
959 5
        return false;
960
    }
961
962
    /**
963
     * Check if the given key/index exists in the array.
964
     *
965
     * @param int|string $key <p>key/index to search for</p>
966
     *
967
     * @return bool
968
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
969
     */
970 4
    public function containsKey($key): bool
971
    {
972 4
        return $this->offsetExists($key);
973
    }
974
975
    /**
976
     * Check if all given needles are present in the array as key/index.
977
     *
978
     * @param array $needles   <p>The keys you are searching for.</p>
979
     * @param bool  $recursive
980
     *
981
     * @return bool
982
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
983
     */
984 2
    public function containsKeys(array $needles, $recursive = false): bool
985
    {
986 2
        if ($recursive === true) {
987
            return
988 2
                \count(
989 2
                    \array_intersect(
990 2
                        $needles,
991 2
                        $this->keys(true)->getArray()
992
                    ),
993 2
                    \COUNT_RECURSIVE
994
                )
995
                ===
996 2
                \count(
997 2
                    $needles,
998 2
                    \COUNT_RECURSIVE
999
                );
1000
        }
1001
1002 1
        return \count(
1003 1
            \array_intersect($needles, $this->keys()->getArray()),
1004 1
            \COUNT_NORMAL
1005
               )
1006
               ===
1007 1
               \count(
1008 1
                   $needles,
1009 1
                   \COUNT_NORMAL
1010
               );
1011
    }
1012
1013
    /**
1014
     * Check if all given needles are present in the array as key/index.
1015
     *
1016
     * @param array $needles <p>The keys you are searching for.</p>
1017
     *
1018
     * @return bool
1019
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1020
     */
1021 1
    public function containsKeysRecursive(array $needles): bool
1022
    {
1023 1
        return $this->containsKeys($needles, true);
1024
    }
1025
1026
    /**
1027
     * alias: for "Arrayy->contains()"
1028
     *
1029
     * @param float|int|string $value
1030
     *
1031
     * @return bool
1032
     *
1033
     * @see Arrayy::contains()
1034
     */
1035 9
    public function containsValue($value): bool
1036
    {
1037 9
        return $this->contains($value);
1038
    }
1039
1040
    /**
1041
     * alias: for "Arrayy->contains($value, true)"
1042
     *
1043
     * @param float|int|string $value
1044
     *
1045
     * @return bool
1046
     *
1047
     * @see Arrayy::contains()
1048
     */
1049 18
    public function containsValueRecursive($value): bool
1050
    {
1051 18
        return $this->contains($value, true);
1052
    }
1053
1054
    /**
1055
     * Check if all given needles are present in the array.
1056
     *
1057
     * @param array $needles
1058
     *
1059
     * @return bool
1060
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1061
     */
1062 1
    public function containsValues(array $needles): bool
1063
    {
1064 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1065
               ===
1066 1
               \count($needles, \COUNT_NORMAL);
1067
    }
1068
1069
    /**
1070
     * Counts all the values of an array
1071
     *
1072
     * @see http://php.net/manual/en/function.array-count-values.php
1073
     *
1074
     * @return static
1075
     *                <p>
1076
     *                (Immutable)
1077
     *                An associative Arrayy-object of values from input as
1078
     *                keys and their count as value.
1079
     *                </p>
1080
     */
1081 1
    public function countValues(): self
1082
    {
1083 1
        return new static(\array_count_values($this->getArray()));
1084
    }
1085
1086
    /**
1087
     * Creates an Arrayy object.
1088
     *
1089
     * @param mixed  $array
1090
     * @param string $iteratorClass
1091
     * @param bool   $checkForMissingPropertiesInConstructor
1092
     *
1093
     * @return static
1094
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1095
     */
1096 559
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1097
    {
1098 559
        return new static(
1099 559
            $array,
1100 559
            $iteratorClass,
1101 559
            $checkForMissingPropertiesInConstructor
1102
        );
1103
    }
1104
1105
    /**
1106
     * WARNING: Creates an Arrayy object by reference.
1107
     *
1108
     * @param array $array
1109
     *
1110
     * @return static
1111
     *                <p>(Mutable) Return this Arrayy object.</p>
1112
     */
1113 1
    public function createByReference(array &$array = []): self
1114
    {
1115 1
        $array = $this->fallbackForArray($array);
1116
1117 1
        $this->array = &$array;
1118
1119 1
        return $this;
1120
    }
1121
1122
    /**
1123
     * Create an new instance from a callable function which will return an Generator.
1124
     *
1125
     * @param callable():Generator $generatorFunction
0 ignored issues
show
Documentation introduced by
The doc-type callable():Generator could not be parsed: Expected "|" or "end of type", but got "(" at position 8. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1126
     *
1127
     * @return static
1128
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1129
     */
1130 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1131
    {
1132 5
        $arrayy = new static($generatorFunction);
1133
1134 5
        return $arrayy;
1135
    }
1136
1137
    /**
1138
     * Create an new instance filled with a copy of values from a "Generator"-object.
1139
     *
1140
     * @param \Generator $generator
1141
     *
1142
     * @return static
1143
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1144
     */
1145 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1146
    {
1147 4
        return new static(\iterator_to_array($generator, true));
1148
    }
1149
1150
    /**
1151
     * Create an new Arrayy object via JSON.
1152
     *
1153
     * @param string $json
1154
     *
1155
     * @return static
1156
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1157
     */
1158 5
    public static function createFromJson(string $json): self
1159
    {
1160 5
        return static::create(\json_decode($json, true));
1161
    }
1162
1163
    /**
1164
     * Create an new instance filled with values from an object that is iterable.
1165
     *
1166
     * @param \Traversable $object <p>iterable object</p>
1167
     *
1168
     * @return static
1169
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1170
     */
1171 4
    public static function createFromObject(\Traversable $object): self
1172
    {
1173
        // init
1174 4
        $array = new static();
1175
1176 4
        if ($object instanceof self) {
1177 4
            $objectArray = $object->getGenerator();
1178
        } else {
1179
            $objectArray = $object;
1180
        }
1181
1182 4
        foreach ($objectArray as $key => $value) {
1183 3
            $array[$key] = $value;
1184
        }
1185
1186 4
        return $array;
1187
    }
1188
1189
    /**
1190
     * Create an new instance filled with values from an object.
1191
     *
1192
     * @param object $object
1193
     *
1194
     * @return static
1195
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1196
     */
1197 5
    public static function createFromObjectVars($object): self
1198
    {
1199 5
        return new static(self::objectToArray($object));
1200
    }
1201
1202
    /**
1203
     * Create an new Arrayy object via string.
1204
     *
1205
     * @param string      $str       <p>The input string.</p>
1206
     * @param string|null $delimiter <p>The boundary string.</p>
1207
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1208
     *                               used.</p>
1209
     *
1210
     * @return static
1211
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1212
     */
1213 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1214
    {
1215 10
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; 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...
1216 1
            \preg_match_all($regEx, $str, $array);
1217
1218 1
            if (!empty($array)) {
1219 1
                $array = $array[0];
1220
            }
1221
        } else {
1222
            /** @noinspection NestedPositiveIfStatementsInspection */
1223 9
            if ($delimiter !== null) {
1224 7
                $array = \explode($delimiter, $str);
1225
            } else {
1226 2
                $array = [$str];
1227
            }
1228
        }
1229
1230
        // trim all string in the array
1231 10
        \array_walk(
1232
            $array,
1233 10
            static function (&$val) {
1234 10
                if (\is_string($val)) {
1235 10
                    $val = \trim($val);
1236
                }
1237 10
            }
1238
        );
1239
1240 10
        return static::create($array);
1241
    }
1242
1243
    /**
1244
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1245
     *
1246
     * @param \Traversable $traversable
1247
     *
1248
     * @return static
1249
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1250
     */
1251
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1252
    {
1253
        return new static(\iterator_to_array($traversable, true));
1254
    }
1255
1256
    /**
1257
     * Create an new instance containing a range of elements.
1258
     *
1259
     * @param mixed $low  <p>First value of the sequence.</p>
1260
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1261
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1262
     *
1263
     * @return static
1264
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1265
     */
1266 2
    public static function createWithRange($low, $high, int $step = 1): self
1267
    {
1268 2
        return static::create(\range($low, $high, $step));
1269
    }
1270
1271
    /**
1272
     * Custom sort by index via "uksort".
1273
     *
1274
     * @see http://php.net/manual/en/function.uksort.php
1275
     *
1276
     * @param callable $function
1277
     *
1278
     * @throws \InvalidArgumentException
1279
     *
1280
     * @return static
1281
     *                <p>(Mutable) Return this Arrayy object.</p>
1282
     */
1283 5 View Code Duplication
    public function customSortKeys($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1284
    {
1285 5
        if (!\is_callable($function)) {
1286
            throw new \InvalidArgumentException(
1287
                'Passed function must be callable'
1288
            );
1289
        }
1290
1291 5
        $this->generatorToArray();
1292
1293 5
        \uksort($this->array, $function);
1294
1295 5
        return $this;
1296
    }
1297
1298
    /**
1299
     * Custom sort by value via "usort".
1300
     *
1301
     * @see http://php.net/manual/en/function.usort.php
1302
     *
1303
     * @param callable $function
1304
     *
1305
     * @throws \InvalidArgumentException
1306
     *
1307
     * @return static
1308
     *                <p>(Mutable) Return this Arrayy object.</p>
1309
     */
1310 5 View Code Duplication
    public function customSortValues($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1311
    {
1312 5
        if (!\is_callable($function)) {
1313
            throw new \InvalidArgumentException(
1314
                'Passed function must be callable'
1315
            );
1316
        }
1317
1318 5
        $this->generatorToArray();
1319
1320 5
        \usort($this->array, $function);
1321
1322 5
        return $this;
1323
    }
1324
1325
    /**
1326
     * Delete the given key or keys.
1327
     *
1328
     * @param int|int[]|string|string[] $keyOrKeys
1329
     */
1330 4
    public function delete($keyOrKeys)
1331
    {
1332 4
        $keyOrKeys = (array) $keyOrKeys;
1333
1334 4
        foreach ($keyOrKeys as $key) {
1335 4
            $this->offsetUnset($key);
1336
        }
1337 4
    }
1338
1339
    /**
1340
     * Return values that are only in the current array.
1341
     *
1342
     * @param array $array
1343
     *
1344
     * @return static
1345
     *                <p>(Immutable)</p>
1346
     */
1347 12
    public function diff(array $array = []): self
1348
    {
1349 12
        return static::create(
1350 12
            \array_diff($this->getArray(), $array),
1351 12
            $this->iteratorClass,
1352 12
            false
1353
        );
1354
    }
1355
1356
    /**
1357
     * Return values that are only in the current multi-dimensional array.
1358
     *
1359
     * @param array      $array
1360
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1361
     *
1362
     * @return static
1363
     *                <p>(Immutable)</p>
1364
     */
1365 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1366
    {
1367
        // init
1368 1
        $result = [];
1369
1370
        if (
1371 1
            $helperVariableForRecursion !== null
1372
            &&
1373 1
            \is_array($helperVariableForRecursion)
1374
        ) {
1375
            $arrayForTheLoop = $helperVariableForRecursion;
1376
        } else {
1377 1
            $arrayForTheLoop = $this->getGenerator();
1378
        }
1379
1380 1
        foreach ($arrayForTheLoop as $key => $value) {
1381 1
            if ($value instanceof self) {
1382
                $value = $value->getArray();
1383
            }
1384
1385 1
            if (\array_key_exists($key, $array)) {
1386 1
                if ($value !== $array[$key]) {
1387 1
                    $result[$key] = $value;
1388
                }
1389
            } else {
1390 1
                $result[$key] = $value;
1391
            }
1392
        }
1393
1394 1
        return static::create(
1395 1
            $result,
1396 1
            $this->iteratorClass,
1397 1
            false
1398
        );
1399
    }
1400
1401
    /**
1402
     * Return values that are only in the new $array.
1403
     *
1404
     * @param array $array
1405
     *
1406
     * @return static
1407
     *                <p>(Immutable)</p>
1408
     */
1409 8
    public function diffReverse(array $array = []): self
1410
    {
1411 8
        return static::create(
1412 8
            \array_diff($array, $this->getArray()),
1413 8
            $this->iteratorClass,
1414 8
            false
1415
        );
1416
    }
1417
1418
    /**
1419
     * Divide an array into two arrays. One with keys and the other with values.
1420
     *
1421
     * @return static
1422
     *                <p>(Immutable)</p>
1423
     */
1424 1
    public function divide(): self
1425
    {
1426 1
        return static::create(
1427
            [
1428 1
                $this->keys(),
1429 1
                $this->values(),
1430
            ],
1431 1
            $this->iteratorClass,
1432 1
            false
1433
        );
1434
    }
1435
1436
    /**
1437
     * Iterate over the current array and modify the array's value.
1438
     *
1439
     * @param \Closure $closure
1440
     *
1441
     * @return static
1442
     *                <p>(Immutable)</p>
1443
     */
1444 4 View Code Duplication
    public function each(\Closure $closure): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1445
    {
1446
        // init
1447 4
        $array = [];
1448
1449 4
        foreach ($this->getGenerator() as $key => $value) {
1450 4
            $array[$key] = $closure($value, $key);
1451
        }
1452
1453 4
        return static::create(
1454 4
            $array,
1455 4
            $this->iteratorClass,
1456 4
            false
1457
        );
1458
    }
1459
1460
    /**
1461
     * Check if a value is in the current array using a closure.
1462
     *
1463
     * @param \Closure $closure
1464
     *
1465
     * @return bool
1466
     *              <p>Returns true if the given value is found, false otherwise.</p>
1467
     */
1468 4
    public function exists(\Closure $closure): bool
1469
    {
1470
        // init
1471 4
        $isExists = false;
1472
1473 4
        foreach ($this->getGenerator() as $key => $value) {
1474 3
            if ($closure($value, $key)) {
1475 1
                $isExists = true;
1476
1477 3
                break;
1478
            }
1479
        }
1480
1481 4
        return $isExists;
1482
    }
1483
1484
    /**
1485
     * Fill the array until "$num" with "$default" values.
1486
     *
1487
     * @param int   $num
1488
     * @param mixed $default
1489
     *
1490
     * @return static
1491
     *                <p>(Immutable)</p>
1492
     */
1493 8
    public function fillWithDefaults(int $num, $default = null): self
1494
    {
1495 8
        if ($num < 0) {
1496 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1497
        }
1498
1499 7
        $this->generatorToArray();
1500
1501 7
        $tmpArray = $this->array;
1502
1503 7
        $count = \count($tmpArray);
1504
1505 7
        while ($count < $num) {
1506 4
            $tmpArray[] = $default;
1507 4
            ++$count;
1508
        }
1509
1510 7
        return static::create(
1511 7
            $tmpArray,
1512 7
            $this->iteratorClass,
1513 7
            false
1514
        );
1515
    }
1516
1517
    /**
1518
     * Find all items in an array that pass the truth test.
1519
     *
1520
     * @param \Closure|null $closure [optional] <p>
1521
     *                               The callback function to use
1522
     *                               </p>
1523
     *                               <p>
1524
     *                               If no callback is supplied, all entries of
1525
     *                               input equal to false (see
1526
     *                               converting to
1527
     *                               boolean) will be removed.
1528
     *                               </p>
1529
     *                               * @param int $flag [optional] <p>
1530
     *                               Flag determining what arguments are sent to <i>callback</i>:
1531
     *                               </p><ul>
1532
     *                               <li>
1533
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1534
     *                               to <i>callback</i> instead of the value</span>
1535
     *                               </li>
1536
     *                               <li>
1537
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1538
     *                               arguments to <i>callback</i> instead of the value</span>
1539
     *                               </li>
1540
     *                               </ul>
1541
     *
1542
     * @return static
1543
     *                <p>(Immutable)</p>
1544
     */
1545 11
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1546
    {
1547 11
        if (!$closure) {
1548 1
            return $this->clean();
1549
        }
1550
1551 11
        return static::create(
1552 11
            \array_filter($this->getArray(), $closure, $flag),
1553 11
            $this->iteratorClass,
1554 11
            false
1555
        );
1556
    }
1557
1558
    /**
1559
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1560
     * property within that.
1561
     *
1562
     * @param string          $property
1563
     * @param string|string[] $value
1564
     * @param string          $comparisonOp
1565
     *                                      <p>
1566
     *                                      'eq' (equals),<br />
1567
     *                                      'gt' (greater),<br />
1568
     *                                      'gte' || 'ge' (greater or equals),<br />
1569
     *                                      'lt' (less),<br />
1570
     *                                      'lte' || 'le' (less or equals),<br />
1571
     *                                      'ne' (not equals),<br />
1572
     *                                      'contains',<br />
1573
     *                                      'notContains',<br />
1574
     *                                      'newer' (via strtotime),<br />
1575
     *                                      'older' (via strtotime),<br />
1576
     *                                      </p>
1577
     *
1578
     * @return static
1579
     *                <p>(Immutable)</p>
1580
     */
1581 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1582
    {
1583 1
        if (!$comparisonOp) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $comparisonOp of type null|string 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...
1584 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
1585
        }
1586
1587
        $ops = [
1588 1
            'eq' => static function ($item, $prop, $value) {
1589 1
                return $item[$prop] === $value;
1590 1
            },
1591 1
            'gt' => static function ($item, $prop, $value) {
1592
                return $item[$prop] > $value;
1593 1
            },
1594 1
            'ge' => static function ($item, $prop, $value) {
1595
                return $item[$prop] >= $value;
1596 1
            },
1597 1
            'gte' => static function ($item, $prop, $value) {
1598
                return $item[$prop] >= $value;
1599 1
            },
1600 1
            'lt' => static function ($item, $prop, $value) {
1601 1
                return $item[$prop] < $value;
1602 1
            },
1603 1
            'le' => static function ($item, $prop, $value) {
1604
                return $item[$prop] <= $value;
1605 1
            },
1606 1
            'lte' => static function ($item, $prop, $value) {
1607
                return $item[$prop] <= $value;
1608 1
            },
1609 1
            'ne' => static function ($item, $prop, $value) {
1610
                return $item[$prop] !== $value;
1611 1
            },
1612 1
            'contains' => static function ($item, $prop, $value) {
1613 1
                return \in_array($item[$prop], (array) $value, true);
1614 1
            },
1615 1
            'notContains' => static function ($item, $prop, $value) {
1616
                return !\in_array($item[$prop], (array) $value, true);
1617 1
            },
1618 1
            'newer' => static function ($item, $prop, $value) {
1619
                return \strtotime($item[$prop]) > \strtotime($value);
1620 1
            },
1621 1
            'older' => static function ($item, $prop, $value) {
1622
                return \strtotime($item[$prop]) < \strtotime($value);
1623 1
            },
1624
        ];
1625
1626 1
        $result = \array_values(
1627 1
            \array_filter(
1628 1
                $this->getArray(),
1629 1
                static function ($item) use (
1630 1
                    $property,
1631 1
                    $value,
1632 1
                    $ops,
1633 1
                    $comparisonOp
1634
                ) {
1635 1
                    $item = (array) $item;
1636 1
                    $itemArrayy = new static($item);
1637 1
                    $item[$property] = $itemArrayy->get($property, []);
1638
1639 1
                    return $ops[$comparisonOp]($item, $property, $value);
1640 1
                }
1641
            )
1642
        );
1643
1644 1
        return static::create(
1645 1
            $result,
1646 1
            $this->iteratorClass,
1647 1
            false
1648
        );
1649
    }
1650
1651
    /**
1652
     * Find the first item in an array that passes the truth test,
1653
     *  otherwise return false
1654
     *
1655
     * @param \Closure $closure
1656
     *
1657
     * @return false|mixed
1658
     *                     <p>Return false if we did not find the value.</p>
1659
     */
1660 8
    public function find(\Closure $closure)
1661
    {
1662 8
        foreach ($this->getGenerator() as $key => $value) {
1663 6
            if ($closure($value, $key)) {
1664 6
                return $value;
1665
            }
1666
        }
1667
1668 3
        return false;
1669
    }
1670
1671
    /**
1672
     * find by ...
1673
     *
1674
     * @param string          $property
1675
     * @param string|string[] $value
1676
     * @param string          $comparisonOp
1677
     *
1678
     * @return static
1679
     *                <p>(Immutable)</p>
1680
     */
1681
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1682
    {
1683
        return $this->filterBy($property, $value, $comparisonOp);
1684
    }
1685
1686
    /**
1687
     * Get the first value from the current array.
1688
     *
1689
     * @return mixed
1690
     *               <p>Return null if there wasn't a element.</p>
1691
     */
1692 14
    public function first()
1693
    {
1694 14
        $tmpArray = $this->getArray();
1695
1696 14
        $key_first = array_key_first($tmpArray);
1697 14
        if ($key_first === null) {
1698 2
            return null;
1699
        }
1700
1701 12
        return $tmpArray[$key_first];
1702
    }
1703
1704
    /**
1705
     * Get the first value(s) from the current array.
1706
     * And will return an empty array if there was no first entry.
1707
     *
1708
     * @param int|null $number <p>How many values you will take?</p>
1709
     *
1710
     * @return static
1711
     *                <p>(Immutable)</p>
1712
     */
1713 36
    public function firstsImmutable(int $number = null): self
1714
    {
1715 36
        $arrayTmp = $this->getArray();
1716
1717 36
        if ($number === null) {
1718 14
            $array = (array) \array_shift($arrayTmp);
1719
        } else {
1720 22
            $number = (int) $number;
1721 22
            $array = \array_splice($arrayTmp, 0, $number);
1722
        }
1723
1724 36
        return static::create(
1725 36
            $array,
1726 36
            $this->iteratorClass,
1727 36
            false
1728
        );
1729
    }
1730
1731
    /**
1732
     * Get and rmove the first value(s) from the current array.
1733
     * And will return an empty array if there was no first entry.
1734
     *
1735
     * @param int|null $number <p>How many values you will take?</p>
1736
     *
1737
     * @return static
1738
     *                <p>(Mutable)</p>
1739
     */
1740 34
    public function firstsMutable(int $number = null): self
1741
    {
1742 34
        $this->generatorToArray();
1743
1744 34
        if ($number === null) {
1745 19
            $this->array = (array) \array_shift($this->array);
1746
        } else {
1747 15
            $number = (int) $number;
1748 15
            $this->array = \array_splice($this->array, 0, $number);
1749
        }
1750
1751 34
        return $this;
1752
    }
1753
1754
    /**
1755
     * Exchanges all keys with their associated values in an array.
1756
     *
1757
     * @return static
1758
     *                <p>(Immutable)</p>
1759
     */
1760 1
    public function flip(): self
1761
    {
1762 1
        return static::create(
1763 1
            \array_flip($this->getArray()),
1764 1
            $this->iteratorClass,
1765 1
            false
1766
        );
1767
    }
1768
1769
    /**
1770
     * Get a value from an array (optional using dot-notation).
1771
     *
1772
     * @param mixed $key      <p>The key to look for.</p>
1773
     * @param mixed $fallback <p>Value to fallback to.</p>
1774
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1775
     *                        class.</p>
1776
     *
1777
     * @return mixed|static
1778
     */
1779 85
    public function get($key, $fallback = null, array $array = null)
1780
    {
1781 85
        if ($array !== null) {
1782 5
            $usedArray = $array;
1783
        } else {
1784 80
            $this->generatorToArray();
1785
1786 80
            $usedArray = $this->array;
1787
        }
1788
1789 85
        if ($key === null) {
1790 1
            return static::create(
1791 1
                $usedArray,
1792 1
                $this->iteratorClass,
1793 1
                false
1794
            );
1795
        }
1796
1797
        // php cast "bool"-index into "int"-index
1798 85
        if ((bool) $key === $key) {
1799 3
            $key = (int) $key;
1800
        }
1801
1802 85
        if (\array_key_exists($key, $usedArray) === true) {
1803 74
            if (\is_array($usedArray[$key])) {
1804 11
                return static::create(
1805 11
                    $usedArray[$key],
1806 11
                    $this->iteratorClass,
1807 11
                    false
1808
                );
1809
            }
1810
1811 65
            return $usedArray[$key];
1812
        }
1813
1814
        // crawl through array, get key according to object or not
1815 24
        $usePath = false;
1816
        if (
1817 24
            $this->pathSeparator
1818
            &&
1819 24
            \is_string($key)
1820
            &&
1821 24
            \strpos($key, $this->pathSeparator) !== false
1822
        ) {
1823 7
            $segments = \explode($this->pathSeparator, (string) $key);
1824 7
            if ($segments !== false) {
1825 7
                $usePath = true;
1826
1827 7
                foreach ($segments as $segment) {
1828
                    if (
1829
                        (
1830 7
                            \is_array($usedArray)
1831
                            ||
1832 7
                            $usedArray instanceof \ArrayAccess
1833
                        )
1834
                        &&
1835 7
                        isset($usedArray[$segment])
1836
                    ) {
1837 7
                        $usedArray = $usedArray[$segment];
1838
1839 7
                        continue;
1840
                    }
1841
1842
                    if (
1843 6
                        \is_object($usedArray)
1844
                        &&
1845 6
                        \property_exists($usedArray, $segment)
1846
                    ) {
1847 1
                        $usedArray = $usedArray->{$segment};
1848
1849 1
                        continue;
1850
                    }
1851
1852 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
1853
                }
1854
            }
1855
        }
1856
1857 24
        if (!$usePath && !isset($usedArray[$key])) {
1858 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
1859
        }
1860
1861 7
        if (\is_array($usedArray)) {
1862 1
            return static::create(
1863 1
                $usedArray,
1864 1
                $this->iteratorClass,
1865 1
                false
1866
            );
1867
        }
1868
1869 7
        return $usedArray;
1870
    }
1871
1872
    /**
1873
     * alias: for "Arrayy->getArray()"
1874
     *
1875
     * @return array
1876
     *
1877
     * @see Arrayy::getArray()
1878
     */
1879
    public function getAll(): array
1880
    {
1881
        return $this->getArray();
1882
    }
1883
1884
    /**
1885
     * Get the current array from the "Arrayy"-object.
1886
     *
1887
     * @return array
1888
     */
1889 785
    public function getArray(): array
1890
    {
1891
        // init
1892 785
        $array = [];
1893
1894 785
        foreach ($this->getGenerator() as $key => $value) {
1895 679
            $array[$key] = $value;
1896
        }
1897
1898 785
        return $array;
1899
    }
1900
1901
    /**
1902
     * Returns the values from a single column of the input array, identified by
1903
     * the $columnKey, can be used to extract data-columns from multi-arrays.
1904
     *
1905
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
1906
     * array by the values from the $indexKey column in the input array.
1907
     *
1908
     * @param mixed $columnKey
1909
     * @param mixed $indexKey
1910
     *
1911
     * @return static
1912
     *                <p>(Immutable)</p>
1913
     */
1914 1
    public function getColumn($columnKey = null, $indexKey = null): self
1915
    {
1916 1
        return static::create(
1917 1
            \array_column($this->getArray(), $columnKey, $indexKey),
1918 1
            $this->iteratorClass,
1919 1
            false
1920
        );
1921
    }
1922
1923
    /**
1924
     * Get the current array from the "Arrayy"-object as generator.
1925
     *
1926
     * @return \Generator
1927
     */
1928 860
    public function getGenerator(): \Generator
1929
    {
1930 860
        if ($this->generator instanceof ArrayyRewindableGenerator) {
1931 36
            yield from $this->generator;
1932
        }
1933
1934 860
        yield from $this->array;
1935 817
    }
1936
1937
    /**
1938
     * alias: for "Arrayy->keys()"
1939
     *
1940
     * @return static
1941
     *                <p>(Immutable)</p>
1942
     *
1943
     * @see Arrayy::keys()
1944
     */
1945 1
    public function getKeys(): self
1946
    {
1947 1
        return $this->keys();
1948
    }
1949
1950
    /**
1951
     * Get the current array from the "Arrayy"-object as object.
1952
     *
1953
     * @return \stdClass
1954
     */
1955 4
    public function getObject(): \stdClass
1956
    {
1957 4
        return self::arrayToObject($this->getArray());
1958
    }
1959
1960
    /**
1961
     * alias: for "Arrayy->randomImmutable()"
1962
     *
1963
     * @return static
1964
     *                <p>(Immutable)</p>
1965
     *
1966
     * @see Arrayy::randomImmutable()
1967
     */
1968 4
    public function getRandom(): self
1969
    {
1970 4
        return $this->randomImmutable();
1971
    }
1972
1973
    /**
1974
     * alias: for "Arrayy->randomKey()"
1975
     *
1976
     * @return mixed
1977
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
1978
     *
1979
     * @see Arrayy::randomKey()
1980
     */
1981 3
    public function getRandomKey()
1982
    {
1983 3
        return $this->randomKey();
1984
    }
1985
1986
    /**
1987
     * alias: for "Arrayy->randomKeys()"
1988
     *
1989
     * @param int $number
1990
     *
1991
     * @return static
1992
     *                <p>(Immutable)</p>
1993
     *
1994
     * @see Arrayy::randomKeys()
1995
     */
1996 8
    public function getRandomKeys(int $number): self
1997
    {
1998 8
        return $this->randomKeys($number);
1999
    }
2000
2001
    /**
2002
     * alias: for "Arrayy->randomValue()"
2003
     *
2004
     * @return mixed
2005
     *               <p>Get a random value or null if there wasn't a value.</p>
2006
     *
2007
     * @see Arrayy::randomValue()
2008
     */
2009 3
    public function getRandomValue()
2010
    {
2011 3
        return $this->randomValue();
2012
    }
2013
2014
    /**
2015
     * alias: for "Arrayy->randomValues()"
2016
     *
2017
     * @param int $number
2018
     *
2019
     * @return static
2020
     *                <p>(Immutable)</p>
2021
     *
2022
     * @see Arrayy::randomValues()
2023
     */
2024 6
    public function getRandomValues(int $number): self
2025
    {
2026 6
        return $this->randomValues($number);
2027
    }
2028
2029
    /**
2030
     * Group values from a array according to the results of a closure.
2031
     *
2032
     * @param callable|string $grouper  <p>A callable function name.</p>
2033
     * @param bool            $saveKeys
2034
     *
2035
     * @return static
2036
     *                <p>(Immutable)</p>
2037
     */
2038 4
    public function group($grouper, bool $saveKeys = false): self
2039
    {
2040
        // init
2041 4
        $result = [];
2042
2043
        // Iterate over values, group by property/results from closure.
2044 4
        foreach ($this->getGenerator() as $key => $value) {
2045 4
            if (\is_callable($grouper)) {
2046 3
                $groupKey = $grouper($value, $key);
2047
            } else {
2048 1
                $groupKey = $this->get($grouper, null, $this->getArray());
2049
            }
2050
2051 4
            $newValue = $this->get($groupKey, null, $result);
2052
2053 4
            if ($groupKey instanceof self) {
2054
                $groupKey = $groupKey->getArray();
2055
            }
2056
2057 4
            if ($newValue instanceof self) {
2058 4
                $newValue = $newValue->getArray();
2059
            }
2060
2061
            // Add to results.
2062 4
            if ($groupKey !== null) {
2063 3
                if ($saveKeys) {
2064 2
                    $result[$groupKey] = $newValue;
2065 2
                    $result[$groupKey][$key] = $value;
2066
                } else {
2067 1
                    $result[$groupKey] = $newValue;
2068 4
                    $result[$groupKey][] = $value;
2069
                }
2070
            }
2071
        }
2072
2073 4
        return static::create(
2074 4
            $result,
2075 4
            $this->iteratorClass,
2076 4
            false
2077
        );
2078
    }
2079
2080
    /**
2081
     * Check if an array has a given key.
2082
     *
2083
     * @param mixed $key
2084
     *
2085
     * @return bool
2086
     */
2087 23
    public function has($key): bool
2088
    {
2089 23
        static $UN_FOUND = null;
2090
2091 23
        if ($UN_FOUND === null) {
2092
            // Generate unique string to use as marker.
2093 1
            $UN_FOUND = \uniqid('arrayy', true);
2094
        }
2095
2096 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2097
    }
2098
2099
    /**
2100
     * Implodes the values of this array.
2101
     *
2102
     * @param string $glue
2103
     *
2104
     * @return string
2105
     */
2106 28
    public function implode(string $glue = ''): string
2107
    {
2108 28
        return $this->implode_recursive($glue, $this->getArray(), false);
2109
    }
2110
2111
    /**
2112
     * Implodes the keys of this array.
2113
     *
2114
     * @param string $glue
2115
     *
2116
     * @return string
2117
     */
2118 8
    public function implodeKeys(string $glue = ''): string
2119
    {
2120 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2121
    }
2122
2123
    /**
2124
     * Given a list and an iterate-function that returns
2125
     * a key for each element in the list (or a property name),
2126
     * returns an object with an index of each item.
2127
     *
2128
     * @param mixed $key
2129
     *
2130
     * @return static
2131
     *                <p>(Immutable)</p>
2132
     */
2133 4
    public function indexBy($key): self
2134
    {
2135
        // init
2136 4
        $results = [];
2137
2138 4
        foreach ($this->getGenerator() as $a) {
2139 4
            if (\array_key_exists($key, $a) === true) {
2140 4
                $results[$a[$key]] = $a;
2141
            }
2142
        }
2143
2144 4
        return static::create(
2145 4
            $results,
2146 4
            $this->iteratorClass,
2147 4
            false
2148
        );
2149
    }
2150
2151
    /**
2152
     * alias: for "Arrayy->searchIndex()"
2153
     *
2154
     * @param mixed $value <p>The value to search for.</p>
2155
     *
2156
     * @return mixed
2157
     *
2158
     * @see Arrayy::searchIndex()
2159
     */
2160 4
    public function indexOf($value)
2161
    {
2162 4
        return $this->searchIndex($value);
2163
    }
2164
2165
    /**
2166
     * Get everything but the last..$to items.
2167
     *
2168
     * @param int $to
2169
     *
2170
     * @return static
2171
     *                <p>(Immutable)</p>
2172
     */
2173 12
    public function initial(int $to = 1): self
2174
    {
2175 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2176
    }
2177
2178
    /**
2179
     * Return an array with all elements found in input array.
2180
     *
2181
     * @param array $search
2182
     * @param bool  $keepKeys
2183
     *
2184
     * @return static
2185
     *                <p>(Immutable)</p>
2186
     */
2187 3
    public function intersection(array $search, bool $keepKeys = false): self
2188
    {
2189 3
        if ($keepKeys) {
2190 1
            return static::create(
2191 1
                \array_uintersect(
2192 1
                    $this->array,
2193 1
                    $search,
2194 1
                    static function ($a, $b) {
2195 1
                        return $a === $b ? 0 : -1;
2196 1
                    }
2197
                ),
2198 1
                $this->iteratorClass,
2199 1
                false
2200
            );
2201
        }
2202
2203 2
        return static::create(
2204 2
            \array_values(\array_intersect($this->getArray(), $search)),
2205 2
            $this->iteratorClass,
2206 2
            false
2207
        );
2208
    }
2209
2210
    /**
2211
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2212
     *
2213
     * @param array $search
2214
     *
2215
     * @return bool
2216
     */
2217 1
    public function intersects(array $search): bool
2218
    {
2219 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2220
    }
2221
2222
    /**
2223
     * Invoke a function on all of an array's values.
2224
     *
2225
     * @param callable $callable
2226
     * @param mixed    $arguments
2227
     *
2228
     * @return static
2229
     *                <p>(Immutable)</p>
2230
     */
2231 1
    public function invoke($callable, $arguments = []): self
2232
    {
2233
        // If one argument given for each iteration, create an array for it.
2234 1
        if (!\is_array($arguments)) {
2235 1
            $arguments = \array_fill(
2236 1
                0,
2237 1
                \count($this->getArray(), \COUNT_NORMAL),
2238 1
                $arguments
2239
            );
2240
        }
2241
2242
        // If the callable has arguments, pass them.
2243 1
        if ($arguments) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arguments of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2244 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2245
        } else {
2246 1
            $array = $this->map($callable);
2247
        }
2248
2249 1
        return static::create(
2250 1
            $array,
2251 1
            $this->iteratorClass,
2252 1
            false
2253
        );
2254
    }
2255
2256
    /**
2257
     * Check whether array is associative or not.
2258
     *
2259
     * @param bool $recursive
2260
     *
2261
     * @return bool
2262
     *              <p>Returns true if associative, false otherwise.</p>
2263
     */
2264 15
    public function isAssoc(bool $recursive = false): bool
2265
    {
2266 15
        if ($this->isEmpty()) {
2267 3
            return false;
2268
        }
2269
2270 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2271 13
            if (!\is_string($key)) {
2272 13
                return false;
2273
            }
2274
        }
2275
2276 3
        return true;
2277
    }
2278
2279
    /**
2280
     * Check if a given key or keys are empty.
2281
     *
2282
     * @param int|int[]|string|string[]|null $keys
2283
     *
2284
     * @return bool
2285
     *              <p>Returns true if empty, false otherwise.</p>
2286
     */
2287 38
    public function isEmpty($keys = null): bool
2288
    {
2289 38
        if ($this->generator) {
2290
            return $this->getArray() === [];
2291
        }
2292
2293 38
        if ($keys === null) {
2294 38
            return $this->array === [];
2295
        }
2296
2297
        foreach ((array) $keys as $key) {
2298
            if (!empty($this->get($key))) {
2299
                return false;
2300
            }
2301
        }
2302
2303
        return true;
2304
    }
2305
2306
    /**
2307
     * Check if the current array is equal to the given "$array" or not.
2308
     *
2309
     * @param array $array
2310
     *
2311
     * @return bool
2312
     */
2313
    public function isEqual(array $array): bool
2314
    {
2315
        return $this->getArray() === $array;
2316
    }
2317
2318
    /**
2319
     * Check if the current array is a multi-array.
2320
     *
2321
     * @return bool
2322
     */
2323 14
    public function isMultiArray(): bool
2324
    {
2325
        return !(
2326 14
            \count($this->getArray(), \COUNT_NORMAL)
2327
            ===
2328 14
            \count($this->getArray(), \COUNT_RECURSIVE)
2329
        );
2330
    }
2331
2332
    /**
2333
     * Check whether array is numeric or not.
2334
     *
2335
     * @return bool
2336
     *              <p>Returns true if numeric, false otherwise.</p>
2337
     */
2338 5
    public function isNumeric(): bool
2339
    {
2340 5
        if ($this->isEmpty()) {
2341 2
            return false;
2342
        }
2343
2344 4
        foreach ($this->keys()->getGenerator() as $key) {
2345 4
            if (!\is_int($key)) {
2346 4
                return false;
2347
            }
2348
        }
2349
2350 2
        return true;
2351
    }
2352
2353
    /**
2354
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2355
     *
2356
     * @param bool $recursive
2357
     *
2358
     * @return bool
2359
     */
2360 1
    public function isSequential(bool $recursive = false): bool
2361
    {
2362
2363
        // recursive
2364
2365 1
        if ($recursive === true) {
2366
            return $this->array_keys_recursive($this->getArray())
2367
                   ===
2368
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2369
        }
2370
2371
        // non recursive
2372
2373 1
        return \array_keys($this->getArray())
2374
               ===
2375 1
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2376
    }
2377
2378
    /**
2379
     * @return array
2380
     */
2381
    public function jsonSerialize(): array
2382
    {
2383
        return $this->getArray();
2384
    }
2385
2386
    /**
2387
     * Checks if the given key exists in the provided array.
2388
     *
2389
     * @param int|string $key the key to look for
2390
     *
2391
     * @return bool
2392
     */
2393 47
    public function keyExists($key): bool
2394
    {
2395 47
        return \array_key_exists($key, $this->array);
2396
    }
2397
2398
    /**
2399
     * Get all keys from the current array.
2400
     *
2401
     * @param bool  $recursive    [optional] <p>
2402
     *                            Get all keys, also from all sub-arrays from an multi-dimensional array.
2403
     *                            </p>
2404
     * @param mixed $search_value [optional] <p>
2405
     *                            If specified, then only keys containing these values are returned.
2406
     *                            </p>
2407
     * @param bool  $strict       [optional] <p>
2408
     *                            Determines if strict comparison (===) should be used during the search.
2409
     *                            </p>
2410
     *
2411
     * @return static
2412
     *                <p>(Immutable) An array of all the keys in input.</p>
2413
     */
2414 26
    public function keys(bool $recursive = false, $search_value = null, bool $strict = true): self
2415
    {
2416
2417
        // recursive
2418
2419 26
        if ($recursive === true) {
2420 3
            if ($search_value === null) {
2421 3
                $array = $this->array_keys_recursive($this->getArray());
2422
            } else {
2423
                $array = $this->array_keys_recursive($this->getArray(), $search_value, $strict);
2424
            }
2425
2426 3
            return static::create(
2427 3
                $array,
2428 3
                $this->iteratorClass,
2429 3
                false
2430
            );
2431
        }
2432
2433
        // non recursive
2434
2435 25
        if ($search_value === null) {
2436 25
            $arrayFunction = function () {
2437 25
                foreach ($this->getGenerator() as $key => $value) {
2438 24
                    yield $key;
2439
                }
2440 25
            };
2441
        } else {
2442
            $arrayFunction = function () use ($search_value, $strict) {
2443
                foreach ($this->getGenerator() as $key => $value) {
2444
                    if ($strict) {
2445
                        if ($search_value === $value) {
2446
                            yield $key;
2447
                        }
2448
                    } else {
2449
                        /** @noinspection NestedPositiveIfStatementsInspection */
2450
                        if ($search_value == $value) {
2451
                            yield $key;
2452
                        }
2453
                    }
2454
                }
2455
            };
2456
        }
2457
2458 25
        return static::create(
2459 25
            $arrayFunction,
2460 25
            $this->iteratorClass,
2461 25
            false
2462
        );
2463
    }
2464
2465
    /**
2466
     * Sort an array by key in reverse order.
2467
     *
2468
     * @param int $sort_flags [optional] <p>
2469
     *                        You may modify the behavior of the sort using the optional
2470
     *                        parameter sort_flags, for details
2471
     *                        see sort.
2472
     *                        </p>
2473
     *
2474
     * @return static
2475
     *                <p>(Mutable) Return this Arrayy object.</p>
2476
     */
2477 4
    public function krsort(int $sort_flags = 0): self
2478
    {
2479 4
        $this->generatorToArray();
2480
2481 4
        \krsort($this->array, $sort_flags);
2482
2483 4
        return $this;
2484
    }
2485
2486
    /**
2487
     * Get the last value from the current array.
2488
     *
2489
     * @return mixed
2490
     *               <p>Return null if there wasn't a element.</p>
2491
     */
2492 12
    public function last()
2493
    {
2494 12
        $this->generatorToArray();
2495
2496 12
        $key_last = \array_key_last($this->array);
2497
2498 12
        if ($key_last === null) {
2499 1
            return null;
2500
        }
2501
2502 11
        return $this->get($key_last);
2503
    }
2504
2505
    /**
2506
     * Get the last value(s) from the current array.
2507
     *
2508
     * @param int|null $number
2509
     *
2510
     * @return static
2511
     *                <p>(Immutable)</p>
2512
     */
2513 13
    public function lastsImmutable(int $number = null): self
2514
    {
2515 13
        if ($this->isEmpty()) {
2516 1
            return static::create(
2517 1
                [],
2518 1
                $this->iteratorClass,
2519 1
                false
2520
            );
2521
        }
2522
2523 12
        if ($number === null) {
2524 8
            $poppedValue = $this->last();
2525
2526 8
            if ($poppedValue === null) {
2527 1
                $poppedValue = [$poppedValue];
2528
            } else {
2529 7
                $poppedValue = (array) $poppedValue;
2530
            }
2531
2532 8
            $arrayy = static::create(
2533 8
                $poppedValue,
2534 8
                $this->iteratorClass,
2535 8
                false
2536
            );
2537
        } else {
2538 4
            $number = (int) $number;
2539 4
            $arrayy = $this->rest(-$number);
2540
        }
2541
2542 12
        return $arrayy;
2543
    }
2544
2545
    /**
2546
     * Get the last value(s) from the current array.
2547
     *
2548
     * @param int|null $number
2549
     *
2550
     * @return static
2551
     *                <p>(Mutable)</p>
2552
     */
2553 13
    public function lastsMutable(int $number = null): self
2554
    {
2555 13
        if ($this->isEmpty()) {
2556 1
            return $this;
2557
        }
2558
2559 12
        if ($number === null) {
2560 8
            $poppedValue = $this->last();
2561
2562 8
            if ($poppedValue === null) {
2563 1
                $poppedValue = [$poppedValue];
2564
            } else {
2565 7
                $poppedValue = (array) $poppedValue;
2566
            }
2567
2568 8
            $this->array = static::create(
2569 8
                $poppedValue,
2570 8
                $this->iteratorClass,
2571 8
                false
2572 8
            )->getArray();
2573
        } else {
2574 4
            $number = (int) $number;
2575 4
            $this->array = $this->rest(-$number)->getArray();
2576
        }
2577
2578 12
        $this->generator = null;
2579
2580 12
        return $this;
2581
    }
2582
2583
    /**
2584
     * Count the values from the current array.
2585
     *
2586
     * alias: for "Arrayy->count()"
2587
     *
2588
     * @param int $mode
2589
     *
2590
     * @return int
2591
     *
2592
     * @see Arrayy::count()
2593
     */
2594 20
    public function length(int $mode = \COUNT_NORMAL): int
2595
    {
2596 20
        return $this->count($mode);
2597
    }
2598
2599
    /**
2600
     * Apply the given function to the every element of the array,
2601
     * collecting the results.
2602
     *
2603
     * @param callable $callable
2604
     * @param bool     $useKeyAsSecondParameter
2605
     * @param mixed    ...$arguments
2606
     *
2607
     * @return static
2608
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2609
     */
2610 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments): self
2611
    {
2612 5
        $useArguments = \func_num_args() > 2;
2613
2614 5
        return static::create(
2615 5
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2616 5
                foreach ($this->getGenerator() as $key => $value) {
2617 4
                    if ($useArguments) {
2618 3
                        if ($useKeyAsSecondParameter) {
2619
                            yield $key => $callable($value, $key, ...$arguments);
2620
                        } else {
2621 3
                            yield $key => $callable($value, ...$arguments);
2622
                        }
2623
                    } else {
2624
                        /** @noinspection NestedPositiveIfStatementsInspection */
2625 4
                        if ($useKeyAsSecondParameter) {
2626
                            yield $key => $callable($value, $key);
2627
                        } else {
2628 4
                            yield $key => $callable($value);
2629
                        }
2630
                    }
2631
                }
2632 5
            },
2633 5
            $this->iteratorClass,
2634 5
            false
2635
        );
2636
    }
2637
2638
    /**
2639
     * Check if all items in current array match a truth test.
2640
     *
2641
     * @param \Closure $closure
2642
     *
2643
     * @return bool
2644
     */
2645 15 View Code Duplication
    public function matches(\Closure $closure): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2646
    {
2647 15
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2648 2
            return false;
2649
        }
2650
2651 13
        foreach ($this->getGenerator() as $key => $value) {
2652 13
            $value = $closure($value, $key);
2653
2654 13
            if ($value === false) {
2655 13
                return false;
2656
            }
2657
        }
2658
2659 7
        return true;
2660
    }
2661
2662
    /**
2663
     * Check if any item in the current array matches a truth test.
2664
     *
2665
     * @param \Closure $closure
2666
     *
2667
     * @return bool
2668
     */
2669 14 View Code Duplication
    public function matchesAny(\Closure $closure): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2670
    {
2671 14
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2672 2
            return false;
2673
        }
2674
2675 12
        foreach ($this->getGenerator() as $key => $value) {
2676 12
            $value = $closure($value, $key);
2677
2678 12
            if ($value === true) {
2679 12
                return true;
2680
            }
2681
        }
2682
2683 4
        return false;
2684
    }
2685
2686
    /**
2687
     * Get the max value from an array.
2688
     *
2689
     * @return mixed
2690
     */
2691 10 View Code Duplication
    public function max()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2692
    {
2693 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2694 1
            return false;
2695
        }
2696
2697 9
        return \max($this->getArray());
2698
    }
2699
2700
    /**
2701
     * Merge the new $array into the current array.
2702
     *
2703
     * - keep key,value from the current array, also if the index is in the new $array
2704
     *
2705
     * @param array $array
2706
     * @param bool  $recursive
2707
     *
2708
     * @return static
2709
     *                <p>(Immutable)</p>
2710
     */
2711 25 View Code Duplication
    public function mergeAppendKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2712
    {
2713 25
        if ($recursive === true) {
2714 4
            $result = \array_replace_recursive($this->getArray(), $array);
2715
        } else {
2716 21
            $result = \array_replace($this->getArray(), $array);
2717
        }
2718
2719 25
        return static::create(
2720 25
            $result,
2721 25
            $this->iteratorClass,
2722 25
            false
2723
        );
2724
    }
2725
2726
    /**
2727
     * Merge the new $array into the current array.
2728
     *
2729
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2730
     * - create new indexes
2731
     *
2732
     * @param array $array
2733
     * @param bool  $recursive
2734
     *
2735
     * @return static
2736
     *                <p>(Immutable)</p>
2737
     */
2738 16 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2739
    {
2740 16
        if ($recursive === true) {
2741 4
            $result = \array_merge_recursive($this->getArray(), $array);
2742
        } else {
2743 12
            $result = \array_merge($this->getArray(), $array);
2744
        }
2745
2746 16
        return static::create(
2747 16
            $result,
2748 16
            $this->iteratorClass,
2749 16
            false
2750
        );
2751
    }
2752
2753
    /**
2754
     * Merge the the current array into the $array.
2755
     *
2756
     * - use key,value from the new $array, also if the index is in the current array
2757
     *
2758
     * @param array $array
2759
     * @param bool  $recursive
2760
     *
2761
     * @return static
2762
     *                <p>(Immutable)</p>
2763
     */
2764 16 View Code Duplication
    public function mergePrependKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2765
    {
2766 16
        if ($recursive === true) {
2767 4
            $result = \array_replace_recursive($array, $this->getArray());
2768
        } else {
2769 12
            $result = \array_replace($array, $this->getArray());
2770
        }
2771
2772 16
        return static::create(
2773 16
            $result,
2774 16
            $this->iteratorClass,
2775 16
            false
2776
        );
2777
    }
2778
2779
    /**
2780
     * Merge the current array into the new $array.
2781
     *
2782
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2783
     * - create new indexes
2784
     *
2785
     * @param array $array
2786
     * @param bool  $recursive
2787
     *
2788
     * @return static
2789
     *                <p>(Immutable)</p>
2790
     */
2791 17 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2792
    {
2793 17
        if ($recursive === true) {
2794 4
            $result = \array_merge_recursive($array, $this->getArray());
2795
        } else {
2796 13
            $result = \array_merge($array, $this->getArray());
2797
        }
2798
2799 17
        return static::create(
2800 17
            $result,
2801 17
            $this->iteratorClass,
2802 17
            false
2803
        );
2804
    }
2805
2806
    /**
2807
     * @return ArrayyMeta|static
2808
     */
2809 14
    public static function meta()
2810
    {
2811 14
        return (new ArrayyMeta())->getMetaObject(static::class);
2812
    }
2813
2814
    /**
2815
     * Get the min value from an array.
2816
     *
2817
     * @return mixed
2818
     */
2819 10 View Code Duplication
    public function min()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2820
    {
2821 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2822 1
            return false;
2823
        }
2824
2825 9
        return \min($this->getArray());
2826
    }
2827
2828
    /**
2829
     * Move an array element to a new index.
2830
     *
2831
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2832
     *
2833
     * @param int|string $from
2834
     * @param int        $to
2835
     *
2836
     * @return static
2837
     *                <p>(Immutable)</p>
2838
     */
2839 1
    public function moveElement($from, $to): self
2840
    {
2841 1
        $array = $this->getArray();
2842
2843 1
        if (\is_int($from)) {
2844 1
            $tmp = \array_splice($array, $from, 1);
2845 1
            \array_splice($array, (int) $to, 0, $tmp);
2846 1
            $output = $array;
2847 1
        } elseif (\is_string($from)) {
2848 1
            $indexToMove = \array_search($from, \array_keys($array), true);
2849 1
            $itemToMove = $array[$from];
2850 1
            if ($indexToMove !== false) {
2851 1
                \array_splice($array, $indexToMove, 1);
2852
            }
2853 1
            $i = 0;
2854 1
            $output = [];
2855 1
            foreach ($array as $key => $item) {
2856 1
                if ($i === $to) {
2857 1
                    $output[$from] = $itemToMove;
2858
                }
2859 1
                $output[$key] = $item;
2860 1
                ++$i;
2861
            }
2862
        } else {
2863
            $output = [];
2864
        }
2865
2866 1
        return static::create(
2867 1
            $output,
2868 1
            $this->iteratorClass,
2869 1
            false
2870
        );
2871
    }
2872
2873
    /**
2874
     * Get a subset of the items from the given array.
2875
     *
2876
     * @param mixed[] $keys
2877
     *
2878
     * @return static
2879
     *                <p>(Immutable)</p>
2880
     */
2881
    public function only(array $keys): self
2882
    {
2883
        $array = $this->getArray();
2884
2885
        return static::create(
2886
            \array_intersect_key($array, \array_flip($keys)),
2887
            $this->iteratorClass,
2888
            false
2889
        );
2890
    }
2891
2892
    /**
2893
     * Pad array to the specified size with a given value.
2894
     *
2895
     * @param int   $size  <p>Size of the result array.</p>
2896
     * @param mixed $value <p>Empty value by default.</p>
2897
     *
2898
     * @return static
2899
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
2900
     */
2901 4
    public function pad(int $size, $value): self
2902
    {
2903 4
        return static::create(
2904 4
            \array_pad($this->getArray(), $size, $value),
2905 4
            $this->iteratorClass,
2906 4
            false
2907
        );
2908
    }
2909
2910
    /**
2911
     * Pop a specified value off the end of the current array.
2912
     *
2913
     * @return mixed
2914
     *               <p>(Mutable) The popped element from the current array.</p>
2915
     */
2916 4
    public function pop()
2917
    {
2918 4
        $this->generatorToArray();
2919
2920 4
        return \array_pop($this->array);
2921
    }
2922
2923
    /**
2924
     * Prepend a (key) + value to the current array.
2925
     *
2926
     * @param mixed $value
2927
     * @param mixed $key
2928
     *
2929
     * @return static
2930
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
2931
     */
2932 9
    public function prepend($value, $key = null): self
2933
    {
2934 9
        $this->generatorToArray();
2935
2936 9
        if ($key === null) {
2937 8
            \array_unshift($this->array, $value);
2938
        } else {
2939
            /** @noinspection AdditionOperationOnArraysInspection */
2940 2
            $this->array = [$key => $value] + $this->array;
2941
        }
2942
2943 9
        return $this;
2944
    }
2945
2946
    /**
2947
     * Add a suffix to each key.
2948
     *
2949
     * @param mixed $suffix
2950
     *
2951
     * @return static
2952
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
2953
     */
2954 10 View Code Duplication
    public function prependToEachKey($suffix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2955
    {
2956
        // init
2957 10
        $result = [];
2958
2959 10
        foreach ($this->getGenerator() as $key => $item) {
2960 9
            if ($item instanceof self) {
2961
                $result[$key] = $item->prependToEachKey($suffix);
2962 9
            } elseif (\is_array($item)) {
2963
                $result[$key] = self::create(
2964
                    $item,
2965
                    $this->iteratorClass,
2966
                    false
2967
                )->prependToEachKey($suffix)
2968
                    ->toArray();
2969
            } else {
2970 9
                $result[$key . $suffix] = $item;
2971
            }
2972
        }
2973
2974 10
        return self::create(
2975 10
            $result,
2976 10
            $this->iteratorClass,
2977 10
            false
2978
        );
2979
    }
2980
2981
    /**
2982
     * Add a suffix to each value.
2983
     *
2984
     * @param mixed $suffix
2985
     *
2986
     * @return static
2987
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
2988
     */
2989 10 View Code Duplication
    public function prependToEachValue($suffix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2990
    {
2991
        // init
2992 10
        $result = [];
2993
2994 10
        foreach ($this->getGenerator() as $key => $item) {
2995 9
            if ($item instanceof self) {
2996
                $result[$key] = $item->prependToEachValue($suffix);
2997 9
            } elseif (\is_array($item)) {
2998
                $result[$key] = self::create(
2999
                    $item,
3000
                    $this->iteratorClass,
3001
                    false
3002
                )->prependToEachValue($suffix)
3003
                    ->toArray();
3004 9
            } elseif (\is_object($item)) {
3005 1
                $result[$key] = $item;
3006
            } else {
3007 9
                $result[$key] = $item . $suffix;
3008
            }
3009
        }
3010
3011 10
        return self::create(
3012 10
            $result,
3013 10
            $this->iteratorClass,
3014 10
            false
3015
        );
3016
    }
3017
3018
    /**
3019
     * Return the value of a given key and
3020
     * delete the key.
3021
     *
3022
     * @param int|int[]|string|string[]|null $keyOrKeys
3023
     * @param mixed                          $fallback
3024
     *
3025
     * @return mixed
3026
     */
3027 1
    public function pull($keyOrKeys = null, $fallback = null)
3028
    {
3029 1
        if ($keyOrKeys === null) {
3030
            $array = $this->getArray();
3031
            $this->clear();
3032
3033
            return $array;
3034
        }
3035
3036 1
        if (\is_array($keyOrKeys)) {
3037 1
            $valueOrValues = [];
3038 1
            foreach ($keyOrKeys as $key) {
3039 1
                $valueOrValues[] = $this->get($key, $fallback);
3040 1
                $this->offsetUnset($key);
3041
            }
3042
        } else {
3043 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3044 1
            $this->offsetUnset($keyOrKeys);
3045
        }
3046
3047 1
        return $valueOrValues;
3048
    }
3049
3050
    /**
3051
     * Push one or more values onto the end of array at once.
3052
     *
3053
     * @return static
3054
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3055
     */
3056 4 View Code Duplication
    public function push(/* variadic arguments allowed */): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3057
    {
3058 4
        $this->generatorToArray();
3059
3060 4
        if (\func_num_args()) {
3061 4
            $args = \func_get_args();
3062 4
            \array_push(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_push() as the parameter $array expects a reference.
Loading history...
3063
        }
3064
3065 4
        return $this;
3066
    }
3067
3068
    /**
3069
     * Get a random value from the current array.
3070
     *
3071
     * @param int|null $number <p>How many values you will take?</p>
3072
     *
3073
     * @return static
3074
     *                <p>(Immutable)</p>
3075
     */
3076 18
    public function randomImmutable(int $number = null): self
3077
    {
3078 18
        $this->generatorToArray();
3079
3080 18 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3081 1
            return static::create(
3082 1
                [],
3083 1
                $this->iteratorClass,
3084 1
                false
3085
            );
3086
        }
3087
3088 17 View Code Duplication
        if ($number === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3089
            /** @noinspection NonSecureArrayRandUsageInspection */
3090 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3091
3092 13
            return static::create(
3093 13
                $arrayRandValue,
3094 13
                $this->iteratorClass,
3095 13
                false
3096
            );
3097
        }
3098
3099 5
        $arrayTmp = $this->array;
3100
        /** @noinspection NonSecureShuffleUsageInspection */
3101 5
        \shuffle($arrayTmp);
3102
3103 5
        return static::create(
3104 5
            $arrayTmp,
3105 5
            $this->iteratorClass,
3106 5
            false
3107 5
        )->firstsImmutable($number);
3108
    }
3109
3110
    /**
3111
     * Pick a random key/index from the keys of this array.
3112
     *
3113
     * @throws \RangeException If array is empty
3114
     *
3115
     * @return mixed
3116
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3117
     */
3118 4
    public function randomKey()
3119
    {
3120 4
        $result = $this->randomKeys(1);
3121
3122 4
        if (!isset($result[0])) {
3123
            $result[0] = null;
3124
        }
3125
3126 4
        return $result[0];
3127
    }
3128
3129
    /**
3130
     * Pick a given number of random keys/indexes out of this array.
3131
     *
3132
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3133
     *
3134
     * @throws \RangeException If array is empty
3135
     *
3136
     * @return static
3137
     *                <p>(Immutable)</p>
3138
     */
3139 13
    public function randomKeys(int $number): self
3140
    {
3141 13
        $this->generatorToArray();
3142
3143 13
        $count = \count($this->array, \COUNT_NORMAL);
3144
3145 13
        if ($number === 0 || $number > $count) {
3146 2
            throw new \RangeException(
3147 2
                \sprintf(
3148 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3149 2
                    $number,
3150 2
                    $count
3151
                )
3152
            );
3153
        }
3154
3155 11
        $result = (array) \array_rand($this->array, $number);
3156
3157 11
        return static::create(
3158 11
            $result,
3159 11
            $this->iteratorClass,
3160 11
            false
3161
        );
3162
    }
3163
3164
    /**
3165
     * Get a random value from the current array.
3166
     *
3167
     * @param int|null $number <p>How many values you will take?</p>
3168
     *
3169
     * @return static
3170
     *                <p>(Mutable)</p>
3171
     */
3172 17
    public function randomMutable(int $number = null): self
3173
    {
3174 17
        $this->generatorToArray();
3175
3176 17 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3177
            return static::create(
3178
                [],
3179
                $this->iteratorClass,
3180
                false
3181
            );
3182
        }
3183
3184 17 View Code Duplication
        if ($number === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3185
            /** @noinspection NonSecureArrayRandUsageInspection */
3186 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3187 7
            $this->array = $arrayRandValue;
3188
3189 7
            return $this;
3190
        }
3191
3192
        /** @noinspection NonSecureShuffleUsageInspection */
3193 11
        \shuffle($this->array);
3194
3195 11
        return $this->firstsMutable($number);
3196
    }
3197
3198
    /**
3199
     * Pick a random value from the values of this array.
3200
     *
3201
     * @return mixed
3202
     *               <p>Get a random value or null if there wasn't a value.</p>
3203
     */
3204 4
    public function randomValue()
3205
    {
3206 4
        $result = $this->randomImmutable();
3207
3208 4
        if (!isset($result[0])) {
3209
            $result[0] = null;
3210
        }
3211
3212 4
        return $result[0];
3213
    }
3214
3215
    /**
3216
     * Pick a given number of random values out of this array.
3217
     *
3218
     * @param int $number
3219
     *
3220
     * @return static
3221
     *                <p>(Mutable)</p>
3222
     */
3223 7
    public function randomValues(int $number): self
3224
    {
3225 7
        return $this->randomMutable($number);
3226
    }
3227
3228
    /**
3229
     * Get a random value from an array, with the ability to skew the results.
3230
     *
3231
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3232
     *
3233
     * @param array    $array
3234
     * @param int|null $number <p>How many values you will take?</p>
3235
     *
3236
     * @return static
3237
     *                <p>(Immutable)</p>
3238
     */
3239 9
    public function randomWeighted(array $array, int $number = null): self
3240
    {
3241
        // init
3242 9
        $options = [];
3243
3244 9
        foreach ($array as $option => $weight) {
3245 9
            if ($this->searchIndex($option) !== false) {
3246 9
                for ($i = 0; $i < $weight; ++$i) {
3247 1
                    $options[] = $option;
3248
                }
3249
            }
3250
        }
3251
3252 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3253
    }
3254
3255
    /**
3256
     * Reduce the current array via callable e.g. anonymous-function.
3257
     *
3258
     * @param callable $callable
3259
     * @param array    $init
3260
     *
3261
     * @return static
3262
     *                <p>(Immutable)</p>
3263
     */
3264 16
    public function reduce($callable, array $init = []): self
3265
    {
3266 16
        if ($this->generator) {
3267 1
            $result = $init;
3268
3269 1
            foreach ($this->getGenerator() as $value) {
3270 1
                $result = $callable($result, $value);
3271
            }
3272
3273 1
            return static::create(
3274 1
                $result,
3275 1
                $this->iteratorClass,
3276 1
                false
3277
            );
3278
        }
3279
3280 16
        $result = \array_reduce($this->array, $callable, $init);
3281
3282 16
        if ($result === null) {
3283
            $this->array = [];
3284
        } else {
3285 16
            $this->array = (array) $result;
3286
        }
3287
3288 16
        return static::create(
3289 16
            $this->array,
3290 16
            $this->iteratorClass,
3291 16
            false
3292
        );
3293
    }
3294
3295
    /**
3296
     * @param bool $unique
3297
     *
3298
     * @return static
3299
     *                <p>(Immutable)</p>
3300
     */
3301 14
    public function reduce_dimension(bool $unique = true): self
3302
    {
3303
        // init
3304 14
        $result = [[]];
3305
3306 14
        foreach ($this->getGenerator() as $val) {
3307 12
            if (\is_array($val)) {
3308 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
3309
            } else {
3310 12
                $result[] = [$val];
3311
            }
3312
        }
3313 14
        $result = \array_merge(...$result);
3314
3315 14
        $resultArrayy = new self($result);
3316
3317 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3318
    }
3319
3320
    /**
3321
     * Create a numerically re-indexed Arrayy object.
3322
     *
3323
     * @return static
3324
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3325
     */
3326 9
    public function reindex(): self
3327
    {
3328 9
        $this->generatorToArray();
3329
3330 9
        $this->array = \array_values($this->array);
3331
3332 9
        return $this;
3333
    }
3334
3335
    /**
3336
     * Return all items that fail the truth test.
3337
     *
3338
     * @param \Closure $closure
3339
     *
3340
     * @return static
3341
     *                <p>(Immutable)</p>
3342
     */
3343 1 View Code Duplication
    public function reject(\Closure $closure): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3344
    {
3345
        // init
3346 1
        $filtered = [];
3347
3348 1
        foreach ($this->getGenerator() as $key => $value) {
3349 1
            if (!$closure($value, $key)) {
3350 1
                $filtered[$key] = $value;
3351
            }
3352
        }
3353
3354 1
        return static::create(
3355 1
            $filtered,
3356 1
            $this->iteratorClass,
3357 1
            false
3358
        );
3359
    }
3360
3361
    /**
3362
     * Remove a value from the current array (optional using dot-notation).
3363
     *
3364
     * @param mixed $key
3365
     *
3366
     * @return static
3367
     *                <p>(Mutable)</p>
3368
     */
3369 18
    public function remove($key): self
3370
    {
3371
        // recursive call
3372 18
        if (\is_array($key)) {
3373
            foreach ($key as $k) {
3374
                $this->internalRemove($k);
3375
            }
3376
3377
            return static::create(
3378
                $this->getArray(),
3379
                $this->iteratorClass,
3380
                false
3381
            );
3382
        }
3383
3384 18
        $this->internalRemove($key);
3385
3386 18
        return static::create(
3387 18
            $this->getArray(),
3388 18
            $this->iteratorClass,
3389 18
            false
3390
        );
3391
    }
3392
3393
    /**
3394
     * Remove the first value from the current array.
3395
     *
3396
     * @return static
3397
     *                <p>(Immutable)</p>
3398
     */
3399 7 View Code Duplication
    public function removeFirst(): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3400
    {
3401 7
        $tmpArray = $this->getArray();
3402
3403 7
        \array_shift($tmpArray);
3404
3405 7
        return static::create(
3406 7
            $tmpArray,
3407 7
            $this->iteratorClass,
3408 7
            false
3409
        );
3410
    }
3411
3412
    /**
3413
     * Remove the last value from the current array.
3414
     *
3415
     * @return static
3416
     *                <p>(Immutable)</p>
3417
     */
3418 7 View Code Duplication
    public function removeLast(): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3419
    {
3420 7
        $tmpArray = $this->getArray();
3421
3422 7
        \array_pop($tmpArray);
3423
3424 7
        return static::create(
3425 7
            $tmpArray,
3426 7
            $this->iteratorClass,
3427 7
            false
3428
        );
3429
    }
3430
3431
    /**
3432
     * Removes a particular value from an array (numeric or associative).
3433
     *
3434
     * @param mixed $value
3435
     *
3436
     * @return static
3437
     *                <p>(Immutable)</p>
3438
     */
3439 7
    public function removeValue($value): self
3440
    {
3441 7
        $this->generatorToArray();
3442
3443
        // init
3444 7
        $isNumericArray = true;
3445
3446 7
        foreach ($this->getGenerator() as $key => $item) {
3447 6
            if ($item === $value) {
3448 6
                if (!\is_int($key)) {
3449
                    $isNumericArray = false;
3450
                }
3451 6
                unset($this->array[$key]);
3452
            }
3453
        }
3454
3455 7
        if ($isNumericArray) {
3456 7
            $this->array = \array_values($this->array);
3457
        }
3458
3459 7
        return static::create(
3460 7
            $this->array,
3461 7
            $this->iteratorClass,
3462 7
            false
3463
        );
3464
    }
3465
3466
    /**
3467
     * Generate array of repeated arrays.
3468
     *
3469
     * @param int $times <p>How many times has to be repeated.</p>
3470
     *
3471
     * @return static
3472
     *                <p>(Immutable)</p>
3473
     */
3474 1
    public function repeat($times): self
3475
    {
3476 1
        if ($times === 0) {
3477 1
            return new static();
3478
        }
3479
3480 1
        return static::create(
3481 1
            \array_fill(0, (int) $times, $this->getArray()),
3482 1
            $this->iteratorClass,
3483 1
            false
3484
        );
3485
    }
3486
3487
    /**
3488
     * Replace a key with a new key/value pair.
3489
     *
3490
     * @param mixed $replace
3491
     * @param mixed $key
3492
     * @param mixed $value
3493
     *
3494
     * @return static
3495
     *                <p>(Immutable)</p>
3496
     */
3497 2
    public function replace($replace, $key, $value): self
3498
    {
3499 2
        $that = clone $this;
3500
3501 2
        return $that->remove($replace)
3502 2
            ->set($key, $value);
3503
    }
3504
3505
    /**
3506
     * Create an array using the current array as values and the other array as keys.
3507
     *
3508
     * @param array $keys <p>An array of keys.</p>
3509
     *
3510
     * @return static
3511
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3512
     */
3513 2
    public function replaceAllKeys(array $keys): self
3514
    {
3515 2
        return static::create(
3516 2
            \array_combine($keys, $this->getArray()),
3517 2
            $this->iteratorClass,
3518 2
            false
3519
        );
3520
    }
3521
3522
    /**
3523
     * Create an array using the current array as keys and the other array as values.
3524
     *
3525
     * @param array $array <p>An array o values.</p>
3526
     *
3527
     * @return static
3528
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3529
     */
3530 2
    public function replaceAllValues(array $array): self
3531
    {
3532 2
        return static::create(
3533 2
            \array_combine($this->array, $array),
3534 2
            $this->iteratorClass,
3535 2
            false
3536
        );
3537
    }
3538
3539
    /**
3540
     * Replace the keys in an array with another set.
3541
     *
3542
     * @param array $keys <p>An array of keys matching the array's size</p>
3543
     *
3544
     * @return static
3545
     *                <p>(Immutable)</p>
3546
     */
3547 1
    public function replaceKeys(array $keys): self
3548
    {
3549 1
        $values = \array_values($this->getArray());
3550 1
        $result = \array_combine($keys, $values);
3551
3552 1
        return static::create(
3553 1
            $result,
3554 1
            $this->iteratorClass,
3555 1
            false
3556
        );
3557
    }
3558
3559
    /**
3560
     * Replace the first matched value in an array.
3561
     *
3562
     * @param mixed $search      <p>The value to replace.</p>
3563
     * @param mixed $replacement <p>The value to replace.</p>
3564
     *
3565
     * @return static
3566
     *                <p>(Immutable)</p>
3567
     */
3568 3
    public function replaceOneValue($search, $replacement = ''): self
3569
    {
3570 3
        $array = $this->getArray();
3571 3
        $key = \array_search($search, $array, true);
3572
3573 3
        if ($key !== false) {
3574 3
            $array[$key] = $replacement;
3575
        }
3576
3577 3
        return static::create(
3578 3
            $array,
3579 3
            $this->iteratorClass,
3580 3
            false
3581
        );
3582
    }
3583
3584
    /**
3585
     * Replace values in the current array.
3586
     *
3587
     * @param mixed $search      <p>The value to replace.</p>
3588
     * @param mixed $replacement <p>What to replace it with.</p>
3589
     *
3590
     * @return static
3591
     *                <p>(Immutable)</p>
3592
     */
3593 1
    public function replaceValues($search, $replacement = ''): self
3594
    {
3595 1
        $array = $this->each(
3596 1
            static function ($value) use ($search, $replacement) {
3597 1
                return \str_replace($search, $replacement, $value);
3598 1
            }
3599
        );
3600
3601 1
        return $array;
3602
    }
3603
3604
    /**
3605
     * Get the last elements from index $from until the end of this array.
3606
     *
3607
     * @param int $from
3608
     *
3609
     * @return static
3610
     *                <p>(Immutable)</p>
3611
     */
3612 15 View Code Duplication
    public function rest(int $from = 1): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3613
    {
3614 15
        $tmpArray = $this->getArray();
3615
3616 15
        return static::create(
3617 15
            \array_splice($tmpArray, $from),
3618 15
            $this->iteratorClass,
3619 15
            false
3620
        );
3621
    }
3622
3623
    /**
3624
     * Return the array in the reverse order.
3625
     *
3626
     * @return static
3627
     *                <p>(Mutable) Return this Arrayy object.</p>
3628
     */
3629 8
    public function reverse(): self
3630
    {
3631 8
        $this->generatorToArray();
3632
3633 8
        $this->array = \array_reverse($this->array);
3634
3635 8
        return $this;
3636
    }
3637
3638
    /**
3639
     * Sort an array in reverse order.
3640
     *
3641
     * @param int $sort_flags [optional] <p>
3642
     *                        You may modify the behavior of the sort using the optional
3643
     *                        parameter sort_flags, for details
3644
     *                        see sort.
3645
     *                        </p>
3646
     *
3647
     * @return static
3648
     *                <p>(Mutable) Return this Arrayy object.</p>
3649
     */
3650 4
    public function rsort(int $sort_flags = 0): self
3651
    {
3652 4
        $this->generatorToArray();
3653
3654 4
        \rsort($this->array, $sort_flags);
3655
3656 4
        return $this;
3657
    }
3658
3659
    /**
3660
     * Search for the first index of the current array via $value.
3661
     *
3662
     * @param mixed $value
3663
     *
3664
     * @return false|float|int|string
3665
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3666
     */
3667 20
    public function searchIndex($value)
3668
    {
3669 20
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3670 19
            if ($value === $valueFromArray) {
3671 19
                return $keyFromArray;
3672
            }
3673
        }
3674
3675 11
        return false;
3676
    }
3677
3678
    /**
3679
     * Search for the value of the current array via $index.
3680
     *
3681
     * @param mixed $index
3682
     *
3683
     * @return static
3684
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3685
     */
3686 9
    public function searchValue($index): self
3687
    {
3688 9
        $this->generatorToArray();
3689
3690
        // init
3691 9
        $return = [];
3692
3693 9
        if ($this->array === []) {
3694
            return static::create(
3695
                [],
3696
                $this->iteratorClass,
3697
                false
3698
            );
3699
        }
3700
3701
        // php cast "bool"-index into "int"-index
3702 9
        if ((bool) $index === $index) {
3703 1
            $index = (int) $index;
3704
        }
3705
3706 9
        if (\array_key_exists($index, $this->array) === true) {
3707 7
            $return = [$this->array[$index]];
3708
        }
3709
3710 9
        return static::create(
3711 9
            $return,
3712 9
            $this->iteratorClass,
3713 9
            false
3714
        );
3715
    }
3716
3717
    /**
3718
     * Set a value for the current array (optional using dot-notation).
3719
     *
3720
     * @param string $key   <p>The key to set.</p>
3721
     * @param mixed  $value <p>Its value.</p>
3722
     *
3723
     * @return static
3724
     *                <p>(Mutable)</p>
3725
     */
3726 18
    public function set($key, $value): self
3727
    {
3728 18
        $this->generatorToArray();
3729
3730 18
        $this->internalSet($key, $value);
3731
3732 18
        return $this;
3733
    }
3734
3735
    /**
3736
     * Get a value from a array and set it if it was not.
3737
     *
3738
     * WARNING: this method only set the value, if the $key is not already set
3739
     *
3740
     * @param mixed $key      <p>The key</p>
3741
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3742
     *
3743
     * @return mixed
3744
     *               <p>(Mutable)</p>
3745
     */
3746 11
    public function setAndGet($key, $fallback = null)
3747
    {
3748 11
        $this->generatorToArray();
3749
3750
        // If the key doesn't exist, set it.
3751 11
        if (!$this->has($key)) {
3752 4
            $this->array = $this->set($key, $fallback)->getArray();
3753
        }
3754
3755 11
        return $this->get($key);
3756
    }
3757
3758
    /**
3759
     * Shifts a specified value off the beginning of array.
3760
     *
3761
     * @return mixed
3762
     *               <p>(Mutable) A shifted element from the current array.</p>
3763
     */
3764 4
    public function shift()
3765
    {
3766 4
        $this->generatorToArray();
3767
3768 4
        return \array_shift($this->array);
3769
    }
3770
3771
    /**
3772
     * Shuffle the current array.
3773
     *
3774
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3775
     * @param array $array  [optional]
3776
     *
3777
     * @return static
3778
     *                <p>(Immutable)</p>
3779
     */
3780 1
    public function shuffle(bool $secure = false, array $array = null): self
3781
    {
3782 1
        if ($array === null) {
3783 1
            $array = $this->getArray();
3784
        }
3785
3786 1
        if ($secure !== true) {
3787
            /** @noinspection NonSecureShuffleUsageInspection */
3788 1
            \shuffle($array);
3789
        } else {
3790 1
            $size = \count($array, \COUNT_NORMAL);
3791 1
            $keys = \array_keys($array);
3792 1
            for ($i = $size - 1; $i > 0; --$i) {
3793
                try {
3794 1
                    $r = \random_int(0, $i);
3795
                } catch (\Exception $e) {
3796
                    /** @noinspection RandomApiMigrationInspection */
3797
                    $r = \mt_rand(0, $i);
3798
                }
3799 1
                if ($r !== $i) {
3800 1
                    $temp = $array[$keys[$r]];
3801 1
                    $array[$keys[$r]] = $array[$keys[$i]];
3802 1
                    $array[$keys[$i]] = $temp;
3803
                }
3804
            }
3805
3806
            // reset indices
3807 1
            $array = \array_values($array);
3808
        }
3809
3810 1
        foreach ($array as $key => $value) {
3811
            // check if recursive is needed
3812 1
            if (\is_array($value) === true) {
3813 1
                $array[$key] = $this->shuffle($secure, $value);
3814
            }
3815
        }
3816
3817 1
        return static::create(
3818 1
            $array,
3819 1
            $this->iteratorClass,
3820 1
            false
3821
        );
3822
    }
3823
3824
    /**
3825
     * Count the values from the current array.
3826
     *
3827
     * alias: for "Arrayy->count()"
3828
     *
3829
     * @param int $mode
3830
     *
3831
     * @return int
3832
     */
3833 20
    public function size(int $mode = \COUNT_NORMAL): int
3834
    {
3835 20
        return $this->count($mode);
3836
    }
3837
3838
    /**
3839
     * Checks whether array has exactly $size items.
3840
     *
3841
     * @param int $size
3842
     *
3843
     * @return bool
3844
     */
3845 1 View Code Duplication
    public function sizeIs(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3846
    {
3847
        // init
3848 1
        $itemsTempCount = 0;
3849
3850 1
        foreach ($this->getGenerator() as $key => $value) {
3851 1
            ++$itemsTempCount;
3852 1
            if ($itemsTempCount > $size) {
3853 1
                return false;
3854
            }
3855
        }
3856
3857 1
        return $itemsTempCount === $size;
3858
    }
3859
3860
    /**
3861
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
3862
     * smaller than $fromSize.
3863
     *
3864
     * @param int $fromSize
3865
     * @param int $toSize
3866
     *
3867
     * @return bool
3868
     */
3869 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
3870
    {
3871 1
        if ($fromSize > $toSize) {
3872 1
            $tmp = $toSize;
3873 1
            $toSize = $fromSize;
3874 1
            $fromSize = $tmp;
3875
        }
3876
3877
        // init
3878 1
        $itemsTempCount = 0;
3879
3880 1
        foreach ($this->getGenerator() as $key => $value) {
3881 1
            ++$itemsTempCount;
3882 1
            if ($itemsTempCount > $toSize) {
3883 1
                return false;
3884
            }
3885
        }
3886
3887 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
3888
    }
3889
3890
    /**
3891
     * Checks whether array has more than $size items.
3892
     *
3893
     * @param int $size
3894
     *
3895
     * @return bool
3896
     */
3897 1 View Code Duplication
    public function sizeIsGreaterThan(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3898
    {
3899
        // init
3900 1
        $itemsTempCount = 0;
3901
3902 1
        foreach ($this->getGenerator() as $key => $value) {
3903 1
            ++$itemsTempCount;
3904 1
            if ($itemsTempCount > $size) {
3905 1
                return true;
3906
            }
3907
        }
3908
3909 1
        return $itemsTempCount > $size;
3910
    }
3911
3912
    /**
3913
     * Checks whether array has less than $size items.
3914
     *
3915
     * @param int $size
3916
     *
3917
     * @return bool
3918
     */
3919 1 View Code Duplication
    public function sizeIsLessThan(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3920
    {
3921
        // init
3922 1
        $itemsTempCount = 0;
3923
3924 1
        foreach ($this->getGenerator() as $key => $value) {
3925 1
            ++$itemsTempCount;
3926 1
            if ($itemsTempCount > $size) {
3927 1
                return false;
3928
            }
3929
        }
3930
3931 1
        return $itemsTempCount < $size;
3932
    }
3933
3934
    /**
3935
     * Counts all elements in an array, or something in an object.
3936
     *
3937
     * <p>
3938
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
3939
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
3940
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
3941
     * implemented and used in PHP.
3942
     * </p>
3943
     *
3944
     * @return int
3945
     *             <p>
3946
     *             The number of elements in var, which is
3947
     *             typically an array, since anything else will have one
3948
     *             element.
3949
     *             </p>
3950
     *             <p>
3951
     *             If var is not an array or an object with
3952
     *             implemented Countable interface,
3953
     *             1 will be returned.
3954
     *             There is one exception, if var is &null;,
3955
     *             0 will be returned.
3956
     *             </p>
3957
     *             <p>
3958
     *             Caution: count may return 0 for a variable that isn't set,
3959
     *             but it may also return 0 for a variable that has been initialized with an
3960
     *             empty array. Use isset to test if a variable is set.
3961
     *             </p>
3962
     */
3963 10
    public function sizeRecursive(): int
3964
    {
3965 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
3966
    }
3967
3968
    /**
3969
     * Extract a slice of the array.
3970
     *
3971
     * @param int      $offset       <p>Slice begin index.</p>
3972
     * @param int|null $length       <p>Length of the slice.</p>
3973
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
3974
     *
3975
     * @return static
3976
     *                <p>A slice of the original array with length $length.</p>
3977
     */
3978 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
3979
    {
3980 4
        return static::create(
3981 4
            \array_slice(
3982 4
                $this->getArray(),
3983 4
                $offset,
3984 4
                $length,
3985 4
                $preserveKeys
3986
            ),
3987 4
            $this->iteratorClass,
3988 4
            false
3989
        );
3990
    }
3991
3992
    /**
3993
     * Sort the current array and optional you can keep the keys.
3994
     *
3995
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3996
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
3997
     *                              <strong>SORT_NATURAL</strong></p>
3998
     * @param bool       $keepKeys
3999
     *
4000
     * @return static
4001
     *                <p>(Mutable) Return this Arrayy object.</p>
4002
     */
4003 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4004
    {
4005 20
        $this->generatorToArray();
4006
4007 20
        return $this->sorting(
4008 20
            $this->array,
4009 20
            $direction,
4010 20
            $strategy,
4011 20
            $keepKeys
4012
        );
4013
    }
4014
4015
    /**
4016
     * Sort the current array by key.
4017
     *
4018
     * @see http://php.net/manual/en/function.ksort.php
4019
     * @see http://php.net/manual/en/function.krsort.php
4020
     *
4021
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4022
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4023
     *                              <strong>SORT_NATURAL</strong></p>
4024
     *
4025
     * @return static
4026
     *                <p>(Mutable) Return this Arrayy object.</p>
4027
     */
4028 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4029
    {
4030 18
        $this->generatorToArray();
4031
4032 18
        $this->sorterKeys($this->array, $direction, $strategy);
4033
4034 18
        return $this;
4035
    }
4036
4037
    /**
4038
     * Sort the current array by value.
4039
     *
4040
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4041
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4042
     *                              <strong>SORT_NATURAL</strong></p>
4043
     *
4044
     * @return static
4045
     *                <p>(Mutable)</p>
4046
     */
4047 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4048
    {
4049 1
        return $this->sort($direction, $strategy, true);
4050
    }
4051
4052
    /**
4053
     * Sort the current array by value.
4054
     *
4055
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4056
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4057
     *                              <strong>SORT_NATURAL</strong></p>
4058
     *
4059
     * @return static
4060
     *                <p>(Mutable)</p>
4061
     */
4062 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4063
    {
4064 1
        return $this->sort($direction, $strategy, false);
4065
    }
4066
4067
    /**
4068
     * Sort a array by value, by a closure or by a property.
4069
     *
4070
     * - If the sorter is null, the array is sorted naturally.
4071
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4072
     *
4073
     * @param callable|string|null $sorter
4074
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4075
     *                                        <strong>SORT_DESC</strong></p>
4076
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4077
     *                                        <strong>SORT_NATURAL</strong></p>
4078
     *
4079
     * @return static
4080
     *                <p>(Immutable)</p>
4081
     */
4082 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4083
    {
4084 1
        $array = $this->getArray();
4085 1
        $direction = $this->getDirection($direction);
4086
4087
        // Transform all values into their results.
4088 1
        if ($sorter) {
4089 1
            $arrayy = static::create(
4090 1
                $array,
4091 1
                $this->iteratorClass,
4092 1
                false
4093
            );
4094
4095 1
            $results = $arrayy->each(
4096 1
                function ($value) use ($sorter) {
4097 1
                    if (\is_callable($sorter)) {
4098 1
                        return $sorter($value);
4099
                    }
4100
4101 1
                    return $this->get($sorter, null, $this->getArray());
4102 1
                }
4103
            );
4104
4105 1
            $results = $results->getArray();
4106
        } else {
4107 1
            $results = $array;
4108
        }
4109
4110
        // Sort by the results and replace by original values
4111 1
        \array_multisort($results, $direction, $strategy, $array);
4112
4113 1
        return static::create(
4114 1
            $array,
4115 1
            $this->iteratorClass,
4116 1
            false
4117
        );
4118
    }
4119
4120
    /**
4121
     * Split an array in the given amount of pieces.
4122
     *
4123
     * @param int  $numberOfPieces
4124
     * @param bool $keepKeys
4125
     *
4126
     * @return static
4127
     *                <p>(Immutable)</p>
4128
     */
4129 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4130
    {
4131 1
        $this->generatorToArray();
4132
4133 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4134
4135 1
        if ($arrayCount === 0) {
4136 1
            $result = [];
4137
        } else {
4138 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4139 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4140
        }
4141
4142 1
        return static::create(
4143 1
            $result,
4144 1
            $this->iteratorClass,
4145 1
            false
4146
        );
4147
    }
4148
4149
    /**
4150
     * Stripe all empty items.
4151
     *
4152
     * @return static
4153
     *                <p>(Immutable)</p>
4154
     */
4155 1
    public function stripEmpty(): self
4156
    {
4157 1
        return $this->filter(
4158 1
            static function ($item) {
4159 1
                if ($item === null) {
4160 1
                    return false;
4161
                }
4162
4163 1
                return (bool) \trim((string) $item);
4164 1
            }
4165
        );
4166
    }
4167
4168
    /**
4169
     * Swap two values between positions by key.
4170
     *
4171
     * @param int|string $swapA <p>a key in the array</p>
4172
     * @param int|string $swapB <p>a key in the array</p>
4173
     *
4174
     * @return static
4175
     *                <p>(Immutable)</p>
4176
     */
4177 1
    public function swap($swapA, $swapB): self
4178
    {
4179 1
        $array = $this->getArray();
4180
4181 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4182
4183 1
        return static::create(
4184 1
            $array,
4185 1
            $this->iteratorClass,
4186 1
            false
4187
        );
4188
    }
4189
4190
    /**
4191
     * alias: for "Arrayy->getArray()"
4192
     *
4193
     * @see Arrayy::getArray()
4194
     */
4195 202
    public function toArray()
4196
    {
4197 202
        return $this->getArray();
4198
    }
4199
4200
    /**
4201
     * Convert the current array to JSON.
4202
     *
4203
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4204
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4205
     *
4206
     * @return string
4207
     */
4208 6
    public function toJson(int $options = 0, int $depth = 512): string
4209
    {
4210 6
        $return = \json_encode($this->getArray(), $options, $depth);
4211 6
        if ($return === false) {
4212
            return '';
4213
        }
4214
4215 6
        return $return;
4216
    }
4217
4218
    /**
4219
     * Implodes array to a string with specified separator.
4220
     *
4221
     * @param string $separator [optional] <p>The element's separator.</p>
4222
     *
4223
     * @return string
4224
     *                <p>The string representation of array, separated by ",".</p>
4225
     */
4226 20
    public function toString(string $separator = ','): string
4227
    {
4228 20
        return $this->implode($separator);
4229
    }
4230
4231
    /**
4232
     * Return a duplicate free copy of the current array.
4233
     *
4234
     * @return static
4235
     *                <p>(Mutable)</p>
4236
     */
4237 12
    public function unique(): self
4238
    {
4239
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4240
4241 12
        $this->array = $this->reduce(
4242 12
            static function ($resultArray, $value) {
4243 11
                if (!\in_array($value, $resultArray, true)) {
4244 11
                    $resultArray[] = $value;
4245
                }
4246
4247 11
                return $resultArray;
4248 12
            },
4249 12
            []
4250 12
        )->getArray();
4251 12
        $this->generator = null;
4252
4253 12
        return $this;
4254
    }
4255
4256
    /**
4257
     * Return a duplicate free copy of the current array. (with the old keys)
4258
     *
4259
     * @return static
4260
     *                <p>(Mutable)</p>
4261
     */
4262 11
    public function uniqueKeepIndex(): self
4263
    {
4264
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4265
4266
        // init
4267 11
        $array = $this->getArray();
4268
4269 11
        $this->array = \array_reduce(
0 ignored issues
show
Documentation Bug introduced by
It seems like \array_reduce(\array_key...esultArray; }, array()) of type * is incompatible with the declared type array of property $array.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
4270 11
            \array_keys($array),
4271 11
            static function ($resultArray, $key) use ($array) {
4272 10
                if (!\in_array($array[$key], $resultArray, true)) {
4273 10
                    $resultArray[$key] = $array[$key];
4274
                }
4275
4276 10
                return $resultArray;
4277 11
            },
4278 11
            []
4279
        );
4280 11
        $this->generator = null;
4281
4282 11
        if ($this->array === null) {
4283
            $this->array = [];
4284
        } else {
4285 11
            $this->array = (array) $this->array;
4286
        }
4287
4288 11
        return $this;
4289
    }
4290
4291
    /**
4292
     * alias: for "Arrayy->unique()"
4293
     *
4294
     * @return static
4295
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4296
     *
4297
     * @see Arrayy::unique()
4298
     */
4299 10
    public function uniqueNewIndex(): self
4300
    {
4301 10
        return $this->unique();
4302
    }
4303
4304
    /**
4305
     * Prepends one or more values to the beginning of array at once.
4306
     *
4307
     * @return static
4308
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4309
     */
4310 4 View Code Duplication
    public function unshift(/* variadic arguments allowed */): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4311
    {
4312 4
        $this->generatorToArray();
4313
4314 4
        if (\func_num_args()) {
4315 4
            $args = \func_get_args();
4316 4
            \array_unshift(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_unshift() as the parameter $array expects a reference.
Loading history...
4317
        }
4318
4319 4
        return $this;
4320
    }
4321
4322
    /**
4323
     * Get all values from a array.
4324
     *
4325
     * @return static
4326
     *                <p>(Immutable)</p>
4327
     */
4328 2
    public function values(): self
4329
    {
4330 2
        return static::create(
4331 2
            function () {
4332
                /** @noinspection YieldFromCanBeUsedInspection */
4333 2
                foreach ($this->getGenerator() as $value) {
4334 2
                    yield $value;
4335
                }
4336 2
            },
4337 2
            $this->iteratorClass,
4338 2
            false
4339
        );
4340
    }
4341
4342
    /**
4343
     * Apply the given function to every element in the array, discarding the results.
4344
     *
4345
     * @param callable $callable
4346
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4347
     *
4348
     * @return static
4349
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4350
     */
4351 11
    public function walk($callable, bool $recursive = false): self
4352
    {
4353 11
        $this->generatorToArray();
4354
4355 11
        if ($recursive === true) {
4356 6
            \array_walk_recursive($this->array, $callable);
4357
        } else {
4358 5
            \array_walk($this->array, $callable);
4359
        }
4360
4361 11
        return $this;
4362
    }
4363
4364
    /**
4365
     * Convert an array into a object.
4366
     *
4367
     * @param array $array PHP array
4368
     *
4369
     * @return \stdClass
4370
     */
4371 4
    protected static function arrayToObject(array $array = []): \stdClass
4372
    {
4373
        // init
4374 4
        $object = new \stdClass();
4375
4376 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4377 1
            return $object;
4378
        }
4379
4380 3
        foreach ($array as $name => $value) {
4381 3
            if (\is_array($value)) {
4382 1
                $object->{$name} = self::arrayToObject($value);
4383
            } else {
4384 3
                $object->{$name} = $value;
4385
            }
4386
        }
4387
4388 3
        return $object;
4389
    }
4390
4391
    /**
4392
     * @param array|\Generator|null $input        <p>
4393
     *                                            An array containing keys to return.
4394
     *                                            </p>
4395
     * @param mixed                 $search_value [optional] <p>
4396
     *                                            If specified, then only keys containing these values are returned.
4397
     *                                            </p>
4398
     * @param bool                  $strict       [optional] <p>
4399
     *                                            Determines if strict comparison (===) should be used during the
4400
     *                                            search.
4401
     *                                            </p>
4402
     *
4403
     * @return array
4404
     *               <p>an array of all the keys in input</p>
4405
     */
4406 10
    protected function array_keys_recursive(
4407
        $input = null,
4408
        $search_value = null,
4409
        bool $strict = true
4410
    ): array {
4411
        // init
4412 10
        $keys = [];
4413 10
        $keysTmp = [[]]; // the inner empty array covers cases when no loops were made
4414
4415 10
        if ($input === null) {
4416
            $input = $this->getGenerator();
4417
        }
4418
4419 10
        foreach ($input as $key => $value) {
4420
            if (
4421 10
                $search_value === null
4422
                ||
4423
                (
4424
                    \is_array($search_value) === true
4425
                    &&
4426 10
                    \in_array($key, $search_value, $strict)
4427
                )
4428
            ) {
4429 10
                $keys[] = $key;
4430
            }
4431
4432
            // check if recursive is needed
4433 10
            if (\is_array($value) === true) {
4434 10
                $keysTmp[] = $this->array_keys_recursive($value);
4435
            }
4436
        }
4437
4438 10
        return \array_merge($keys, ...$keysTmp);
4439
    }
4440
4441
    /**
4442
     * @param mixed      $path
4443
     * @param callable   $callable
4444
     * @param array|null $currentOffset
4445
     */
4446 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4447
    {
4448 4
        $this->generatorToArray();
4449
4450 4
        if ($currentOffset === null) {
4451 4
            $currentOffset = &$this->array;
4452
        }
4453
4454 4
        $explodedPath = \explode($this->pathSeparator, $path);
4455 4
        if ($explodedPath === false) {
4456
            return;
4457
        }
4458
4459 4
        $nextPath = \array_shift($explodedPath);
4460
4461 4
        if (!isset($currentOffset[$nextPath])) {
4462
            return;
4463
        }
4464
4465 4
        if (!empty($explodedPath)) {
4466 1
            $this->callAtPath(
4467 1
                \implode($this->pathSeparator, $explodedPath),
4468 1
                $callable,
4469 1
                $currentOffset[$nextPath]
4470
            );
4471
        } else {
4472 4
            $callable($currentOffset[$nextPath]);
4473
        }
4474 4
    }
4475
4476
    /**
4477
     * create a fallback for array
4478
     *
4479
     * 1. use the current array, if it's a array
4480
     * 2. fallback to empty array, if there is nothing
4481
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4482
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4483
     * 5. call "__toArray()" on object, if the method exists
4484
     * 6. cast a string or object with "__toString()" into an array
4485
     * 7. throw a "InvalidArgumentException"-Exception
4486
     *
4487
     * @param mixed $data
4488
     *
4489
     * @throws \InvalidArgumentException
4490
     *
4491
     * @return array
4492
     */
4493 947
    protected function fallbackForArray(&$data): array
4494
    {
4495 947
        $data = $this->internalGetArray($data);
4496
4497 947
        if ($data === null) {
4498 2
            throw new \InvalidArgumentException(
4499 2
                'Passed value should be a array'
4500
            );
4501
        }
4502
4503 945
        return $data;
4504
    }
4505
4506
    /**
4507
     * Get correct PHP constant for direction.
4508
     *
4509
     * @param int|string $direction
4510
     *
4511
     * @return int
4512
     */
4513 39
    protected function getDirection($direction): int
4514
    {
4515 39
        if (\is_string($direction)) {
4516 10
            $direction = \strtolower($direction);
4517
4518 10
            if ($direction === 'desc') {
4519 2
                $direction = \SORT_DESC;
4520
            } else {
4521 8
                $direction = \SORT_ASC;
4522
            }
4523
        }
4524
4525
        if (
4526 39
            $direction !== \SORT_DESC
4527
            &&
4528 39
            $direction !== \SORT_ASC
4529
        ) {
4530
            $direction = \SORT_ASC;
4531
        }
4532
4533 39
        return $direction;
4534
    }
4535
4536
    /**
4537
     * @param mixed $glue
4538
     * @param mixed $pieces
4539
     * @param bool  $useKeys
4540
     *
4541
     * @return string
4542
     */
4543 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4544
    {
4545 36
        if ($pieces instanceof self) {
4546 1
            $pieces = $pieces->getArray();
4547
        }
4548
4549 36
        if (\is_array($pieces)) {
4550 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4551 36
            $pieces_count_not_zero = $pieces_count > 0;
4552
4553 36
            return \implode(
4554 36
                $glue,
4555 36
                \array_map(
4556 36
                    [$this, 'implode_recursive'],
4557 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4558 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4559
                )
4560
            );
4561
        }
4562
4563
        if (
4564 36
            \is_scalar($pieces)
4565
            ||
4566 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
4567
        ) {
4568 31
            return (string) $pieces;
4569
        }
4570
4571 9
        return '';
4572
    }
4573
4574
    /**
4575
     * @param mixed                 $needle   <p>
4576
     *                                        The searched value.
4577
     *                                        </p>
4578
     *                                        <p>
4579
     *                                        If needle is a string, the comparison is done
4580
     *                                        in a case-sensitive manner.
4581
     *                                        </p>
4582
     * @param array|\Generator|null $haystack <p>
4583
     *                                        The array.
4584
     *                                        </p>
4585
     * @param bool                  $strict   [optional] <p>
4586
     *                                        If the third parameter strict is set to true
4587
     *                                        then the in_array function will also check the
4588
     *                                        types of the
4589
     *                                        needle in the haystack.
4590
     *                                        </p>
4591
     *
4592
     * @return bool
4593
     *              <p>true if needle is found in the array, false otherwise</p>
4594
     */
4595 19
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4596
    {
4597 19
        if ($haystack === null) {
4598
            $haystack = $this->getGenerator();
4599
        }
4600
4601 19
        foreach ($haystack as $item) {
4602 15
            if (\is_array($item) === true) {
4603 4
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4604
            } else {
4605
                /** @noinspection NestedPositiveIfStatementsInspection */
4606 15
                if ($strict === true) {
4607 15
                    $returnTmp = $item === $needle;
4608
                } else {
4609
                    $returnTmp = $item == $needle;
4610
                }
4611
            }
4612
4613 15
            if ($returnTmp === true) {
4614 15
                return true;
4615
            }
4616
        }
4617
4618 8
        return false;
4619
    }
4620
4621
    /**
4622
     * @param mixed $data
4623
     *
4624
     * @return array|null
4625
     */
4626 947
    protected function internalGetArray(&$data)
4627
    {
4628 947
        if (\is_array($data)) {
4629 944
            return $data;
4630
        }
4631
4632 46
        if (!$data) {
4633 6
            return [];
4634
        }
4635
4636 45
        $isObject = \is_object($data);
4637
4638 45
        if ($isObject && $data instanceof self) {
4639 2
            return $data->getArray();
4640
        }
4641
4642 44
        if ($isObject && $data instanceof \ArrayObject) {
4643
            return $data->getArrayCopy();
4644
        }
4645
4646 44
        if ($isObject && $data instanceof \Generator) {
4647
            return static::createFromGeneratorImmutable($data)->getArray();
4648
        }
4649
4650 44
        if ($isObject && $data instanceof \Traversable) {
4651
            return static::createFromObject($data)->getArray();
4652
        }
4653
4654 44
        if ($data instanceof \JsonSerializable) {
0 ignored issues
show
Bug introduced by
The class JsonSerializable does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
4655
            return (array) $data->jsonSerialize();
4656
        }
4657
4658 44
        if (\is_callable($data)) {
4659 37
            $this->generator = new ArrayyRewindableGenerator($data);
4660
4661 37
            return [];
4662
        }
4663
4664 9
        if ($isObject && \method_exists($data, '__toArray')) {
4665
            return (array) $data->__toArray();
4666
        }
4667
4668 9
        if (\is_scalar($data)) {
4669 7
            return [$data];
4670
        }
4671
4672 2
        if ($isObject && \method_exists($data, '__toString')) {
4673
            return [(string) $data];
4674
        }
4675
4676 2
        return null;
4677
    }
4678
4679
    /**
4680
     * Internal mechanics of remove method.
4681
     *
4682
     * @param mixed $key
4683
     *
4684
     * @return bool
4685
     */
4686 18
    protected function internalRemove($key): bool
4687
    {
4688 18
        $this->generatorToArray();
4689
4690
        if (
4691 18
            $this->pathSeparator
4692
            &&
4693 18
            \is_string($key)
4694
            &&
4695 18
            \strpos($key, $this->pathSeparator) !== false
4696
        ) {
4697
            $path = \explode($this->pathSeparator, (string) $key);
4698
4699
            if ($path !== false) {
4700
                // crawl though the keys
4701
                while (\count($path, \COUNT_NORMAL) > 1) {
4702
                    $key = \array_shift($path);
4703
4704
                    if (!$this->has($key)) {
4705
                        return false;
4706
                    }
4707
4708
                    $this->array = &$this->array[$key];
4709
                }
4710
4711
                $key = \array_shift($path);
4712
            }
4713
        }
4714
4715 18
        unset($this->array[$key]);
4716
4717 18
        return true;
4718
    }
4719
4720
    /**
4721
     * Internal mechanic of set method.
4722
     *
4723
     * @param int|string|null $key
4724
     * @param mixed           $value
4725
     * @param bool            $checkProperties
4726
     *
4727
     * @return bool
4728
     */
4729 829
    protected function internalSet($key, $value, $checkProperties = true): bool
4730
    {
4731
        if (
4732 829
            $checkProperties === true
4733
            &&
4734 829
            $this->properties !== []
4735
        ) {
4736 13
            if (isset($this->properties[$key]) === false) {
4737
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
4738
            }
4739
4740 13
            $this->properties[$key]->checkType($value);
4741
        }
4742
4743 828
        if ($key === null) {
4744
            return false;
4745
        }
4746
4747 828
        $this->generatorToArray();
4748
4749 828
        $array = &$this->array;
4750
4751
        if (
4752 828
            $this->pathSeparator
4753
            &&
4754 828
            \is_string($key)
4755
            &&
4756 828
            \strpos($key, $this->pathSeparator) !== false
4757
        ) {
4758 3
            $path = \explode($this->pathSeparator, (string) $key);
4759
4760 3
            if ($path !== false) {
4761
                // crawl through the keys
4762 3
                while (\count($path, \COUNT_NORMAL) > 1) {
4763 3
                    $key = \array_shift($path);
4764
4765 3
                    $array = &$array[$key];
4766
                }
4767
4768 3
                $key = \array_shift($path);
4769
            }
4770
        }
4771
4772 828
        $array[$key] = $value;
4773
4774 828
        return true;
4775
    }
4776
4777
    /**
4778
     * Convert a object into an array.
4779
     *
4780
     * @param object $object
4781
     *
4782
     * @return mixed
4783
     */
4784 5
    protected static function objectToArray($object)
4785
    {
4786 5
        if (!\is_object($object)) {
4787 4
            return $object;
4788
        }
4789
4790 5
        if (\is_object($object)) {
4791 5
            $object = \get_object_vars($object);
4792
        }
4793
4794 5
        return \array_map(['static', 'objectToArray'], $object);
4795
    }
4796
4797
    /**
4798
     * sorting keys
4799
     *
4800
     * @param array      $elements
4801
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4802
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4803
     *                              <strong>SORT_NATURAL</strong></p>
4804
     *
4805
     * @return static
4806
     *                <p>(Mutable) Return this Arrayy object.</p>
4807
     */
4808 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4809
    {
4810 18
        $direction = $this->getDirection($direction);
4811
4812
        switch ($direction) {
4813 18
            case 'desc':
4814 18
            case \SORT_DESC:
4815 6
                \krsort($elements, $strategy);
4816
4817 6
                break;
4818 13
            case 'asc':
4819 13
            case \SORT_ASC:
4820
            default:
4821 13
                \ksort($elements, $strategy);
4822
        }
4823
4824 18
        return $this;
4825
    }
4826
4827
    /**
4828
     * @param array      $elements  <p>Warning: used as reference</p>
4829
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4830
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4831
     *                              <strong>SORT_NATURAL</strong></p>
4832
     * @param bool       $keepKeys
4833
     *
4834
     * @return static
4835
     *                <p>(Mutable) Return this Arrayy object.</p>
4836
     */
4837 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4838
    {
4839 20
        $direction = $this->getDirection($direction);
4840
4841 20
        if (!$strategy) {
4842 20
            $strategy = \SORT_REGULAR;
4843
        }
4844
4845
        switch ($direction) {
4846 20
            case 'desc':
4847 20
            case \SORT_DESC:
4848 9
                if ($keepKeys) {
4849 5
                    \arsort($elements, $strategy);
4850
                } else {
4851 4
                    \rsort($elements, $strategy);
4852
                }
4853
4854 9
                break;
4855 11
            case 'asc':
4856 11
            case \SORT_ASC:
4857
            default:
4858 11
                if ($keepKeys) {
4859 4
                    \asort($elements, $strategy);
4860
                } else {
4861 7
                    \sort($elements, $strategy);
4862
                }
4863
        }
4864
4865 20
        return $this;
4866
    }
4867
4868
    /**
4869
     * @return bool
4870
     */
4871 865
    private function generatorToArray(): bool
4872
    {
4873 865
        if ($this->generator) {
4874 1
            $this->array = $this->getArray();
4875 1
            $this->generator = null;
4876
4877 1
            return true;
4878
        }
4879
4880 865
        return false;
4881
    }
4882
4883
    /**
4884
     * @return Property[]
4885
     */
4886 15
    private function getPropertiesFromPhpDoc(): array
4887
    {
4888 15
        static $PROPERTY_CACHE = [];
4889 15
        $cacheKey = 'Class::' . static::class;
4890
4891 15
        if (isset($PROPERTY_CACHE[$cacheKey])) {
4892 14
            return $PROPERTY_CACHE[$cacheKey];
4893
        }
4894
4895
        // init
4896 2
        $properties = [];
4897
4898 2
        $reflector = new \ReflectionClass($this);
4899 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
4900 2
        $docComment = $reflector->getDocComment();
4901 2
        if ($docComment) {
4902 2
            $docblock = $factory->create($docComment);
4903 2
            foreach ($docblock->getTagsByName('property') as $tag) {
4904
                /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Property */
4905 2
                $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
4906
            }
4907
        }
4908
4909 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
4910
    }
4911
}
4912